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