diff --git a/app/build.gradle b/app/build.gradle index 3c77533..eadc27e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -33,5 +33,6 @@ dependencies { compile 'com.android.support:support-v4:23.4.0' compile 'com.android.support:recyclerview-v7:23.4.0' compile 'com.android.support:gridlayout-v7:23.4.0' + compile 'com.android.support:cardview-v7:23.4.0' compile 'com.jakewharton:butterknife:7.0.1' } diff --git a/app/src/main/java/com/philliphsu/clock2/BaseFragment.java b/app/src/main/java/com/philliphsu/clock2/BaseFragment.java index 05d68bc..991007b 100644 --- a/app/src/main/java/com/philliphsu/clock2/BaseFragment.java +++ b/app/src/main/java/com/philliphsu/clock2/BaseFragment.java @@ -1,10 +1,42 @@ package com.philliphsu.clock2; +import android.os.Bundle; +import android.support.annotation.LayoutRes; +import android.support.annotation.Nullable; import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import butterknife.ButterKnife; /** * Created by Phillip Hsu on 6/30/2016. */ public abstract class BaseFragment extends Fragment { - public abstract void onFabClick(); + /** + * Required empty public constructor. Subclasses do not + * need to implement their own. + */ + public BaseFragment() {} + + /** + * @return the layout resource for this Fragment + */ + @LayoutRes + protected abstract int contentLayout(); + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + View view = inflater.inflate(contentLayout(), container, false); + ButterKnife.bind(this, view); + return view; + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + ButterKnife.unbind(this); // Only for fragments! + } } diff --git a/app/src/main/java/com/philliphsu/clock2/MainActivity.java b/app/src/main/java/com/philliphsu/clock2/MainActivity.java index 5df54c6..2207f0c 100644 --- a/app/src/main/java/com/philliphsu/clock2/MainActivity.java +++ b/app/src/main/java/com/philliphsu/clock2/MainActivity.java @@ -17,6 +17,7 @@ import android.widget.TextView; import com.philliphsu.clock2.alarms.AlarmsFragment; import com.philliphsu.clock2.editalarm.EditAlarmActivity; import com.philliphsu.clock2.settings.SettingsActivity; +import com.philliphsu.clock2.timers.TimersFragment; import butterknife.Bind; @@ -147,8 +148,14 @@ public class MainActivity extends BaseActivity { @Override public Fragment getItem(int position) { // getItem is called to instantiate the fragment for the given page. - return position == 0 ? AlarmsFragment.newInstance(1) - : PlaceholderFragment.newInstance(position + 1); + switch (position) { + case 0: + return AlarmsFragment.newInstance(1); + case 1: + return new TimersFragment(); + default: + return PlaceholderFragment.newInstance(position + 1); + } } @Override diff --git a/app/src/main/java/com/philliphsu/clock2/RecyclerViewFragment.java b/app/src/main/java/com/philliphsu/clock2/RecyclerViewFragment.java new file mode 100644 index 0000000..5ad65d4 --- /dev/null +++ b/app/src/main/java/com/philliphsu/clock2/RecyclerViewFragment.java @@ -0,0 +1,78 @@ +package com.philliphsu.clock2; + +import android.database.Cursor; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v4.app.LoaderManager; +import android.support.v4.content.Loader; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import butterknife.Bind; + +/** + * Created by Phillip Hsu on 7/26/2016. + */ +public abstract class RecyclerViewFragment, + A extends BaseAdapter> // TODO: From AlarmsCursorAdapter, abstract it out and use that type here. + extends BaseFragment implements + LoaderManager.LoaderCallbacks, + OnListItemInteractionListener { + + private A mAdapter; + + // TODO: Rename id to recyclerView? + // TODO: Rename variable to mRecyclerView? + @Bind(R.id.list) RecyclerView mList; + + public abstract void onFabClick(); + + /** + * @return the adapter to set on the RecyclerView + */ + protected abstract A getAdapter(); + + /** + * @return the LayoutManager to set on the RecyclerView. The default implementation + * returns a vertical LinearLayoutManager. + */ + protected RecyclerView.LayoutManager getLayoutManager() { + // Called in onCreateView(), so the host activity is alive already. + return new LinearLayoutManager(getActivity()); + } + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + View view = super.onCreateView(inflater, container, savedInstanceState); + mList.setLayoutManager(getLayoutManager()); + mList.setAdapter(mAdapter = getAdapter()); + return view; + } + + @Override + public void onLoadFinished(Loader loader, Cursor data) { + // TODO: Change the adapter type to one that supports Cursors as its dataset +// mAdapter.swapCursor(data); + } + + @Override + public void onLoaderReset(Loader loader) { + // TODO: Change the adapter type to one that supports Cursors as its dataset +// mAdapter.swapCursor(null); + } + + /** + * @return a layout resource that MUST contain a RecyclerView. The default implementation + * returns a layout that has just a single RecyclerView in its hierarchy. + */ + @Override + protected int contentLayout() { + // TODO: Rename to fragment_recycler_view + return R.layout.fragment_alarms; + } +} diff --git a/app/src/main/java/com/philliphsu/clock2/Timer.java b/app/src/main/java/com/philliphsu/clock2/Timer.java new file mode 100644 index 0000000..b7fd08f --- /dev/null +++ b/app/src/main/java/com/philliphsu/clock2/Timer.java @@ -0,0 +1,117 @@ +package com.philliphsu.clock2; + +import android.os.SystemClock; + +import com.google.auto.value.AutoValue; + +import java.util.concurrent.TimeUnit; + +/** + * Created by Phillip Hsu on 7/25/2016. + */ +@AutoValue +public abstract class Timer { + private static final int MINUTE = 60 * 1000; + + private long id; + private long endTime; + private long pauseTime; + + public abstract int hour(); + public abstract int minute(); + public abstract int second(); + public abstract String group(); + public abstract String label(); + + public static Timer create(int hour, int minute, int second) { + return create(hour, minute, second, "", ""); + } + + public static Timer createWithGroup(int hour, int minute, int second, String group) { + return create(hour, minute, second, group, ""); + } + + public static Timer createWithLabel(int hour, int minute, int second, String label) { + return create(hour, minute, second, "", label); + } + + public static Timer create(int hour, int minute, int second, String group, String label) { + if (hour < 0 || minute < 0 || second < 0 || (hour == 0 && minute == 0 && second == 0)) + throw new IllegalArgumentException("Cannot create a timer with h = " + + hour + ", m = " + minute + ", s = " + second); + return new AutoValue_Timer(hour, minute, second, group, label); + } + + public long id() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public long endTime() { + return endTime; + } + + public boolean expired() { + return /*!hasStarted() ||*/endTime <= SystemClock.elapsedRealtime(); + } + + public long timeRemaining() { + if (!hasStarted()) + return 0; + return isRunning() + ? endTime - SystemClock.elapsedRealtime() + : endTime - pauseTime; + } + + public long duration() { + return TimeUnit.HOURS.toMillis(hour()) + + TimeUnit.MINUTES.toMillis(minute()) + + TimeUnit.SECONDS.toMillis(second()); + } + + public void start() { + if (isRunning()) + throw new IllegalStateException("Cannot start a timer that has already started OR is already running"); + // TOneverDO: use nanos, AlarmManager expects times in millis + endTime = SystemClock.elapsedRealtime() + duration(); + } + + public void pause() { + if (!isRunning()) + throw new IllegalStateException("Cannot pause a timer that is not running OR has not started"); + pauseTime = SystemClock.elapsedRealtime(); + } + + public void resume() { + if (!hasStarted() || isRunning()) + throw new IllegalStateException("Cannot resume a timer that is already running OR has not started"); + endTime += SystemClock.elapsedRealtime() - pauseTime; + pauseTime = 0; + } + + public void stop() { + endTime = 0; + pauseTime = 0; + } + + public void addOneMinute() { + if (!isRunning()) + throw new IllegalStateException("Cannot extend a timer that is not running"); + if (expired()) { + endTime = SystemClock.elapsedRealtime() + MINUTE; + } else { + endTime += MINUTE; + } + } + + public boolean hasStarted() { + return endTime > 0; + } + + public boolean isRunning() { + return hasStarted() && pauseTime == 0; + } +} diff --git a/app/src/main/java/com/philliphsu/clock2/timers/CountdownChronometer.java b/app/src/main/java/com/philliphsu/clock2/timers/CountdownChronometer.java new file mode 100644 index 0000000..6b00b91 --- /dev/null +++ b/app/src/main/java/com/philliphsu/clock2/timers/CountdownChronometer.java @@ -0,0 +1,372 @@ +package com.philliphsu.clock2.timers; + +import android.annotation.TargetApi; +import android.content.Context; +import android.os.Handler; +import android.os.Message; +import android.os.SystemClock; +import android.text.format.DateUtils; +import android.util.AttributeSet; +import android.util.Log; +import android.widget.TextView; + +import java.util.Formatter; +import java.util.IllegalFormatException; +import java.util.Locale; + +/** + * Created by Phillip Hsu on 7/25/2016. + * + * A modified version of the framework's Chronometer widget to count down + * towards the base time. The ability to count down was added to Chronometer + * in API 24. + */ +public class CountdownChronometer extends TextView { + private static final String TAG = "CountdownChronometer"; + + /** + * A callback that notifies when the chronometer has incremented on its own. + */ + public interface OnChronometerTickListener { + + /** + * Notification that the chronometer has changed. + */ + void onChronometerTick(CountdownChronometer chronometer); + + } + + private long mBase; + private long mNow; // the currently displayed time +// private long mPause; // the time at which pause() was called +// private long mDuration; + private boolean mVisible; + private boolean mStarted; + private boolean mRunning; + private boolean mLogged; + private String mFormat; + private Formatter mFormatter; + private Locale mFormatterLocale; + private Object[] mFormatterArgs = new Object[1]; + private StringBuilder mFormatBuilder; + private OnChronometerTickListener mOnChronometerTickListener; + private StringBuilder mRecycle = new StringBuilder(8); + + private static final int TICK_WHAT = 2; + + /** + * Initialize this Chronometer object. + * Sets the base to the current time. + */ + public CountdownChronometer(Context context) { + this(context, null, 0); + } + + /** + * Initialize with standard view layout information. + * Sets the base to the current time. + */ + public CountdownChronometer(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + /** + * Initialize with standard view layout information and style. + * Sets the base to the current time. + */ + public CountdownChronometer(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + +// final TypedArray a = context.obtainStyledAttributes( +// attrs, com.android.internal.R.styleable.Chronometer, defStyleAttr, 0); +// setFormat(a.getString(com.android.internal.R.styleable.Chronometer_format)); +// a.recycle(); + + init(); + } + + @TargetApi(21) + public CountdownChronometer(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + +// final TypedArray a = context.obtainStyledAttributes( +// attrs, com.android.internal.R.styleable.Chronometer, defStyleAttr, defStyleRes); +// setFormat(a.getString(com.android.internal.R.styleable.Chronometer_format)); +// a.recycle(); + + init(); + } + + private void init() { + mBase = SystemClock.elapsedRealtime(); + updateText(mBase); + } + + /** + * Set the time that the count-up timer is in reference to. + * + * @param base Use the {@link SystemClock#elapsedRealtime} time base. + */ +// @android.view.RemotableViewMethod + public void setBase(long base) { + mBase = base; +// mDuration = base - SystemClock.elapsedRealtime(); + dispatchChronometerTick(); + updateText(SystemClock.elapsedRealtime()); + } + + /** + * Return the base time as set through {@link #setBase}. + */ + public long getBase() { + return mBase; + } + + /** + * Equivalent to {@link #setBase(long) setBase(SystemClock.elapsedRealtime() + duration)}. + */ + public void setDuration(long duration) { +// mDuration = duration; + setBase(SystemClock.elapsedRealtime() + duration); + } + +// /** +// * Return the duration of this countdown. +// */ +// public long getDuration() { +// return mDuration; +// } + + /** + * Sets the format string used for display. The Chronometer will display + * this string, with the first "%s" replaced by the current timer value in + * "MM:SS" or "H:MM:SS" form. + * + * If the format string is null, or if you never call setFormat(), the + * Chronometer will simply display the timer value in "MM:SS" or "H:MM:SS" + * form. + * + * @param format the format string. + */ +// @android.view.RemotableViewMethod + public void setFormat(String format) { + mFormat = format; + if (format != null && mFormatBuilder == null) { + mFormatBuilder = new StringBuilder(format.length() * 2); + } + } + + /** + * Returns the current format string as set through {@link #setFormat}. + */ + public String getFormat() { + return mFormat; + } + + /** + * Sets the listener to be called when the chronometer changes. + * + * @param listener The listener. + */ + public void setOnChronometerTickListener(OnChronometerTickListener listener) { + mOnChronometerTickListener = listener; + } + + /** + * @return The listener (may be null) that is listening for chronometer change + * events. + */ + public OnChronometerTickListener getOnChronometerTickListener() { + return mOnChronometerTickListener; + } + + /** + * Start counting up. This does not affect the base as set from {@link #setBase}, just + * the view display. + * + * Chronometer works by regularly scheduling messages to the handler, even when the + * Widget is not visible. To make sure resource leaks do not occur, the user should + * make sure that each start() call has a reciprocal call to {@link #stop}. + */ + public void start() { + // If we don't do this, the text display won't get to count + // all of the seconds in the set duration. Time passes + // between the call to setDuration(), or setBase(), and start(). +// mBase = SystemClock.elapsedRealtime() + mDuration; + mStarted = true; + updateRunning(); + } + + /** + * Stop counting up. This does not affect the base as set from {@link #setBase}, just + * the view display. + * + * This stops the messages to the handler, effectively releasing resources that would + * be held as the chronometer is running, via {@link #start}. + */ + public void stop() { + mStarted = false; + updateRunning(); +// setDuration(mDuration); // Reset the text display + } + +// public void pause() { +// if (mPause == 0 && mRunning) { +// mPause = SystemClock.elapsedRealtime(); +// } +// mStarted = false; +// updateRunning(); +// } +// +// public void resume() { +// if (mPause > 0 && !mRunning) { +// mBase += SystemClock.elapsedRealtime() - mPause; +// mPause = 0; +// } +// mStarted = true; +// updateRunning(); +// } + + /** + * The same as calling {@link #start} or {@link #stop}. + * @hide pending API council approval + */ +// @android.view.RemotableViewMethod + public void setStarted(boolean started) { + mStarted = started; + updateRunning(); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + mVisible = false; + updateRunning(); + } + + @Override + protected void onWindowVisibilityChanged(int visibility) { + Log.d(TAG, "onWindowVisibilityChanged()"); + super.onWindowVisibilityChanged(visibility); + mVisible = visibility == VISIBLE; + updateRunning(); + } + + private synchronized void updateText(long now) { + mNow = now; + long seconds = mBase - now; + seconds /= 1000; + String text = DateUtils.formatElapsedTime(mRecycle, seconds); + + if (mFormat != null) { + Locale loc = Locale.getDefault(); + if (mFormatter == null || !loc.equals(mFormatterLocale)) { + mFormatterLocale = loc; + mFormatter = new Formatter(mFormatBuilder, loc); + } + mFormatBuilder.setLength(0); + mFormatterArgs[0] = text; + try { + mFormatter.format(mFormat, mFormatterArgs); + text = mFormatBuilder.toString(); + } catch (IllegalFormatException ex) { + if (!mLogged) { + Log.w(TAG, "Illegal format string: " + mFormat); + mLogged = true; + } + } + } + setText(text); + } + + private void updateRunning() { + boolean running = mVisible && mStarted; + if (running != mRunning) { + if (running) { + Log.d(TAG, "Running"); + updateText(SystemClock.elapsedRealtime()); + dispatchChronometerTick(); + mHandler.sendMessageDelayed(Message.obtain(mHandler, TICK_WHAT), 1000); + } else { + Log.d(TAG, "Not running anymore"); + mHandler.removeMessages(TICK_WHAT); + } + mRunning = running; + } + } + + private Handler mHandler = new Handler() { + public void handleMessage(Message m) { + if (mRunning) { + updateText(SystemClock.elapsedRealtime()); + dispatchChronometerTick(); + sendMessageDelayed(Message.obtain(this, TICK_WHAT), 1000); + } + } + }; + + void dispatchChronometerTick() { + if (mOnChronometerTickListener != null) { + mOnChronometerTickListener.onChronometerTick(this); + } + } + + private static final int MIN_IN_SEC = 60; + private static final int HOUR_IN_SEC = MIN_IN_SEC*60; +// private static String formatDuration(long ms) { +// final Resources res = Resources.getSystem(); +// final StringBuilder text = new StringBuilder(); +// +// int duration = (int) (ms / DateUtils.SECOND_IN_MILLIS); +// if (duration < 0) { +// duration = -duration; +// } +// +// int h = 0; +// int m = 0; +// +// if (duration >= HOUR_IN_SEC) { +// h = duration / HOUR_IN_SEC; +// duration -= h * HOUR_IN_SEC; +// } +// if (duration >= MIN_IN_SEC) { +// m = duration / MIN_IN_SEC; +// duration -= m * MIN_IN_SEC; +// } +// int s = duration; +// +// try { +// if (h > 0) { +// text.append(res.getQuantityString( +// com.android.internal.R.plurals.duration_hours, h, h)); +// } +// if (m > 0) { +// if (text.length() > 0) { +// text.append(' '); +// } +// text.append(res.getQuantityString( +// com.android.internal.R.plurals.duration_minutes, m, m)); +// } +// +// if (text.length() > 0) { +// text.append(' '); +// } +// text.append(res.getQuantityString( +// com.android.internal.R.plurals.duration_seconds, s, s)); +// } catch (Resources.NotFoundException e) { +// // Ignore; plurals throws an exception for an untranslated quantity for a given locale. +// return null; +// } +// return text.toString(); +// } +// +// @Override +// public CharSequence getContentDescription() { +// return formatDuration(mNow - mBase); +// } +// +// @Override +// public CharSequence getAccessibilityClassName() { +// return CountdownChronometer.class.getName(); +// } +} diff --git a/app/src/main/java/com/philliphsu/clock2/timers/TimerAdapter.java b/app/src/main/java/com/philliphsu/clock2/timers/TimerAdapter.java new file mode 100644 index 0000000..a3d4205 --- /dev/null +++ b/app/src/main/java/com/philliphsu/clock2/timers/TimerAdapter.java @@ -0,0 +1,39 @@ +package com.philliphsu.clock2.timers; + +import android.view.ViewGroup; + +import com.philliphsu.clock2.BaseAdapter; +import com.philliphsu.clock2.OnListItemInteractionListener; +import com.philliphsu.clock2.Timer; + +import java.util.List; + +/** + * Created by Phillip Hsu on 7/26/2016. + */ +public class TimerAdapter extends BaseAdapter { + + public TimerAdapter(List items, OnListItemInteractionListener listener) { + super(Timer.class, items, listener); + } + + @Override + protected TimerViewHolder onCreateViewHolder(ViewGroup parent, OnListItemInteractionListener listener) { + return new TimerViewHolder(parent, listener); + } + + @Override + protected int compare(Timer o1, Timer o2) { + return 0; + } + + @Override + protected boolean areContentsTheSame(Timer oldItem, Timer newItem) { + return false; + } + + @Override + protected boolean areItemsTheSame(Timer item1, Timer item2) { + return false; + } +} diff --git a/app/src/main/java/com/philliphsu/clock2/timers/TimerViewHolder.java b/app/src/main/java/com/philliphsu/clock2/timers/TimerViewHolder.java new file mode 100644 index 0000000..980ce66 --- /dev/null +++ b/app/src/main/java/com/philliphsu/clock2/timers/TimerViewHolder.java @@ -0,0 +1,103 @@ +package com.philliphsu.clock2.timers; + +import android.view.ViewGroup; +import android.widget.ImageButton; +import android.widget.ProgressBar; +import android.widget.TextView; + +import com.philliphsu.clock2.BaseViewHolder; +import com.philliphsu.clock2.OnListItemInteractionListener; +import com.philliphsu.clock2.R; +import com.philliphsu.clock2.Timer; + +import butterknife.Bind; +import butterknife.OnClick; + +/** + * Created by Phillip Hsu on 7/25/2016. + */ +public class TimerViewHolder extends BaseViewHolder { + + @Bind(R.id.label) TextView mLabel; + @Bind(R.id.duration) CountdownChronometer mChronometer; + @Bind(R.id.progress_bar) ProgressBar mProgressBar; + @Bind(R.id.add_one_minute) ImageButton mAddOneMinute; + @Bind(R.id.start_pause) ImageButton mStartPause; + @Bind(R.id.stop) ImageButton mStop; + + public TimerViewHolder(ViewGroup parent, OnListItemInteractionListener listener) { + super(parent, R.layout.item_timer, listener); + } + + @Override + public void onBind(Timer timer) { + super.onBind(timer); + bindLabel(timer.label()); + bindChronometer(timer); + } + + @OnClick(R.id.start_pause) + void startPause() { + // Every time BEFORE you call start() on the chronometer, you have to + // call setBase() again because time passes between the time we first call + // setBase() and the time we start the timer. Otherwise, after start() is called, + // that period of time would appear to have counted off already, as the text + // display jumps downward by that amount of time from its initial duration. + Timer t = getItem(); + if (t.hasStarted()) { + if (t.isRunning()) { + // Records the start of this pause + t.pause(); + // Stops the counting, but does not reset any values + mChronometer.stop(); + } else { + // Pushes up the end time + t.resume(); + // Use the new end time as reference from now + mChronometer.setBase(t.endTime()); + mChronometer.start(); + } + } else { + t.start(); + mChronometer.setBase(t.endTime()); + mChronometer.start(); + } + } + + private void bindLabel(String label) { + if (!label.isEmpty()) { + mLabel.setText(label); + } + } + + private void bindChronometer(Timer timer) { + // In case we're reusing a chronometer instance that could be running: + // If the Timer instance is not running, this just guarantees the chronometer + // won't tick, regardless of whether it was running. + // If the Timer instance is running, we don't care whether the chronometer is + // also running, because we call start() right after. Stopping it just + // guarantees that, if it was running, we don't deliver another set of + // concurrent messages to its handler. + mChronometer.stop(); + + if (!timer.hasStarted()) { + // Set the initial text + mChronometer.setDuration(timer.duration()); + } else if (timer.isRunning()) { + // Re-initialize the base + mChronometer.setBase(timer.endTime()); + // Previously stopped, so no old messages will interfere. + mChronometer.start(); + } else { + // Set the text as last displayed before we stopped. + // When you call stop() on a Chronometer, it freezes the current text shown, + // so why do we need this? While that is sufficient for a static View layout, + // VH recycling will reuse the same Chronometer widget across multiple VHs, + // so we would have invalid data across those VHs. + // If a new VH is created, then the chronometer it contains will be in its + // uninitialized state. We will always need to set the Chronometer's base + // every time VHs are bound/recycled. + mChronometer.setDuration(timer.timeRemaining()); + } + } +} diff --git a/app/src/main/java/com/philliphsu/clock2/timers/TimersFragment.java b/app/src/main/java/com/philliphsu/clock2/timers/TimersFragment.java new file mode 100644 index 0000000..ab6fa19 --- /dev/null +++ b/app/src/main/java/com/philliphsu/clock2/timers/TimersFragment.java @@ -0,0 +1,40 @@ +package com.philliphsu.clock2.timers; + + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.philliphsu.clock2.R; +import com.philliphsu.clock2.timers.dummy.DummyContent; + +import butterknife.ButterKnife; + +/** + * TODO: Extend from RecyclerViewFragment. + */ +public class TimersFragment extends Fragment { + + public TimersFragment() { + // Required empty public constructor + } + + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_alarms, container, false); + ButterKnife.bind(this, view); + + RecyclerView rv = ButterKnife.findById(view, R.id.list); + rv.setLayoutManager(new LinearLayoutManager(getActivity())); + rv.setAdapter(new TimerAdapter(DummyContent.ITEMS, null)); + + return view; + } + +} diff --git a/app/src/main/java/com/philliphsu/clock2/timers/dummy/DummyContent.java b/app/src/main/java/com/philliphsu/clock2/timers/dummy/DummyContent.java new file mode 100644 index 0000000..805d2d4 --- /dev/null +++ b/app/src/main/java/com/philliphsu/clock2/timers/dummy/DummyContent.java @@ -0,0 +1,37 @@ +package com.philliphsu.clock2.timers.dummy; + +import com.philliphsu.clock2.Timer; + +import java.util.ArrayList; +import java.util.List; + +/** + * Helper class for providing sample content for user interfaces created by + * Android template wizards. + *

+ * TODO: Replace all uses of this class before publishing your app. + */ +public class DummyContent { + + /** + * An array of sample (dummy) items. + */ + public static final List ITEMS = new ArrayList<>(); + + private static final int COUNT = 1; + + static { + // Add some sample items. + for (int i = 1; i <= COUNT; i++) { + addItem(createTimer(i)); + } + } + + private static void addItem(Timer item) { + ITEMS.add(item); + } + + private static Timer createTimer(int position) { + return Timer.create(1, 0, 0); + } +} diff --git a/app/src/main/res/layout/item_timer.xml b/app/src/main/res/layout/item_timer.xml new file mode 100644 index 0000000..91f8e9e --- /dev/null +++ b/app/src/main/res/layout/item_timer.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 284f44c..f37ce86 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -188,4 +188,7 @@ "For example, position the FAB to one side of stream of a cards so the FAB won’t interfere " "when a user tries to pick up one of cards.\n\n" + + + Hello blank fragment