Create DialogFragmentController to manage showing and restoring dialogs

This commit is contained in:
Phillip Hsu
2016-09-07 02:20:11 -07:00
parent e0ddd0b702
commit 17ad81d55e
9 changed files with 219 additions and 131 deletions
@@ -16,13 +16,15 @@ import com.philliphsu.clock2.Alarm;
import com.philliphsu.clock2.AsyncAlarmsTableUpdateHandler;
import com.philliphsu.clock2.R;
import com.philliphsu.clock2.RecyclerViewFragment;
import com.philliphsu.clock2.TimePickerDialogController;
import com.philliphsu.clock2.editalarm.BaseTimePickerDialog;
import com.philliphsu.clock2.editalarm.TimePickerHelper;
import com.philliphsu.clock2.model.AlarmCursor;
import com.philliphsu.clock2.model.AlarmsListCursorLoader;
import com.philliphsu.clock2.util.AlarmController;
import com.philliphsu.clock2.util.DelayedSnackbarHandler;
import static com.philliphsu.clock2.util.FragmentTagUtils.makeTag;
public class AlarmsFragment extends RecyclerViewFragment<
Alarm,
BaseAlarmViewHolder,
@@ -32,8 +34,6 @@ public class AlarmsFragment extends RecyclerViewFragment<
BaseTimePickerDialog.OnTimeSetListener {
private static final String TAG = "AlarmsFragment";
static final String TAG_TIME_PICKER = "time_picker";
private static final String KEY_EXPANDED_POSITION = "expanded_position";
// TODO: Delete these constants. We no longer use EditAlarmActivity.
@@ -44,13 +44,15 @@ public class AlarmsFragment extends RecyclerViewFragment<
// @Deprecated
// public static final int REQUEST_CREATE_ALARM = 1;
// TODO: Delete this. We no longer use the system's ringtone picker.
public static final int REQUEST_PICK_RINGTONE = 1;
// private AlarmsCursorAdapter mAdapter;
private AsyncAlarmsTableUpdateHandler mAsyncUpdateHandler;
private AlarmController mAlarmController;
// TODO: Delete this. If I recall correctly, this was just used for delaying item animations.
private Handler mHandler = new Handler();
private View mSnackbarAnchor;
private TimePickerDialogController mTimePickerDialogController;
private int mExpandedPosition = RecyclerView.NO_POSITION;
@@ -91,6 +93,9 @@ public class AlarmsFragment extends RecyclerViewFragment<
mAlarmController = new AlarmController(getActivity(), mSnackbarAnchor);
mAsyncUpdateHandler = new AsyncAlarmsTableUpdateHandler(getActivity(),
mSnackbarAnchor, this, mAlarmController);
mTimePickerDialogController = new TimePickerDialogController(
getFragmentManager(), getActivity(), this);
mTimePickerDialogController.tryRestoreCallback(makeTimePickerDialogTag());
}
@Override
@@ -120,21 +125,7 @@ public class AlarmsFragment extends RecyclerViewFragment<
@Override
public void onFabClick() {
// 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 = TimePickerHelper.newDialog(getActivity(), this, 0, 0);
// DISREGARD THE LINT WARNING ABOUT DIALOG BEING NULL.
dialog.show(getFragmentManager(), TAG_TIME_PICKER);
mTimePickerDialogController.show(0, 0, makeTimePickerDialogTag());
}
@Override
@@ -304,6 +295,10 @@ public class AlarmsFragment extends RecyclerViewFragment<
}
}
private static String makeTimePickerDialogTag() {
return makeTag(AlarmsFragment.class, R.id.fab);
}
/////////////////////////////////////////////////////////////////////////////////////
// TODO: We won't need these anymore, since we won't handle the db
// update in onActivityResult() anymore.
@@ -2,6 +2,7 @@ package com.philliphsu.clock2.alarms;
import android.content.res.ColorStateList;
import android.graphics.drawable.Drawable;
import android.support.annotation.IdRes;
import android.support.annotation.LayoutRes;
import android.support.annotation.NonNull;
import android.support.v4.app.FragmentManager;
@@ -9,7 +10,6 @@ import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.SwitchCompat;
import android.text.format.DateFormat;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
@@ -22,13 +22,13 @@ import com.philliphsu.clock2.Alarm;
import com.philliphsu.clock2.BaseViewHolder;
import com.philliphsu.clock2.OnListItemInteractionListener;
import com.philliphsu.clock2.R;
import com.philliphsu.clock2.TimePickerDialogController;
import com.philliphsu.clock2.aospdatetimepicker.Utils;
import com.philliphsu.clock2.editalarm.BaseTimePickerDialog;
import com.philliphsu.clock2.editalarm.BaseTimePickerDialog.OnTimeSetListener;
import com.philliphsu.clock2.editalarm.TimePickerHelper;
import com.philliphsu.clock2.editalarm.TimeTextUtils;
import com.philliphsu.clock2.util.AlarmController;
import com.philliphsu.clock2.util.AlarmUtils;
import com.philliphsu.clock2.util.FragmentTagUtils;
import java.util.Date;
@@ -49,12 +49,14 @@ public abstract class BaseAlarmViewHolder extends BaseViewHolder<Alarm> {
private final AlarmController mAlarmController;
private final AddLabelDialogController mAddLabelDialogController;
private final TimePickerDialogController mTimePickerDialogController;
// TODO: Should we use VectorDrawable type?
private final Drawable mDismissNowDrawable;
private final Drawable mCancelSnoozeDrawable;
// Exposed for use by subclasses (obviously in this package.
// TODO: THis is still here for ExpandedVH's RingtonePickerDialog. If we ever write a
// Controller for it, finally delete this.
final FragmentManager mFragmentManager;
@Bind(R.id.time) TextView mTime;
@@ -76,30 +78,48 @@ public abstract class BaseAlarmViewHolder extends BaseViewHolder<Alarm> {
// or simply pass in an instance of FragmentManager to the ctor.
AppCompatActivity act = (AppCompatActivity) getContext();
mFragmentManager = act.getSupportFragmentManager();
mAddLabelDialogController = new AddLabelDialogController(
mFragmentManager,
new AddLabelDialog.OnLabelSetListener() {
@Override
public void onLabelSet(String label) {
final Alarm oldAlarm = getAlarm();
Alarm newAlarm = oldAlarm.toBuilder()
.label(label)
.build();
oldAlarm.copyMutableFieldsTo(newAlarm);
persistUpdatedAlarm(newAlarm, false);
}
});
mAddLabelDialogController = new AddLabelDialogController(mFragmentManager,
// TODO: Why can't we implement the interface and pass `this` instead?
new AddLabelDialog.OnLabelSetListener() {
@Override
public void onLabelSet(String label) {
final Alarm oldAlarm = getAlarm();
Alarm newAlarm = oldAlarm.toBuilder()
.label(label)
.build();
oldAlarm.copyMutableFieldsTo(newAlarm);
persistUpdatedAlarm(newAlarm, false);
}
}
);
mTimePickerDialogController = new TimePickerDialogController(mFragmentManager, getContext(),
// TODO: Why can't we implement the interface and pass `this` instead?
new OnTimeSetListener() {
@Override
public void onTimeSet(ViewGroup viewGroup, int hourOfDay, int minute) {
final Alarm oldAlarm = getAlarm();
// I don't think we need this; scheduling a new alarm that is considered
// equal to a previous alarm will overwrite the previous alarm.
// mAlarmController.cancelAlarm(oldAlarm, false);
Alarm newAlarm = oldAlarm.toBuilder()
.hour(hourOfDay)
.minutes(minute)
.build();
oldAlarm.copyMutableFieldsTo(newAlarm);
// -------------------------------------------
// TOneverDO: precede copyMutableFieldsTo()
newAlarm.setEnabled(true); // Always enabled, esp. if oldAlarm is not enabled
// ----------------------------------------------
persistUpdatedAlarm(newAlarm, true);
}
}
);
// Are we recreating this because of a rotation?
// If so, try finding any dialog that was last shown in our backstack,
// and restore the callback.
BaseTimePickerDialog picker = (BaseTimePickerDialog)
mFragmentManager.findFragmentByTag(AlarmsFragment.TAG_TIME_PICKER);
if (picker != null) {
Log.i(TAG, "Restoring time picker callback");
picker.setOnTimeSetListener(newOnTimeSetListener());
}
mAddLabelDialogController.tryRestoreCallback();
mAddLabelDialogController.tryRestoreCallback(makeTag(R.id.label));
mTimePickerDialogController.tryRestoreCallback(makeTag(R.id.time));
}
@Override
@@ -204,14 +224,12 @@ public abstract class BaseAlarmViewHolder extends BaseViewHolder<Alarm> {
@OnClick(R.id.time)
void openTimePicker() {
Alarm alarm = getAlarm();
BaseTimePickerDialog dialog = TimePickerHelper.newDialog(getContext(),
newOnTimeSetListener(), alarm.hour(), alarm.minutes());
dialog.show(mFragmentManager, AlarmsFragment.TAG_TIME_PICKER);
mTimePickerDialogController.show(alarm.hour(), alarm.minutes(), makeTag(R.id.time));
}
@OnClick(R.id.label)
void openLabelEditor() {
mAddLabelDialogController.show(mLabel.getText());
mAddLabelDialogController.show(mLabel.getText(), makeTag(R.id.label));
}
/**
@@ -282,32 +300,7 @@ public abstract class BaseAlarmViewHolder extends BaseViewHolder<Alarm> {
bindLabel(visible, label);
}
private OnTimeSetListener newOnTimeSetListener() {
// Create a new listener per request. This is primarily used for
// setting the dialog callback again after a rotation.
//
// If we saved a reference to a listener, it would be tied to
// its ViewHolder instance. ViewHolders are reused, so we
// could accidentally leak this reference to other Alarm items
// in the list.
return new OnTimeSetListener() {
@Override
public void onTimeSet(ViewGroup viewGroup, int hourOfDay, int minute) {
final Alarm oldAlarm = getAlarm();
// I don't think we need this; scheduling a new alarm that is considered
// equal to a previous alarm will overwrite the previous alarm.
// mAlarmController.cancelAlarm(oldAlarm, false);
Alarm newAlarm = oldAlarm.toBuilder()
.hour(hourOfDay)
.minutes(minute)
.build();
oldAlarm.copyMutableFieldsTo(newAlarm);
// -------------------------------------------
// TOneverDO: precede copyMutableFieldsTo()
newAlarm.setEnabled(true); // Always enabled, esp. if oldAlarm is not enabled
// ----------------------------------------------
persistUpdatedAlarm(newAlarm, true);
}
};
private String makeTag(@IdRes int viewId) {
return FragmentTagUtils.makeTag(BaseAlarmViewHolder.class, viewId, getItemId());
}
}