1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.settings.notification;
18
19import android.app.AlertDialog;
20import android.app.AutomaticZenRule;
21import android.app.Dialog;
22import android.app.DialogFragment;
23import android.app.FragmentManager;
24import android.app.TimePickerDialog;
25import android.content.Context;
26import android.content.DialogInterface;
27import android.content.DialogInterface.OnDismissListener;
28import android.os.Bundle;
29import android.provider.Settings;
30import android.service.notification.ZenModeConfig;
31import android.service.notification.ZenModeConfig.ScheduleInfo;
32import android.support.v14.preference.SwitchPreference;
33import android.support.v7.preference.Preference;
34import android.support.v7.preference.Preference.OnPreferenceClickListener;
35import android.support.v7.preference.PreferenceScreen;
36import android.text.format.DateFormat;
37import android.util.Log;
38import android.widget.TimePicker;
39
40import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
41import com.android.settings.R;
42import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
43
44import java.text.SimpleDateFormat;
45import java.util.Arrays;
46import java.util.Calendar;
47
48public class ZenModeScheduleRuleSettings extends ZenModeRuleSettingsBase {
49    private static final String KEY_DAYS = "days";
50    private static final String KEY_START_TIME = "start_time";
51    private static final String KEY_END_TIME = "end_time";
52    private static final String KEY_EXIT_AT_ALARM = "exit_at_alarm";
53
54    public static final String ACTION = Settings.ACTION_ZEN_MODE_SCHEDULE_RULE_SETTINGS;
55
56    // per-instance to ensure we're always using the current locale
57    private final SimpleDateFormat mDayFormat = new SimpleDateFormat("EEE");
58
59    private Preference mDays;
60    private TimePickerPreference mStart;
61    private TimePickerPreference mEnd;
62    private SwitchPreference mExitAtAlarm;
63
64    private ScheduleInfo mSchedule;
65
66    @Override
67    protected boolean setRule(AutomaticZenRule rule) {
68        mSchedule = rule != null ? ZenModeConfig.tryParseScheduleConditionId(rule.getConditionId())
69                : null;
70        return mSchedule != null;
71    }
72
73    @Override
74    protected String getZenModeDependency() {
75        return mDays.getKey();
76    }
77
78    @Override
79    protected int getEnabledToastText() {
80        return R.string.zen_schedule_rule_enabled_toast;
81    }
82
83    @Override
84    protected void onCreateInternal() {
85        addPreferencesFromResource(R.xml.zen_mode_schedule_rule_settings);
86        final PreferenceScreen root = getPreferenceScreen();
87
88        mDays = root.findPreference(KEY_DAYS);
89        mDays.setOnPreferenceClickListener(new OnPreferenceClickListener() {
90            @Override
91            public boolean onPreferenceClick(Preference preference) {
92                showDaysDialog();
93                return true;
94            }
95        });
96
97        final FragmentManager mgr = getFragmentManager();
98
99        mStart = new TimePickerPreference(getPrefContext(), mgr);
100        mStart.setKey(KEY_START_TIME);
101        mStart.setTitle(R.string.zen_mode_start_time);
102        mStart.setCallback(new TimePickerPreference.Callback() {
103            @Override
104            public boolean onSetTime(final int hour, final int minute) {
105                if (mDisableListeners) return true;
106                if (!ZenModeConfig.isValidHour(hour)) return false;
107                if (!ZenModeConfig.isValidMinute(minute)) return false;
108                if (hour == mSchedule.startHour && minute == mSchedule.startMinute) {
109                    return true;
110                }
111                if (DEBUG) Log.d(TAG, "onPrefChange start h=" + hour + " m=" + minute);
112                mSchedule.startHour = hour;
113                mSchedule.startMinute = minute;
114                updateRule(ZenModeConfig.toScheduleConditionId(mSchedule));
115                return true;
116            }
117        });
118        root.addPreference(mStart);
119        mStart.setDependency(mDays.getKey());
120
121        mEnd = new TimePickerPreference(getPrefContext(), mgr);
122        mEnd.setKey(KEY_END_TIME);
123        mEnd.setTitle(R.string.zen_mode_end_time);
124        mEnd.setCallback(new TimePickerPreference.Callback() {
125            @Override
126            public boolean onSetTime(final int hour, final int minute) {
127                if (mDisableListeners) return true;
128                if (!ZenModeConfig.isValidHour(hour)) return false;
129                if (!ZenModeConfig.isValidMinute(minute)) return false;
130                if (hour == mSchedule.endHour && minute == mSchedule.endMinute) {
131                    return true;
132                }
133                if (DEBUG) Log.d(TAG, "onPrefChange end h=" + hour + " m=" + minute);
134                mSchedule.endHour = hour;
135                mSchedule.endMinute = minute;
136                updateRule(ZenModeConfig.toScheduleConditionId(mSchedule));
137                return true;
138            }
139        });
140        root.addPreference(mEnd);
141        mEnd.setDependency(mDays.getKey());
142
143        mExitAtAlarm = (SwitchPreference) root.findPreference(KEY_EXIT_AT_ALARM);
144        mExitAtAlarm.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
145            @Override
146            public boolean onPreferenceChange(Preference preference, Object o) {
147                mSchedule.exitAtAlarm = (Boolean) o;
148                updateRule(ZenModeConfig.toScheduleConditionId(mSchedule));
149                return true;
150            }
151        });
152    }
153
154    private void updateDays() {
155        // Compute an ordered, delimited list of day names based on the persisted user config.
156        final int[] days = mSchedule.days;
157        if (days != null && days.length > 0) {
158            final StringBuilder sb = new StringBuilder();
159            final Calendar c = Calendar.getInstance();
160            int[] daysOfWeek = ZenModeScheduleDaysSelection.getDaysOfWeekForLocale(c);
161            for (int i = 0; i < daysOfWeek.length; i++) {
162                final int day = daysOfWeek[i];
163                for (int j = 0; j < days.length; j++) {
164                    if (day == days[j]) {
165                        c.set(Calendar.DAY_OF_WEEK, day);
166                        if (sb.length() > 0) {
167                            sb.append(mContext.getString(R.string.summary_divider_text));
168                        }
169                        sb.append(mDayFormat.format(c.getTime()));
170                        break;
171                    }
172                }
173            }
174            if (sb.length() > 0) {
175                mDays.setSummary(sb);
176                mDays.notifyDependencyChange(false);
177                return;
178            }
179        }
180        mDays.setSummary(R.string.zen_mode_schedule_rule_days_none);
181        mDays.notifyDependencyChange(true);
182    }
183
184    private void updateEndSummary() {
185        final int startMin = 60 * mSchedule.startHour + mSchedule.startMinute;
186        final int endMin = 60 * mSchedule.endHour + mSchedule.endMinute;
187        final boolean nextDay = startMin >= endMin;
188        final int summaryFormat = nextDay ? R.string.zen_mode_end_time_next_day_summary_format : 0;
189        mEnd.setSummaryFormat(summaryFormat);
190    }
191
192    @Override
193    protected void updateControlsInternal() {
194        updateDays();
195        mStart.setTime(mSchedule.startHour, mSchedule.startMinute);
196        mEnd.setTime(mSchedule.endHour, mSchedule.endMinute);
197        mExitAtAlarm.setChecked(mSchedule.exitAtAlarm);
198        updateEndSummary();
199    }
200
201    @Override
202    public int getMetricsCategory() {
203        return MetricsEvent.NOTIFICATION_ZEN_MODE_SCHEDULE_RULE;
204    }
205
206    private void showDaysDialog() {
207        new AlertDialog.Builder(mContext)
208                .setTitle(R.string.zen_mode_schedule_rule_days)
209                .setView(new ZenModeScheduleDaysSelection(mContext, mSchedule.days) {
210                      @Override
211                      protected void onChanged(final int[] days) {
212                          if (mDisableListeners) return;
213                          if (Arrays.equals(days, mSchedule.days)) return;
214                          if (DEBUG) Log.d(TAG, "days.onChanged days=" + Arrays.asList(days));
215                          mSchedule.days = days;
216                          updateRule(ZenModeConfig.toScheduleConditionId(mSchedule));
217                      }
218                })
219                .setOnDismissListener(new OnDismissListener() {
220                    @Override
221                    public void onDismiss(DialogInterface dialog) {
222                        updateDays();
223                    }
224                })
225                .setPositiveButton(R.string.done_button, null)
226                .show();
227    }
228
229    private static class TimePickerPreference extends Preference {
230        private final Context mContext;
231
232        private int mSummaryFormat;
233        private int mHourOfDay;
234        private int mMinute;
235        private Callback mCallback;
236
237        public TimePickerPreference(Context context, final FragmentManager mgr) {
238            super(context);
239            mContext = context;
240            setPersistent(false);
241            setOnPreferenceClickListener(new OnPreferenceClickListener(){
242                @Override
243                public boolean onPreferenceClick(Preference preference) {
244                    final TimePickerFragment frag = new TimePickerFragment();
245                    frag.pref = TimePickerPreference.this;
246                    frag.show(mgr, TimePickerPreference.class.getName());
247                    return true;
248                }
249            });
250        }
251
252        public void setCallback(Callback callback) {
253            mCallback = callback;
254        }
255
256        public void setSummaryFormat(int resId) {
257            mSummaryFormat = resId;
258            updateSummary();
259        }
260
261        public void setTime(int hourOfDay, int minute) {
262            if (mCallback != null && !mCallback.onSetTime(hourOfDay, minute)) return;
263            mHourOfDay = hourOfDay;
264            mMinute = minute;
265            updateSummary();
266        }
267
268        private void updateSummary() {
269            final Calendar c = Calendar.getInstance();
270            c.set(Calendar.HOUR_OF_DAY, mHourOfDay);
271            c.set(Calendar.MINUTE, mMinute);
272            String time = DateFormat.getTimeFormat(mContext).format(c.getTime());
273            if (mSummaryFormat != 0) {
274                time = mContext.getResources().getString(mSummaryFormat, time);
275            }
276            setSummary(time);
277        }
278
279        public static class TimePickerFragment extends InstrumentedDialogFragment implements
280                TimePickerDialog.OnTimeSetListener {
281            public TimePickerPreference pref;
282
283            @Override
284            public int getMetricsCategory() {
285                return MetricsEvent.DIALOG_ZEN_TIMEPICKER;
286            }
287
288            @Override
289            public Dialog onCreateDialog(Bundle savedInstanceState) {
290                final boolean usePref = pref != null && pref.mHourOfDay >= 0 && pref.mMinute >= 0;
291                final Calendar c = Calendar.getInstance();
292                final int hour = usePref ? pref.mHourOfDay : c.get(Calendar.HOUR_OF_DAY);
293                final int minute = usePref ? pref.mMinute : c.get(Calendar.MINUTE);
294                return new TimePickerDialog(getActivity(), this, hour, minute,
295                        DateFormat.is24HourFormat(getActivity()));
296            }
297
298            public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
299                if (pref != null) {
300                    pref.setTime(hourOfDay, minute);
301                }
302            }
303        }
304
305        public interface Callback {
306            boolean onSetTime(int hour, int minute);
307        }
308    }
309
310}
311