Depcrecated EditAlarmActivity, moved some code to AlarmFragment and ExpandedAlarmViewHolder
This commit is contained in:
parent
e408a7467a
commit
3732246bb7
@ -218,6 +218,52 @@ public class MainActivity extends BaseActivity {
|
||||
mAddItemDrawable = ContextCompat.getDrawable(this, R.drawable.ic_add_24dp);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
// If we get here, either this Activity OR one of its hosted Fragments
|
||||
// started a requested Activity for a result. The latter case may seem
|
||||
// strange; the Fragment is the one starting the requested Activity, so why
|
||||
// does the result end up in its host Activity? Shouldn't it end up in
|
||||
// Fragment#onActivityResult()? Actually, the Fragment's host Activity gets the
|
||||
// first shot at handling the result, before delegating it to the Fragment
|
||||
// in Fragment#onActivityResult().
|
||||
//
|
||||
// There are subtle points to keep in mind when it is actually the Fragment
|
||||
// that should handle the result, NOT this Activity. You MUST start
|
||||
// the requested Activity with Fragment#startActivityForResult(), NOT
|
||||
// Activity#startActivityForResult(). The former calls
|
||||
// FragmentActivity#startActivityFromFragment() to implement its behavior.
|
||||
// Among other things (not relevant to the discussion),
|
||||
// FragmentActivity#startActivityFromFragment() sets internal bit flags
|
||||
// that are necessary to achieve the described behavior (that this Activity
|
||||
// should delegate the result to the Fragment). Finally, you MUST call
|
||||
// through to the super implementation of Activity#onActivityResult(),
|
||||
// i.e. FragmentActivity#onActivityResult(). It is this method where
|
||||
// the aforementioned internal bit flags will be read to determine
|
||||
// which of this Activity's hosted Fragments started the requested
|
||||
// Activity.
|
||||
//
|
||||
// If you are not careful with these points and instead mistakenly call
|
||||
// Activity#startActivityForResult(), THEN YOU WILL ONLY BE ABLE TO
|
||||
// HANDLE THE REQUEST HERE; the super implementation of onActivityResult()
|
||||
// will not delegate the result to the Fragment, because the requisite
|
||||
// internal bit flags are not set with Activity#startActivityForResult().
|
||||
//
|
||||
// Further reading:
|
||||
// http://stackoverflow.com/q/6147884/5055032
|
||||
// http://stackoverflow.com/a/24303360/5055032
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
// This is a hacky workaround when you absolutely must have a Fragment
|
||||
// handle the result, even when it was not the one to start the requested
|
||||
// Activity. For example, the ExpandedAlarmViewHolder can start the ringtone
|
||||
// picker dialog (which is an Activity) for a result; ExpandedAlarmViewHolder
|
||||
// has no reference to the AlarmsFragment, but it does have a reference to a
|
||||
// Context (which we can cast to Activity). Thus, ExpandedAlarmViewHolder
|
||||
// uses Activity#startActivityForResult().
|
||||
mSectionsPagerAdapter.getFragment(mViewPager.getCurrentItem())
|
||||
.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int layoutResId() {
|
||||
return R.layout.activity_main;
|
||||
|
||||
@ -2,18 +2,25 @@ package com.philliphsu.clock2.alarms;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.media.RingtoneManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.content.Loader;
|
||||
import android.text.format.DateFormat;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.philliphsu.clock2.Alarm;
|
||||
import com.philliphsu.clock2.AsyncAlarmsTableUpdateHandler;
|
||||
import com.philliphsu.clock2.R;
|
||||
import com.philliphsu.clock2.RecyclerViewFragment;
|
||||
import com.philliphsu.clock2.editalarm.EditAlarmActivity;
|
||||
import com.philliphsu.clock2.editalarm.BaseTimePickerDialog;
|
||||
import com.philliphsu.clock2.editalarm.NumberGridTimePickerDialog;
|
||||
import com.philliphsu.clock2.editalarm.NumpadTimePickerDialog;
|
||||
import com.philliphsu.clock2.model.AlarmCursor;
|
||||
import com.philliphsu.clock2.model.AlarmsListCursorLoader;
|
||||
import com.philliphsu.clock2.util.AlarmController;
|
||||
@ -23,15 +30,24 @@ public class AlarmsFragment extends RecyclerViewFragment<
|
||||
Alarm,
|
||||
BaseAlarmViewHolder,
|
||||
AlarmCursor,
|
||||
AlarmsCursorAdapter> implements ScrollHandler { // TODO: Move interface to base class
|
||||
AlarmsCursorAdapter>
|
||||
implements ScrollHandler, // TODO: Move interface to base class
|
||||
BaseTimePickerDialog.OnTimeSetListener {
|
||||
private static final String TAG = "AlarmsFragment";
|
||||
private static final int REQUEST_EDIT_ALARM = 0;
|
||||
// Public because MainActivity needs to use it.
|
||||
// TODO: private because we handle fab clicks in the fragment now
|
||||
public static final int REQUEST_CREATE_ALARM = 1;
|
||||
private static final String TAG_TIME_PICKER = "time_picker";
|
||||
|
||||
// TODO: Delete these constants. We no longer use EditAlarmActivity.
|
||||
// @Deprecated
|
||||
// private static final int REQUEST_EDIT_ALARM = 0;
|
||||
// // Public because MainActivity needs to use it.
|
||||
// // TODO: private because we handle fab clicks in the fragment now
|
||||
// @Deprecated
|
||||
// public static final int REQUEST_CREATE_ALARM = 1;
|
||||
|
||||
public static final int REQUEST_PICK_RINGTONE = 1;
|
||||
|
||||
// private AlarmsCursorAdapter mAdapter;
|
||||
private AsyncAlarmsTableUpdateHandler mAsyncAlarmsTableUpdateHandler;
|
||||
private AsyncAlarmsTableUpdateHandler mAsyncUpdateHandler;
|
||||
private AlarmController mAlarmController;
|
||||
private Handler mHandler = new Handler();
|
||||
private View mSnackbarAnchor;
|
||||
@ -64,7 +80,7 @@ public class AlarmsFragment extends RecyclerViewFragment<
|
||||
// See the Fragment lifecycle.
|
||||
mSnackbarAnchor = getActivity().findViewById(R.id.main_content);
|
||||
mAlarmController = new AlarmController(getActivity(), mSnackbarAnchor);
|
||||
mAsyncAlarmsTableUpdateHandler = new AsyncAlarmsTableUpdateHandler(getActivity(),
|
||||
mAsyncUpdateHandler = new AsyncAlarmsTableUpdateHandler(getActivity(),
|
||||
mSnackbarAnchor, this, mAlarmController);
|
||||
}
|
||||
|
||||
@ -90,8 +106,37 @@ public class AlarmsFragment extends RecyclerViewFragment<
|
||||
|
||||
@Override
|
||||
public void onFabClick() {
|
||||
Intent intent = new Intent(getActivity(), EditAlarmActivity.class);
|
||||
startActivityForResult(intent, REQUEST_CREATE_ALARM);
|
||||
// Intent intent = new Intent(getActivity(), EditAlarmActivity.class);
|
||||
// startActivityForResult(intent, REQUEST_CREATE_ALARM);
|
||||
|
||||
// Close the keyboard first, or else our dialog will be screwed up.
|
||||
// If not open, this does nothing.
|
||||
// TODO: I don't think the keyboard can possibly be open in this Fragment?
|
||||
// hideKeyboard(this); // This is only important for BottomSheetDialogs!
|
||||
|
||||
// Create a new instance each time we want to show the dialog.
|
||||
// If we keep a reference to the dialog, we keep its previous state as well.
|
||||
// So the next time we call show() on it, the input field will show the
|
||||
// last inputted time.
|
||||
BaseTimePickerDialog dialog = null;
|
||||
String numpadStyle = getString(R.string.number_pad);
|
||||
String gridStyle = getString(R.string.grid_selector);
|
||||
String prefTimePickerStyle = PreferenceManager.getDefaultSharedPreferences(getActivity()).getString(
|
||||
// key for the preference value to retrieve
|
||||
getString(R.string.key_time_picker_style),
|
||||
// default value
|
||||
numpadStyle);
|
||||
if (prefTimePickerStyle.equals(numpadStyle)) {
|
||||
dialog = NumpadTimePickerDialog.newInstance(this);
|
||||
} else if (prefTimePickerStyle.equals(gridStyle)) {
|
||||
dialog = NumberGridTimePickerDialog.newInstance(
|
||||
this, // OnTimeSetListener
|
||||
0, // Initial hour of day
|
||||
0, // Initial minute
|
||||
DateFormat.is24HourFormat(getActivity()));
|
||||
}
|
||||
// DISREGARD THE LINT WARNING ABOUT DIALOG BEING NULL.
|
||||
dialog.show(getFragmentManager(), TAG_TIME_PICKER);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@ -113,39 +158,47 @@ public class AlarmsFragment extends RecyclerViewFragment<
|
||||
Log.d(TAG, "onActivityResult()");
|
||||
if (resultCode != Activity.RESULT_OK || data == null)
|
||||
return;
|
||||
final Alarm alarm = data.getParcelableExtra(EditAlarmActivity.EXTRA_MODIFIED_ALARM);
|
||||
if (alarm == null)
|
||||
return;
|
||||
if (requestCode == REQUEST_PICK_RINGTONE) {
|
||||
Uri uri = data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI);
|
||||
Log.d(TAG, "Retrieved ringtone URI: " + uri);
|
||||
// TODO: We'll have to create a new Alarm instance with this ringtone value
|
||||
// because we don't have a setter method. Alternatively, write an independent
|
||||
// SQL update statement updating COLUMN_RINGTONE.
|
||||
}
|
||||
|
||||
// http://stackoverflow.com/a/27055512/5055032
|
||||
// "RecyclerView does not run animations in the first layout
|
||||
// pass after being attached." A workaround is to postpone
|
||||
// the CRUD operation to the next frame. A delay of 300ms is
|
||||
// short enough to not be noticeable, and long enough to
|
||||
// give us the animation *most of the time*.
|
||||
switch (requestCode) {
|
||||
case REQUEST_CREATE_ALARM:
|
||||
mHandler.postDelayed(
|
||||
new AsyncAddItemRunnable(mAsyncAlarmsTableUpdateHandler, alarm),
|
||||
300);
|
||||
break;
|
||||
case REQUEST_EDIT_ALARM:
|
||||
if (data.getBooleanExtra(EditAlarmActivity.EXTRA_IS_DELETING, false)) {
|
||||
// TODO: Should we delay this too? It seems animations run
|
||||
// some of the time.
|
||||
mAsyncAlarmsTableUpdateHandler.asyncDelete(alarm);
|
||||
} else {
|
||||
// TODO: Increase the delay, because update animation is
|
||||
// more elusive than insert.
|
||||
mHandler.postDelayed(
|
||||
new AsyncUpdateItemRunnable(mAsyncAlarmsTableUpdateHandler, alarm),
|
||||
300);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Log.i(TAG, "Could not handle request code " + requestCode);
|
||||
break;
|
||||
}
|
||||
// final Alarm alarm = data.getParcelableExtra(EditAlarmActivity.EXTRA_MODIFIED_ALARM);
|
||||
// if (alarm == null)
|
||||
// return;
|
||||
//
|
||||
// // http://stackoverflow.com/a/27055512/5055032
|
||||
// // "RecyclerView does not run animations in the first layout
|
||||
// // pass after being attached." A workaround is to postpone
|
||||
// // the CRUD operation to the next frame. A delay of 300ms is
|
||||
// // short enough to not be noticeable, and long enough to
|
||||
// // give us the animation *most of the time*.
|
||||
// switch (requestCode) {
|
||||
// case REQUEST_CREATE_ALARM:
|
||||
// mHandler.postDelayed(
|
||||
// new AsyncAddItemRunnable(mAsyncUpdateHandler, alarm),
|
||||
// 300);
|
||||
// break;
|
||||
// case REQUEST_EDIT_ALARM:
|
||||
// if (data.getBooleanExtra(EditAlarmActivity.EXTRA_IS_DELETING, false)) {
|
||||
// // TODO: Should we delay this too? It seems animations run
|
||||
// // some of the time.
|
||||
// mAsyncUpdateHandler.asyncDelete(alarm);
|
||||
// } else {
|
||||
// // TODO: Increase the delay, because update animation is
|
||||
// // more elusive than insert.
|
||||
// mHandler.postDelayed(
|
||||
// new AsyncUpdateItemRunnable(mAsyncUpdateHandler, alarm),
|
||||
// 300);
|
||||
// }
|
||||
// break;
|
||||
// default:
|
||||
// Log.i(TAG, "Could not handle request code " + requestCode);
|
||||
// break;
|
||||
// }
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -160,7 +213,7 @@ public class AlarmsFragment extends RecyclerViewFragment<
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// TODO: Just like with TimersCursorAdapter, we could pass in the mAsyncAlarmsTableUpdateHandler
|
||||
// TODO: Just like with TimersCursorAdapter, we could pass in the mAsyncUpdateHandler
|
||||
// to the AlarmsCursorAdapter and call these on the save and delete button click bindings.
|
||||
|
||||
@Override
|
||||
@ -168,7 +221,7 @@ public class AlarmsFragment extends RecyclerViewFragment<
|
||||
public void onListItemDeleted(final Alarm item) {
|
||||
// The corresponding VH will be automatically removed from view following
|
||||
// the requery, so we don't have to do anything to it.
|
||||
mAsyncAlarmsTableUpdateHandler.asyncDelete(item);
|
||||
mAsyncUpdateHandler.asyncDelete(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -180,7 +233,7 @@ public class AlarmsFragment extends RecyclerViewFragment<
|
||||
// TODO: Implement editing in the expanded VH. Then verify that changes
|
||||
// while in that VH are saved and updated after the requery.
|
||||
// getAdapter().collapse(position);
|
||||
mAsyncAlarmsTableUpdateHandler.asyncUpdate(item.getId(), item);
|
||||
mAsyncUpdateHandler.asyncUpdate(item.getId(), item);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -197,6 +250,18 @@ public class AlarmsFragment extends RecyclerViewFragment<
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTimeSet(ViewGroup viewGroup, int hourOfDay, int minute) {
|
||||
// When we request the Builder, default values are provided for us,
|
||||
// which is why we don't have to set the ringtone, label, etc.
|
||||
Alarm alarm = Alarm.builder()
|
||||
.hour(hourOfDay)
|
||||
.minutes(minute)
|
||||
.build();
|
||||
alarm.setEnabled(true);
|
||||
mAsyncUpdateHandler.asyncInsert(alarm);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
// TODO: We won't need these anymore, since we won't handle the db
|
||||
// update in onActivityResult() anymore.
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
package com.philliphsu.clock2.alarms;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.media.RingtoneManager;
|
||||
import android.net.Uri;
|
||||
import android.view.View;
|
||||
@ -23,7 +25,7 @@ import butterknife.OnClick;
|
||||
*/
|
||||
public class ExpandedAlarmViewHolder extends BaseAlarmViewHolder {
|
||||
|
||||
@Bind(R.id.save) Button mSave;
|
||||
@Bind(R.id.ok) Button mOk;
|
||||
@Bind(R.id.delete) Button mDelete;
|
||||
@Bind(R.id.ringtone) Button mRingtone;
|
||||
@Bind(R.id.vibrate) CheckBox mVibrate;
|
||||
@ -41,7 +43,7 @@ public class ExpandedAlarmViewHolder extends BaseAlarmViewHolder {
|
||||
listener.onListItemDeleted(getAlarm());
|
||||
}
|
||||
});
|
||||
mSave.setOnClickListener(new View.OnClickListener() {
|
||||
mOk.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
listener.onListItemUpdate(getAlarm(), getAdapterPosition());
|
||||
@ -74,7 +76,18 @@ public class ExpandedAlarmViewHolder extends BaseAlarmViewHolder {
|
||||
|
||||
@OnClick(R.id.ringtone)
|
||||
void showRingtonePickerDialog() {
|
||||
// TODO
|
||||
Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
|
||||
intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_ALARM)
|
||||
.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, false)
|
||||
// The ringtone to show as selected when the dialog is opened
|
||||
.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, Uri.parse(getAlarm().ringtone()))
|
||||
// Whether to show "Default" item in the list
|
||||
.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, false);
|
||||
// The ringtone that plays when default option is selected
|
||||
//.putExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI, DEFAULT_TONE);
|
||||
// TODO: This is VERY BAD. Use a Controller/Presenter instead.
|
||||
// The result will be delivered to MainActivity, and then delegated to AlarmsFragment.
|
||||
((Activity) getContext()).startActivityForResult(intent, AlarmsFragment.REQUEST_PICK_RINGTONE);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@ -23,7 +23,7 @@ public abstract class BaseTimePickerDialog extends BottomSheetDialogFragment {
|
||||
* The callback interface used to indicate the user is done filling in
|
||||
* the time (they clicked on the 'Set' button).
|
||||
*/
|
||||
interface OnTimeSetListener {
|
||||
public interface OnTimeSetListener {
|
||||
/**
|
||||
* @param viewGroup The view associated with this listener.
|
||||
* @param hourOfDay The hour that was set.
|
||||
|
||||
@ -52,6 +52,7 @@ import static com.philliphsu.clock2.util.Preconditions.checkNotNull;
|
||||
* The class would have the API for editing the alarm, so move all
|
||||
* the relevant helper methods from here to there.
|
||||
*/
|
||||
@Deprecated
|
||||
public class EditAlarmActivity extends BaseActivity implements
|
||||
EditAlarmContract.View, // TODO: Remove @Override from the methods
|
||||
AlarmUtilsHelper,
|
||||
@ -517,7 +518,7 @@ public class EditAlarmActivity extends BaseActivity implements
|
||||
// The ringtone to show as selected when the dialog is opened
|
||||
.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, mSelectedRingtoneUri)
|
||||
// Whether to show "Default" item in the list
|
||||
.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, false); // TODO: false?
|
||||
.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, false);
|
||||
// The ringtone that plays when default option is selected
|
||||
//.putExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI, DEFAULT_TONE);
|
||||
startActivityForResult(intent, REQUEST_PICK_RINGTONE);
|
||||
|
||||
@ -12,7 +12,8 @@
|
||||
- Alternatively, just keep the CardView because that takes care of the non-transparent
|
||||
- background issue for free.
|
||||
-->
|
||||
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<android.support.v7.widget.CardView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@ -128,6 +129,7 @@
|
||||
android:layout_height="48dp"
|
||||
android:hint="Add label"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:gravity="center_vertical"/>
|
||||
|
||||
<LinearLayout
|
||||
@ -169,9 +171,7 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:id="@+id/divider"
|
||||
style="@style/Divider.Horizontal"/>
|
||||
<View style="@style/Divider.Horizontal"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
@ -187,11 +187,11 @@
|
||||
android:text="@string/delete"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/save"
|
||||
android:id="@+id/ok"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/Widget.AppCompat.Button.Borderless.Colored"
|
||||
android:text="@string/save"/>
|
||||
android:text="@android:string/ok"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user