SettingsActivity.java revision 270e0e33f661dc7a726b0c64d4227a54d3068946
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.deskclock.settings;
18
19import android.app.Activity;
20import android.content.Intent;
21import android.content.res.Resources;
22import android.net.Uri;
23import android.os.Bundle;
24import android.provider.Settings;
25import android.support.annotation.NonNull;
26import android.support.v14.preference.PreferenceDialogFragment;
27import android.support.v14.preference.PreferenceFragment;
28import android.support.v7.preference.ListPreference;
29import android.support.v7.preference.Preference;
30import android.support.v7.preference.TwoStatePreference;
31import android.text.format.DateUtils;
32import android.view.Menu;
33import android.view.MenuItem;
34import android.view.View;
35
36import com.android.deskclock.BaseActivity;
37import com.android.deskclock.DropShadowController;
38import com.android.deskclock.LogUtils;
39import com.android.deskclock.R;
40import com.android.deskclock.RingtonePickerDialogFragment;
41import com.android.deskclock.Utils;
42import com.android.deskclock.actionbarmenu.ActionBarMenuManager;
43import com.android.deskclock.actionbarmenu.MenuItemControllerFactory;
44import com.android.deskclock.actionbarmenu.NavUpMenuItemController;
45import com.android.deskclock.data.DataModel;
46
47import java.util.ArrayList;
48import java.util.Collections;
49import java.util.List;
50import java.util.TimeZone;
51
52/**
53 * Settings for the Alarm Clock.
54 */
55public final class SettingsActivity extends BaseActivity {
56
57    public static final String KEY_ALARM_SNOOZE = "snooze_duration";
58    public static final String KEY_ALARM_CRESCENDO = "alarm_crescendo_duration";
59    public static final String KEY_TIMER_CRESCENDO = "timer_crescendo_duration";
60    public static final String KEY_TIMER_RINGTONE = "timer_ringtone";
61    public static final String KEY_TIMER_VIBRATE = "timer_vibrate";
62    public static final String KEY_AUTO_SILENCE = "auto_silence";
63    public static final String KEY_CLOCK_STYLE = "clock_style";
64    public static final String KEY_HOME_TZ = "home_time_zone";
65    public static final String KEY_AUTO_HOME_CLOCK = "automatic_home_clock";
66    public static final String KEY_DATE_TIME = "date_time";
67    public static final String KEY_VOLUME_BUTTONS = "volume_button_setting";
68    public static final String KEY_WEEK_START = "week_start";
69
70    public static final String DEFAULT_VOLUME_BEHAVIOR = "0";
71    public static final String VOLUME_BEHAVIOR_SNOOZE = "1";
72    public static final String VOLUME_BEHAVIOR_DISMISS = "2";
73
74    public static final String PREFS_FRAGMENT_TAG = "prefs_fragment";
75    public static final String PREFERENCE_DIALOG_FRAGMENT_TAG = "preference_dialog";
76
77    private final ActionBarMenuManager mActionBarMenuManager = new ActionBarMenuManager();
78
79    /**
80     * The controller that shows the drop shadow when content is not scrolled to the top.
81     */
82    private DropShadowController mDropShadowController;
83
84    @Override
85    protected void onCreate(Bundle savedInstanceState) {
86        super.onCreate(savedInstanceState);
87        setContentView(R.layout.settings);
88        mActionBarMenuManager.addMenuItemController(new NavUpMenuItemController(this))
89                .addMenuItemController(MenuItemControllerFactory.getInstance()
90                        .buildMenuItemControllers(this));
91
92        // Create the prefs fragment in code to ensure it's created before PreferenceDialogFragment
93        if (savedInstanceState == null) {
94            getFragmentManager().beginTransaction()
95                    .replace(R.id.main, new PrefsFragment(), PREFS_FRAGMENT_TAG)
96                    .disallowAddToBackStack()
97                    .commit();
98        }
99    }
100
101    @Override
102    protected void onResume() {
103        super.onResume();
104
105        final View dropShadow = findViewById(R.id.drop_shadow);
106        final PrefsFragment fragment =
107                (PrefsFragment) getFragmentManager().findFragmentById(R.id.main);
108        mDropShadowController = new DropShadowController(dropShadow, fragment.getListView());
109    }
110
111    @Override
112    protected void onPause() {
113        mDropShadowController.stop();
114        super.onPause();
115    }
116
117    @Override
118    public boolean onCreateOptionsMenu(Menu menu) {
119        mActionBarMenuManager.createOptionsMenu(menu, getMenuInflater());
120        return true;
121    }
122
123    @Override
124    public boolean onPrepareOptionsMenu(Menu menu) {
125        mActionBarMenuManager.prepareShowMenu(menu);
126        return true;
127    }
128
129    @Override
130    public boolean onOptionsItemSelected(MenuItem item) {
131        return mActionBarMenuManager.handleMenuItemClick(item)
132                || super.onOptionsItemSelected(item);
133    }
134
135    public static class PrefsFragment extends PreferenceFragment implements
136            RingtonePickerDialogFragment.OnRingtoneSelectedListener,
137            Preference.OnPreferenceChangeListener,
138            Preference.OnPreferenceClickListener {
139
140        @Override
141        public void onCreatePreferences(Bundle bundle, String rootKey) {
142            addPreferencesFromResource(R.xml.settings);
143            loadTimeZoneList();
144        }
145
146        @Override
147        public void onActivityCreated(Bundle savedInstanceState) {
148            super.onActivityCreated(savedInstanceState);
149
150            // By default, do not recreate the DeskClock activity
151            getActivity().setResult(RESULT_CANCELED);
152        }
153
154        @Override
155        public void onResume() {
156            super.onResume();
157            refresh();
158        }
159
160        @Override
161        public void onDisplayPreferenceDialog(Preference preference) {
162            final String key = preference.getKey();
163            switch (key) {
164                case KEY_ALARM_SNOOZE:
165                    showDialog(SnoozeLengthDialogFragment.newInstance(preference));
166                    break;
167                case KEY_ALARM_CRESCENDO:
168                case KEY_TIMER_CRESCENDO:
169                    showDialog(CrescendoLengthDialogFragment.newInstance(preference));
170                    break;
171                default:
172                    super.onDisplayPreferenceDialog(preference);
173            }
174        }
175
176        @Override
177        public boolean onPreferenceChange(Preference pref, Object newValue) {
178            final int idx;
179            switch (pref.getKey()) {
180                case KEY_AUTO_SILENCE:
181                    String delay = (String) newValue;
182                    updateAutoSnoozeSummary((ListPreference) pref, delay);
183                    break;
184                case KEY_CLOCK_STYLE:
185                    final ListPreference clockStylePref = (ListPreference) pref;
186                    idx = clockStylePref.findIndexOfValue((String) newValue);
187                    clockStylePref.setSummary(clockStylePref.getEntries()[idx]);
188                    break;
189                case KEY_HOME_TZ:
190                    final ListPreference homeTimezonePref = (ListPreference) pref;
191                    idx = homeTimezonePref.findIndexOfValue((String) newValue);
192                    homeTimezonePref.setSummary(homeTimezonePref.getEntries()[idx]);
193                    break;
194                case KEY_AUTO_HOME_CLOCK:
195                    final boolean autoHomeClockEnabled = ((TwoStatePreference) pref).isChecked();
196                    final Preference homeTimeZonePref = findPreference(KEY_HOME_TZ);
197                    homeTimeZonePref.setEnabled(!autoHomeClockEnabled);
198                    break;
199                case KEY_VOLUME_BUTTONS:
200                    final ListPreference volumeButtonsPref = (ListPreference) pref;
201                    final int index = volumeButtonsPref.findIndexOfValue((String) newValue);
202                    volumeButtonsPref.setSummary(volumeButtonsPref.getEntries()[index]);
203                    break;
204                case KEY_WEEK_START:
205                    final ListPreference weekStartPref = (ListPreference) pref;
206                    idx = weekStartPref.findIndexOfValue((String) newValue);
207                    weekStartPref.setSummary(weekStartPref.getEntries()[idx]);
208                    break;
209                case KEY_TIMER_VIBRATE:
210                    final TwoStatePreference timerVibratePref = (TwoStatePreference) pref;
211                    DataModel.getDataModel().setTimerVibrate(timerVibratePref.isChecked());
212                    break;
213                case KEY_TIMER_RINGTONE:
214                    pref.setSummary(DataModel.getDataModel().getTimerRingtoneTitle());
215                    break;
216            }
217            // Set result so DeskClock knows to refresh itself
218            getActivity().setResult(RESULT_OK);
219            return true;
220        }
221
222        @Override
223        public boolean onPreferenceClick(Preference pref) {
224            final Activity activity = getActivity();
225            if (activity == null) {
226                return false;
227            }
228
229            switch (pref.getKey()) {
230                case KEY_DATE_TIME:
231                    final Intent dialogIntent = new Intent(Settings.ACTION_DATE_SETTINGS);
232                    dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
233                    startActivity(dialogIntent);
234                    return true;
235                case KEY_TIMER_RINGTONE:
236                    new RingtonePickerDialogFragment.Builder()
237                            .setTitle(R.string.timer_ringtone_title)
238                            .setDefaultRingtoneTitle(R.string.default_timer_ringtone_title)
239                            .setDefaultRingtoneUri(
240                                    DataModel.getDataModel().getDefaultTimerRingtoneUri())
241                            .setExistingRingtoneUri(DataModel.getDataModel().getTimerRingtoneUri())
242                            .show(getChildFragmentManager(), PREFERENCE_DIALOG_FRAGMENT_TAG);
243                default:
244                    return false;
245            }
246        }
247
248        @Override
249        public void onRingtoneSelected(String tag, Uri ringtoneUri) {
250            DataModel.getDataModel().setTimerRingtoneUri(ringtoneUri);
251
252            // Manually call onPreferenceChange since PreferenceFragment doesn't listen to
253            // external changes via SharedPreferences.
254            onPreferenceChange(findPreference(KEY_TIMER_RINGTONE), ringtoneUri);
255        }
256
257        /**
258         * Reconstruct the timezone list.
259         */
260        private void loadTimeZoneList() {
261            final CharSequence[][] timezones = getAllTimezones();
262            final ListPreference homeTimezonePref = (ListPreference) findPreference(KEY_HOME_TZ);
263            homeTimezonePref.setEntryValues(timezones[0]);
264            homeTimezonePref.setEntries(timezones[1]);
265            homeTimezonePref.setSummary(homeTimezonePref.getEntry());
266            homeTimezonePref.setOnPreferenceChangeListener(this);
267        }
268
269        /**
270         * Returns an array of ids/time zones. This returns a double indexed array
271         * of ids and time zones for Calendar. It is an inefficient method and
272         * shouldn't be called often, but can be used for one time generation of
273         * this list.
274         *
275         * @return double array of tz ids and tz names
276         */
277        public CharSequence[][] getAllTimezones() {
278            final Resources res = getResources();
279            final String[] ids = res.getStringArray(R.array.timezone_values);
280            final String[] labels = res.getStringArray(R.array.timezone_labels);
281
282            int minLength = ids.length;
283            if (ids.length != labels.length) {
284                minLength = Math.min(minLength, labels.length);
285                LogUtils.e("Timezone ids and labels have different length!");
286            }
287
288            final long currentTimeMillis = System.currentTimeMillis();
289            final List<TimeZoneRow> timezones = new ArrayList<>(minLength);
290            for (int i = 0; i < minLength; i++) {
291                timezones.add(new TimeZoneRow(ids[i], labels[i], currentTimeMillis));
292            }
293            Collections.sort(timezones);
294
295            final CharSequence[][] timeZones = new CharSequence[2][timezones.size()];
296            int i = 0;
297            for (TimeZoneRow row : timezones) {
298                timeZones[0][i] = row.mId;
299                timeZones[1][i++] = row.mDisplayName;
300            }
301            return timeZones;
302        }
303
304        private void refresh() {
305            final ListPreference autoSilencePref =
306                    (ListPreference) findPreference(KEY_AUTO_SILENCE);
307            String delay = autoSilencePref.getValue();
308            updateAutoSnoozeSummary(autoSilencePref, delay);
309            autoSilencePref.setOnPreferenceChangeListener(this);
310
311            final ListPreference clockStylePref = (ListPreference) findPreference(KEY_CLOCK_STYLE);
312            clockStylePref.setSummary(clockStylePref.getEntry());
313            clockStylePref.setOnPreferenceChangeListener(this);
314
315            final Preference autoHomeClockPref = findPreference(KEY_AUTO_HOME_CLOCK);
316            final boolean autoHomeClockEnabled =
317                    ((TwoStatePreference) autoHomeClockPref).isChecked();
318            autoHomeClockPref.setOnPreferenceChangeListener(this);
319
320            final ListPreference homeTimezonePref = (ListPreference) findPreference(KEY_HOME_TZ);
321            homeTimezonePref.setEnabled(autoHomeClockEnabled);
322            homeTimezonePref.setSummary(homeTimezonePref.getEntry());
323            homeTimezonePref.setOnPreferenceChangeListener(this);
324
325            final ListPreference volumeButtonsPref =
326                    (ListPreference) findPreference(KEY_VOLUME_BUTTONS);
327            volumeButtonsPref.setSummary(volumeButtonsPref.getEntry());
328            volumeButtonsPref.setOnPreferenceChangeListener(this);
329
330            ((SnoozeLengthDialogPreference) findPreference(KEY_ALARM_SNOOZE)).updateSummary();
331            ((CrescendoLengthDialogPreference) findPreference(KEY_ALARM_CRESCENDO)).updateSummary();
332            ((CrescendoLengthDialogPreference) findPreference(KEY_TIMER_CRESCENDO)).updateSummary();
333
334            final Preference dateAndTimeSetting = findPreference(KEY_DATE_TIME);
335            dateAndTimeSetting.setOnPreferenceClickListener(this);
336
337            final ListPreference weekStartPref = (ListPreference) findPreference(KEY_WEEK_START);
338            // Set the default value programmatically
339            final String value = weekStartPref.getValue();
340            final int idx = weekStartPref.findIndexOfValue(
341                    value == null ? String.valueOf(Utils.DEFAULT_WEEK_START) : value);
342            weekStartPref.setValueIndex(idx);
343            weekStartPref.setSummary(weekStartPref.getEntries()[idx]);
344            weekStartPref.setOnPreferenceChangeListener(this);
345
346            final Preference timerRingtonePref = findPreference(KEY_TIMER_RINGTONE);
347            timerRingtonePref.setOnPreferenceClickListener(this);
348            timerRingtonePref.setSummary(DataModel.getDataModel().getTimerRingtoneTitle());
349        }
350
351        private void updateAutoSnoozeSummary(ListPreference listPref, String delay) {
352            int i = Integer.parseInt(delay);
353            if (i == -1) {
354                listPref.setSummary(R.string.auto_silence_never);
355            } else {
356                listPref.setSummary(Utils.getNumberFormattedQuantityString(getActivity(),
357                        R.plurals.auto_silence_summary, i));
358            }
359        }
360
361        private void showDialog(PreferenceDialogFragment fragment) {
362            // Always set the target fragment, this is required by PreferenceDialogFragment
363            // internally.
364            fragment.setTargetFragment(this, 0);
365            // Don't use getChildFragmentManager(), it causes issues on older platforms when the
366            // target fragment is being restored after an orientation change.
367            fragment.show(getFragmentManager(), PREFERENCE_DIALOG_FRAGMENT_TAG);
368        }
369
370        private static class TimeZoneRow implements Comparable<TimeZoneRow> {
371
372            public final String mId;
373            public final String mDisplayName;
374            public final int mOffset;
375
376            public TimeZoneRow(String id, String name, long currentTimeMillis) {
377                final TimeZone tz = TimeZone.getTimeZone(id);
378                final boolean useDaylightTime = tz.useDaylightTime();
379                mId = id;
380                mOffset = tz.getOffset(currentTimeMillis);
381                mDisplayName = buildGmtDisplayName(name, useDaylightTime);
382            }
383
384            @Override
385            public int compareTo(@NonNull TimeZoneRow another) {
386                return mOffset - another.mOffset;
387            }
388
389            public String buildGmtDisplayName(String displayName, boolean useDaylightTime) {
390                final int p = Math.abs(mOffset);
391                final StringBuilder name = new StringBuilder("(GMT");
392                name.append(mOffset < 0 ? '-' : '+');
393
394                name.append(p / DateUtils.HOUR_IN_MILLIS);
395                name.append(':');
396
397                int min = p / 60000;
398                min %= 60;
399
400                if (min < 10) {
401                    name.append('0');
402                }
403                name.append(min);
404                name.append(") ");
405                name.append(displayName);
406                return name.toString();
407            }
408        }
409    }
410}
411