SettingsActivity.java revision 272cba265101a33e7cd0586fc1f2a7f5d70666d9
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.Intent;
23import android.content.res.Resources;
24import android.net.Uri;
25import android.os.Bundle;
26import android.provider.Settings;
27import android.support.v14.preference.PreferenceFragment;
28import android.support.v14.preference.SwitchPreference;
29import android.support.v7.preference.ListPreference;
30import android.support.v7.preference.Preference;
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.OptionsMenuManager;
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        implements RingtonePickerDialogFragment.OnRingtoneSelectedListener {
57
58    public static final String KEY_ALARM_SNOOZE = "snooze_duration";
59    public static final String KEY_ALARM_CRESCENDO = "alarm_crescendo_duration";
60    public static final String KEY_TIMER_CRESCENDO = "timer_crescendo_duration";
61    public static final String KEY_TIMER_RINGTONE = "timer_ringtone";
62    public static final String KEY_TIMER_VIBRATE = "timer_vibrate";
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 DEFAULT_VOLUME_BEHAVIOR = "0";
72    public static final String VOLUME_BEHAVIOR_SNOOZE = "1";
73    public static final String VOLUME_BEHAVIOR_DISMISS = "2";
74
75    public static final String PREFS_FRAGMENT_TAG = "prefs_fragment";
76    public static final String PREFERENCE_DIALOG_FRAGMENT_TAG = "preference_dialog";
77
78    private final OptionsMenuManager mOptionsMenuManager = new OptionsMenuManager();
79
80    /** The controller that shows the drop shadow when content is not scrolled to the top. */
81    private DropShadowController mDropShadowController;
82
83    @Override
84    protected void onCreate(Bundle savedInstanceState) {
85        super.onCreate(savedInstanceState);
86        setContentView(R.layout.settings);
87        mOptionsMenuManager.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    protected void onResume() {
102        super.onResume();
103
104        final View dropShadow = findViewById(R.id.drop_shadow);
105        final PrefsFragment fragment =
106                (PrefsFragment) getFragmentManager().findFragmentById(R.id.main);
107        mDropShadowController = new DropShadowController(dropShadow, fragment.getListView());
108    }
109
110    @Override
111    protected void onPause() {
112        mDropShadowController.stop();
113        super.onPause();
114    }
115
116    @Override
117    public boolean onCreateOptionsMenu(Menu menu) {
118        mOptionsMenuManager.onCreateOptionsMenu(menu);
119        return true;
120    }
121
122    @Override
123    public boolean onPrepareOptionsMenu(Menu menu) {
124        mOptionsMenuManager.onPrepareOptionsMenu(menu);
125        return true;
126    }
127
128    @Override
129    public boolean onOptionsItemSelected(MenuItem item) {
130        if (mOptionsMenuManager.onOptionsItemSelected(item)) {
131            return true;
132        }
133        return super.onOptionsItemSelected(item);
134    }
135
136    @Override
137    public void onRingtoneSelected(String tag, Uri ringtoneUri) {
138        DataModel.getDataModel().setTimerRingtoneUri(ringtoneUri);
139    }
140
141    public static class PrefsFragment extends PreferenceFragment
142            implements Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener {
143
144        @Override
145        public void onCreatePreferences(Bundle bundle, String rootKey) {
146            getPreferenceManager().setStorageDeviceProtected();
147            addPreferencesFromResource(R.xml.settings);
148            loadTimeZoneList();
149        }
150
151        @Override
152        public void onActivityCreated(Bundle savedInstanceState) {
153            super.onActivityCreated(savedInstanceState);
154
155            // By default, do not recreate the DeskClock activity
156            getActivity().setResult(RESULT_CANCELED);
157        }
158
159        @Override
160        public void onResume() {
161            super.onResume();
162
163            refresh();
164        }
165
166        @Override
167        public void onDisplayPreferenceDialog(Preference preference) {
168            final String key = preference.getKey();
169            switch (key) {
170                case KEY_ALARM_SNOOZE:
171                    showDialog(SnoozeLengthDialogFragment.newInstance(preference));
172                    break;
173                case KEY_ALARM_CRESCENDO:
174                case KEY_TIMER_CRESCENDO:
175                    showDialog(CrescendoLengthDialogFragment.newInstance(preference));
176                    break;
177                default:
178                    super.onDisplayPreferenceDialog(preference);
179            }
180        }
181
182        @Override
183        public boolean onPreferenceChange(Preference pref, Object newValue) {
184            final int idx;
185            switch (pref.getKey()) {
186                case KEY_AUTO_SILENCE:
187                    final ListPreference autoSilencePref = (ListPreference) pref;
188                    String delay = (String) newValue;
189                    updateAutoSnoozeSummary(autoSilencePref, delay);
190                    break;
191                case KEY_CLOCK_STYLE:
192                    final ListPreference clockStylePref = (ListPreference) pref;
193                    idx = clockStylePref.findIndexOfValue((String) newValue);
194                    clockStylePref.setSummary(clockStylePref.getEntries()[idx]);
195                    break;
196                case KEY_HOME_TZ:
197                    final ListPreference homeTimezonePref = (ListPreference) pref;
198                    idx = homeTimezonePref.findIndexOfValue((String) newValue);
199                    homeTimezonePref.setSummary(homeTimezonePref.getEntries()[idx]);
200                    break;
201                case KEY_AUTO_HOME_CLOCK:
202                    final boolean autoHomeClockEnabled = ((SwitchPreference) pref).isChecked();
203                    final Preference homeTimeZonePref = findPreference(KEY_HOME_TZ);
204                    homeTimeZonePref.setEnabled(!autoHomeClockEnabled);
205                    break;
206                case KEY_VOLUME_BUTTONS:
207                    final ListPreference volumeButtonsPref = (ListPreference) pref;
208                    final int index = volumeButtonsPref.findIndexOfValue((String) newValue);
209                    volumeButtonsPref.setSummary(volumeButtonsPref.getEntries()[index]);
210                    break;
211                case KEY_WEEK_START:
212                    final ListPreference weekStartPref =
213                            (ListPreference) findPreference(KEY_WEEK_START);
214                    idx = weekStartPref.findIndexOfValue((String) newValue);
215                    weekStartPref.setSummary(weekStartPref.getEntries()[idx]);
216                    break;
217                case KEY_TIMER_VIBRATE:
218                    final SwitchPreference timerVibratePref =
219                            (SwitchPreference) findPreference(KEY_TIMER_VIBRATE);
220                    DataModel.getDataModel().setTimerVibrate(timerVibratePref.isChecked());
221                    break;
222                case KEY_TIMER_RINGTONE:
223                    final Preference timerRingtonePref = findPreference(KEY_TIMER_RINGTONE);
224                    timerRingtonePref.setSummary(DataModel.getDataModel().getTimerRingtoneTitle());
225                    break;
226            }
227            // Set result so DeskClock knows to refresh itself
228            getActivity().setResult(RESULT_OK);
229            return true;
230        }
231
232        @Override
233        public boolean onPreferenceClick(Preference pref) {
234            final Activity activity = getActivity();
235            if (activity == null) {
236                return false;
237            }
238
239            switch (pref.getKey()) {
240                case KEY_DATE_TIME:
241                    final Intent dialogIntent = new Intent(Settings.ACTION_DATE_SETTINGS);
242                    dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
243                    startActivity(dialogIntent);
244                    return true;
245                case KEY_TIMER_RINGTONE:
246                    new RingtonePickerDialogFragment.Builder()
247                            .setTitle(R.string.timer_ringtone_title)
248                            .setDefaultRingtoneTitle(R.string.default_timer_ringtone_title)
249                            .setDefaultRingtoneUri(
250                                    DataModel.getDataModel().getDefaultTimerRingtoneUri())
251                            .setExistingRingtoneUri(DataModel.getDataModel().getTimerRingtoneUri())
252                            .show(getChildFragmentManager(), PREFERENCE_DIALOG_FRAGMENT_TAG);
253                default:
254                    return false;
255            }
256        }
257
258        /**
259         * Reconstruct the timezone list.
260         */
261        private void loadTimeZoneList() {
262            final CharSequence[][] timezones = getAllTimezones();
263            final ListPreference homeTimezonePref = (ListPreference) findPreference(KEY_HOME_TZ);
264            homeTimezonePref.setEntryValues(timezones[0]);
265            homeTimezonePref.setEntries(timezones[1]);
266            homeTimezonePref.setSummary(homeTimezonePref.getEntry());
267            homeTimezonePref.setOnPreferenceChangeListener(this);
268        }
269
270        /**
271         * Returns an array of ids/time zones. This returns a double indexed array
272         * of ids and time zones for Calendar. It is an inefficient method and
273         * shouldn't be called often, but can be used for one time generation of
274         * this list.
275         *
276         * @return double array of tz ids and tz names
277         */
278        public CharSequence[][] getAllTimezones() {
279            final Resources res = getResources();
280            final String[] ids = res.getStringArray(R.array.timezone_values);
281            final String[] labels = res.getStringArray(R.array.timezone_labels);
282
283            int minLength = ids.length;
284            if (ids.length != labels.length) {
285                minLength = Math.min(minLength, labels.length);
286                LogUtils.e("Timezone ids and labels have different length!");
287            }
288
289            final long currentTimeMillis = System.currentTimeMillis();
290            final List<TimeZoneRow> timezones = new ArrayList<>(minLength);
291            for (int i = 0; i < minLength; i++) {
292                timezones.add(new TimeZoneRow(ids[i], labels[i], currentTimeMillis));
293            }
294            Collections.sort(timezones);
295
296            final CharSequence[][] timeZones = new CharSequence[2][timezones.size()];
297            int i = 0;
298            for (TimeZoneRow row : timezones) {
299                timeZones[0][i] = row.mId;
300                timeZones[1][i++] = row.mDisplayName;
301            }
302            return timeZones;
303        }
304
305        private void refresh() {
306            final ListPreference autoSilencePref =
307                    (ListPreference) findPreference(KEY_AUTO_SILENCE);
308            String delay = autoSilencePref.getValue();
309            updateAutoSnoozeSummary(autoSilencePref, delay);
310            autoSilencePref.setOnPreferenceChangeListener(this);
311
312            final ListPreference clockStylePref = (ListPreference) findPreference(KEY_CLOCK_STYLE);
313            clockStylePref.setSummary(clockStylePref.getEntry());
314            clockStylePref.setOnPreferenceChangeListener(this);
315
316            final Preference autoHomeClockPref = findPreference(KEY_AUTO_HOME_CLOCK);
317            final boolean autoHomeClockEnabled = ((SwitchPreference) 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(DialogFragment fragment) {
362            fragment.setTargetFragment(this, 0);
363            fragment.show(getChildFragmentManager(), PREFERENCE_DIALOG_FRAGMENT_TAG);
364        }
365
366        private static class TimeZoneRow implements Comparable<TimeZoneRow> {
367
368            private static final boolean SHOW_DAYLIGHT_SAVINGS_INDICATOR = false;
369
370            public final String mId;
371            public final String mDisplayName;
372            public final int mOffset;
373
374            public TimeZoneRow(String id, String name, long currentTimeMillis) {
375                final TimeZone tz = TimeZone.getTimeZone(id);
376                final boolean useDaylightTime = tz.useDaylightTime();
377                mId = id;
378                mOffset = tz.getOffset(currentTimeMillis);
379                mDisplayName = buildGmtDisplayName(name, useDaylightTime);
380            }
381
382            @Override
383            public int compareTo(TimeZoneRow another) {
384                return mOffset - another.mOffset;
385            }
386
387            public String buildGmtDisplayName(String displayName, boolean useDaylightTime) {
388                final int p = Math.abs(mOffset);
389                final StringBuilder name = new StringBuilder("(GMT");
390                name.append(mOffset < 0 ? '-' : '+');
391
392                name.append(p / DateUtils.HOUR_IN_MILLIS);
393                name.append(':');
394
395                int min = p / 60000;
396                min %= 60;
397
398                if (min < 10) {
399                    name.append('0');
400                }
401                name.append(min);
402                name.append(") ");
403                name.append(displayName);
404                if (useDaylightTime && SHOW_DAYLIGHT_SAVINGS_INDICATOR) {
405                    name.append(" \u2600"); // Sun symbol
406                }
407                return name.toString();
408            }
409        }
410    }
411}
412