diff --git a/app/src/main/java/com/philliphsu/clock2/Alarm.java b/app/src/main/java/com/philliphsu/clock2/Alarm.java index 46e65a9..c70b99e 100644 --- a/app/src/main/java/com/philliphsu/clock2/Alarm.java +++ b/app/src/main/java/com/philliphsu/clock2/Alarm.java @@ -64,7 +64,7 @@ public abstract class Alarm implements JsonSerializable { .label(jsonObject.getString(KEY_LABEL)) .ringtone(jsonObject.getString(KEY_RINGTONE)) .vibrates(jsonObject.getBoolean(KEY_VIBRATES)) - .build(); + .rebuild(); alarm.setEnabled(jsonObject.getBoolean(KEY_ENABLED)); return alarm; } catch (JSONException e) { @@ -218,11 +218,19 @@ public abstract class Alarm implements JsonSerializable { /*not public*/abstract Alarm autoBuild(); public Alarm build() { - this.id(idCount++); + this.id(++idCount); // TOneverDO: change to post-increment without also adding offset of 1 to idCount in rebuild() Alarm alarm = autoBuild(); checkTime(alarm.hour(), alarm.minutes()); return alarm; } + + /** Should only be called when recreating an instance from JSON */ + private Alarm rebuild() { + Alarm alarm = autoBuild(); + idCount = alarm.id(); // prevent future instances from id collision + checkTime(alarm.hour(), alarm.minutes()); + return alarm; + } } private static void checkDay(int day) { diff --git a/app/src/main/java/com/philliphsu/clock2/BaseViewHolder.java b/app/src/main/java/com/philliphsu/clock2/BaseViewHolder.java index ae86d92..12c0726 100644 --- a/app/src/main/java/com/philliphsu/clock2/BaseViewHolder.java +++ b/app/src/main/java/com/philliphsu/clock2/BaseViewHolder.java @@ -44,7 +44,7 @@ public abstract class BaseViewHolder extends RecyclerView.ViewHolder implemen @Override public final void onClick(View v) { if (mListener != null) { - mListener.onListItemInteraction(mItem); + mListener.onListItemClick(mItem); } } } diff --git a/app/src/main/java/com/philliphsu/clock2/MainActivity.java b/app/src/main/java/com/philliphsu/clock2/MainActivity.java index 217e7d5..766fad7 100644 --- a/app/src/main/java/com/philliphsu/clock2/MainActivity.java +++ b/app/src/main/java/com/philliphsu/clock2/MainActivity.java @@ -6,11 +6,13 @@ import android.content.Intent; import android.media.RingtoneManager; import android.os.Bundle; import android.support.design.widget.FloatingActionButton; +import android.support.design.widget.Snackbar; import android.support.design.widget.TabLayout; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentPagerAdapter; import android.support.v4.view.ViewPager; +import android.util.Log; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; @@ -22,6 +24,7 @@ import com.philliphsu.clock2.editalarm.EditAlarmActivity; import com.philliphsu.clock2.ringtone.RingtoneActivity; public class MainActivity extends BaseActivity implements AlarmsFragment.OnAlarmInteractionListener { + private static final String TAG = "MainActivity"; /** * The {@link android.support.v4.view.PagerAdapter} that will provide @@ -78,8 +81,6 @@ public class MainActivity extends BaseActivity implements AlarmsFragment.OnAlarm }); } - - @Override protected int layoutResId() { return R.layout.activity_main; @@ -158,9 +159,8 @@ public class MainActivity extends BaseActivity implements AlarmsFragment.OnAlarm @Override public Fragment getItem(int position) { // getItem is called to instantiate the fragment for the given page. - // Return a PlaceholderFragment (defined as a static inner class below). - //return PlaceholderFragment.newInstance(position + 1); - return AlarmsFragment.newInstance(1); + return position == 0 ? AlarmsFragment.newInstance(1) + : PlaceholderFragment.newInstance(position + 1); } @Override @@ -184,10 +184,25 @@ public class MainActivity extends BaseActivity implements AlarmsFragment.OnAlarm } @Override - public void onListItemInteraction(Alarm item) { + public void onListItemClick(Alarm item) { startEditAlarmActivity(item.id()); } + @Override + public void onListItemDeleted(Alarm item) { + Log.d(TAG, "Deleted " + item.toString()); + Snackbar.make(findViewById(R.id.main_content), + getString(R.string.snackbar_item_deleted, "Alarm"), + Snackbar.LENGTH_LONG) // TODO: not long enough? + .setAction(R.string.snackbar_undo_item_deleted, new View.OnClickListener() { + @Override + public void onClick(View v) { + // TODO get AlarmsFragment instance and restore the alarm + } + }) + .show(); + } + private void startEditAlarmActivity(long alarmId) { Intent intent = new Intent(this, EditAlarmActivity.class); intent.putExtra(EditAlarmActivity.EXTRA_ALARM_ID, alarmId); diff --git a/app/src/main/java/com/philliphsu/clock2/OnListItemInteractionListener.java b/app/src/main/java/com/philliphsu/clock2/OnListItemInteractionListener.java index 1cbdc39..664820b 100644 --- a/app/src/main/java/com/philliphsu/clock2/OnListItemInteractionListener.java +++ b/app/src/main/java/com/philliphsu/clock2/OnListItemInteractionListener.java @@ -12,5 +12,6 @@ package com.philliphsu.clock2; * be easily adapted to not just Fragments, but also custom Views, Activities, etc. */ public interface OnListItemInteractionListener { - void onListItemInteraction(T item); + void onListItemClick(T item); + void onListItemDeleted(T item); } diff --git a/app/src/main/java/com/philliphsu/clock2/alarms/AlarmsFragment.java b/app/src/main/java/com/philliphsu/clock2/alarms/AlarmsFragment.java index cc876e0..819e8ec 100644 --- a/app/src/main/java/com/philliphsu/clock2/alarms/AlarmsFragment.java +++ b/app/src/main/java/com/philliphsu/clock2/alarms/AlarmsFragment.java @@ -16,6 +16,9 @@ import com.philliphsu.clock2.R; import com.philliphsu.clock2.model.AlarmsRepository; import com.philliphsu.clock2.model.BaseRepository; +import butterknife.Bind; +import butterknife.ButterKnife; + /** * A fragment representing a list of Items. *

@@ -29,9 +32,11 @@ public class AlarmsFragment extends Fragment implements BaseRepository.DataObser // TODO: Customize parameters private int mColumnCount = 1; private OnAlarmInteractionListener mListener; + private AlarmsRepository mRepo; private AlarmsAdapter mAdapter; - private AlarmsRepository mRepo; + + @Bind(R.id.list) RecyclerView mList; /** * Mandatory empty constructor for the fragment manager to instantiate the @@ -62,49 +67,55 @@ public class AlarmsFragment extends Fragment implements BaseRepository.DataObser public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_alarms, container, false); - + ButterKnife.bind(this, view); // Set the adapter - if (view instanceof RecyclerView) { - Context context = view.getContext(); - RecyclerView recyclerView = (RecyclerView) view; - if (mColumnCount <= 1) { - recyclerView.setLayoutManager(new LinearLayoutManager(context)); - } else { - recyclerView.setLayoutManager(new GridLayoutManager(context, mColumnCount)); - } - mAdapter = new AlarmsAdapter(mRepo.getItems(), mListener); - recyclerView.setAdapter(mAdapter); + Context context = view.getContext(); + if (mColumnCount <= 1) { + mList.setLayoutManager(new LinearLayoutManager(context)); + } else { + mList.setLayoutManager(new GridLayoutManager(context, mColumnCount)); } + mAdapter = new AlarmsAdapter(mRepo.getItems(), mListener); + mList.setAdapter(mAdapter); return view; } + @Override + public void onDestroyView() { + super.onDestroyView(); + ButterKnife.unbind(this); // Only for fragments! + } + @Override public void onAttach(Context context) { super.onAttach(context); if (context instanceof OnAlarmInteractionListener) { mListener = (OnAlarmInteractionListener) context; - mRepo = AlarmsRepository.getInstance(context); - mRepo.registerDataObserver(this); } else { throw new RuntimeException(context.toString() + " must implement OnAlarmInteractionListener"); } + mRepo = AlarmsRepository.getInstance(context); + mRepo.registerDataObserver(this); } @Override public void onDetach() { super.onDetach(); mListener = null; + mRepo.unregisterDataObserver(); + mRepo = null; } @Override public void onItemAdded(Alarm item) { - mAdapter.addItem(item); + mList.smoothScrollToPosition(mAdapter.addItem(item)); } @Override public void onItemDeleted(Alarm item) { mAdapter.removeItem(item); + mListener.onListItemDeleted(item); } @Override diff --git a/app/src/main/java/com/philliphsu/clock2/editalarm/EditAlarmActivity.java b/app/src/main/java/com/philliphsu/clock2/editalarm/EditAlarmActivity.java index bc2707b..a96a1de 100644 --- a/app/src/main/java/com/philliphsu/clock2/editalarm/EditAlarmActivity.java +++ b/app/src/main/java/com/philliphsu/clock2/editalarm/EditAlarmActivity.java @@ -1,8 +1,14 @@ package com.philliphsu.clock2.editalarm; +import android.media.Ringtone; +import android.media.RingtoneManager; +import android.net.Uri; import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v7.app.ActionBar; import android.support.v7.widget.SwitchCompat; -import android.text.format.DateFormat; +import android.view.Menu; +import android.view.MenuItem; import android.widget.Button; import android.widget.CheckBox; import android.widget.EditText; @@ -19,13 +25,16 @@ import java.util.Date; import butterknife.Bind; import butterknife.OnClick; +import static android.text.format.DateFormat.getTimeFormat; import static com.philliphsu.clock2.DaysOfWeek.SATURDAY; import static com.philliphsu.clock2.DaysOfWeek.SUNDAY; public class EditAlarmActivity extends BaseActivity { - public static final String EXTRA_ALARM_ID = "com.philliphsu.clock2.editalarm.extra.ALARM_ID"; + private static final int ID_MENU_ITEM = 0; + @Nullable private Alarm mAlarm; + @Bind(R.id.save) Button mSave; @Bind(R.id.delete) Button mDelete; @Bind(R.id.on_off) SwitchCompat mSwitch; @@ -39,24 +48,52 @@ public class EditAlarmActivity extends BaseActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - getSupportActionBar().setTitle("Snoozing until 12:40 PM"); setWeekDaysText(); long alarmId = getIntent().getLongExtra(EXTRA_ALARM_ID, -1); if (alarmId > -1) { - Alarm alarm = AlarmsRepository.getInstance(this).getItem(alarmId); - if (alarm != null) { - mSwitch.setChecked(alarm.isEnabled()); - mTimeText.setText(DateFormat.getTimeFormat(this).format(new Date(alarm.ringsAt()))); + mAlarm = AlarmsRepository.getInstance(this).getItem(alarmId); + if (mAlarm != null) { + mSwitch.setChecked(mAlarm.isEnabled()); + mTimeText.setText(getTimeFormat(this).format(new Date(mAlarm.ringsAt()))); for (int i = SUNDAY; i <= SATURDAY; i++) { // What position in the week is this day located at? int at = DaysOfWeek.getInstance(this).positionOf(i); // Toggle the button that corresponds to this day - mDays[at].setChecked(alarm.isRecurring(i)); + mDays[at].setChecked(mAlarm.isRecurring(i)); + } + mLabel.setText(mAlarm.label()); + Ringtone r = RingtoneManager.getRingtone(this, Uri.parse(mAlarm.ringtone())); + mRingtone.setText(r.getTitle(this)); + mVibrate.setChecked(mAlarm.vibrates()); + if (mAlarm.isSnoozed()) { + String title = getString(R.string.title_snoozing_until, + getTimeFormat(this).format(new Date(mAlarm.snoozingUntil())) + ); + ActionBar ab = getSupportActionBar(); + ab.setDisplayShowTitleEnabled(true); + ab.setTitle(title); } } } } + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + // TODO: Read upcoming threshold preference + if (mAlarm != null && (mAlarm.ringsWithinHours(2) || mAlarm.isSnoozed())) { + if (menu.findItem(ID_MENU_ITEM) == null) { + // Create dynamically because there is almost nothing we can statically define + // in a layout resource. + menu.add(0 /*group*/, ID_MENU_ITEM, 0 /*order*/, + mAlarm.isSnoozed() ? R.string.done_snoozing : R.string.dismiss_now) + .setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_IF_ROOM) + .setIcon(android.R.drawable.ic_delete); + // TODO: Show correct icon based on which is happening + } + } + return super.onPrepareOptionsMenu(menu); + } + @Override protected int layoutResId() { return R.layout.activity_edit_alarm; @@ -67,11 +104,6 @@ public class EditAlarmActivity extends BaseActivity { return 0; } - @Override - protected boolean isDisplayShowTitleEnabled() { - return true; - } - @OnClick(R.id.save) void save() { boolean[] days = new boolean[7]; @@ -87,7 +119,9 @@ public class EditAlarmActivity extends BaseActivity { @OnClick(R.id.delete) void delete() { - //AlarmsRepository.getInstance(this).deleteItem(); + if (mAlarm != null) { + AlarmsRepository.getInstance(this).deleteItem(mAlarm); + } finish(); } diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index ab7207d..e69b1fb 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -6,7 +6,8 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" - tools:context="com.philliphsu.clock2.MainActivity"> + tools:context="com.philliphsu.clock2.MainActivity" + android:id="@+id/main_content"> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 44e2992..0611111 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -6,10 +6,17 @@ Clock+ Dummy Button DUMMY\nCONTENT + EditAlarmActivity Save Delete 12:00 AM + Snoozing until %1$s + Dismiss now + Done snoozing + + %1$s deleted + Undo Sun Mon