SettingsActivity.java revision 4e7ee09e878178873241846757b178535da3dd76
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.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        implements RingtonePickerDialogFragment.RingtoneSelectionListener {
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 ActionBarMenuManager mActionBarMenuManager = new ActionBarMenuManager();
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        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    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        mActionBarMenuManager.createOptionsMenu(menu, getMenuInflater());
119        return true;
120    }
121
122    @Override
123    public boolean onPrepareOptionsMenu(Menu menu) {
124        mActionBarMenuManager.prepareShowMenu(menu);
125        return true;
126    }
127
128    @Override
129    public boolean onOptionsItemSelected(MenuItem item) {
130        if (mActionBarMenuManager.handleMenuItemClick(item)) {
131            return true;
132        }
133        return super.onOptionsItemSelected(item);
134    }
135
136    /**
137     * Called by the RingtonePickerDialogFragment class after the dialog is finished.
138     */
139    @Override
140    public void onRingtoneSelected(Uri ringtoneUri, String fragmentTag) {
141        final PrefsFragment fragment =
142                (PrefsFragment) getFragmentManager().findFragmentById(R.id.main);
143        final Preference preference = fragment.findPreference(KEY_TIMER_RINGTONE);
144        DataModel.getDataModel().setTimerRingtoneUri(ringtoneUri);
145        preference.setSummary(DataModel.getDataModel().getTimerRingtoneTitle());
146    }
147
148    public static class PrefsFragment extends PreferenceFragment
149            implements Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener {
150
151        @Override
152        public void onCreatePreferences(Bundle bundle, String rootKey) {
153            addPreferencesFromResource(R.xml.settings);
154            loadTimeZoneList();
155        }
156
157        @Override
158        public void onActivityCreated(Bundle savedInstanceState) {
159            super.onActivityCreated(savedInstanceState);
160
161            // By default, do not recreate the DeskClock activity
162            getActivity().setResult(RESULT_CANCELED);
163        }
164
165        @Override
166        public void onResume() {
167            super.onResume();
168
169            refresh();
170        }
171
172        @Override
173        public void onDisplayPreferenceDialog(Preference preference) {
174            final String key = preference.getKey();
175            switch (key) {
176                case KEY_ALARM_SNOOZE:
177                    showDialog(SnoozeLengthDialogFragment.newInstance(preference));
178                    break;
179                case KEY_ALARM_CRESCENDO:
180                case KEY_TIMER_CRESCENDO:
181                    showDialog(CrescendoLengthDialogFragment.newInstance(preference));
182                    break;
183                default:
184                    super.onDisplayPreferenceDialog(preference);
185            }
186        }
187
188        @Override
189        public boolean onPreferenceChange(Preference pref, Object newValue) {
190            final int idx;
191            switch (pref.getKey()) {
192                case KEY_AUTO_SILENCE:
193                    final ListPreference autoSilencePref = (ListPreference) pref;
194                    String delay = (String) newValue;
195                    updateAutoSnoozeSummary(autoSilencePref, delay);
196                    break;
197                case KEY_CLOCK_STYLE:
198                    final ListPreference clockStylePref = (ListPreference) pref;
199                    idx = clockStylePref.findIndexOfValue((String) newValue);
200                    clockStylePref.setSummary(clockStylePref.getEntries()[idx]);
201                    break;
202                case KEY_HOME_TZ:
203                    final ListPreference homeTimezonePref = (ListPreference) pref;
204                    idx = homeTimezonePref.findIndexOfValue((String) newValue);
205                    homeTimezonePref.setSummary(homeTimezonePref.getEntries()[idx]);
206                    break;
207                case KEY_AUTO_HOME_CLOCK:
208                    final boolean autoHomeClockEnabled = ((SwitchPreference) pref).isChecked();
209                    final Preference homeTimeZonePref = findPreference(KEY_HOME_TZ);
210                    homeTimeZonePref.setEnabled(!autoHomeClockEnabled);
211                    break;
212                case KEY_VOLUME_BUTTONS:
213                    final ListPreference volumeButtonsPref = (ListPreference) pref;
214                    final int index = volumeButtonsPref.findIndexOfValue((String) newValue);
215                    volumeButtonsPref.setSummary(volumeButtonsPref.getEntries()[index]);
216                    break;
217                case KEY_WEEK_START:
218                    final ListPreference weekStartPref =
219                            (ListPreference) findPreference(KEY_WEEK_START);
220                    idx = weekStartPref.findIndexOfValue((String) newValue);
221                    weekStartPref.setSummary(weekStartPref.getEntries()[idx]);
222                    break;
223                case KEY_TIMER_VIBRATE:
224                    final SwitchPreference timerVibratePref =
225                            (SwitchPreference) findPreference(KEY_TIMER_VIBRATE);
226                    DataModel.getDataModel().setTimerVibrate(timerVibratePref.isChecked());
227                    break;
228            }
229            // Set result so DeskClock knows to refresh itself
230            getActivity().setResult(RESULT_OK);
231            return true;
232        }
233
234        @Override
235        public boolean onPreferenceClick(Preference pref) {
236            final Activity activity = getActivity();
237            if (activity == null) {
238                return false;
239            }
240
241            switch (pref.getKey()) {
242                case KEY_DATE_TIME:
243                    final Intent dialogIntent = new Intent(Settings.ACTION_DATE_SETTINGS);
244                    dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
245                    startActivity(dialogIntent);
246                    return true;
247                case KEY_TIMER_RINGTONE:
248                    final String dialogTitle = getString(R.string.timer_ringtone_title);
249                    final String defaultTitle = getString(R.string.default_timer_ringtone_title);
250                    final Uri currentUri = DataModel.getDataModel().getTimerRingtoneUri();
251                    final Uri defaultUri = DataModel.getDataModel().getDefaultTimerRingtoneUri();
252                    final DialogFragment newFragment = RingtonePickerDialogFragment
253                            .newInstance(dialogTitle, defaultTitle, defaultUri, currentUri, null);
254                    showDialog(newFragment);
255                default:
256                    return false;
257            }
258        }
259
260        /**
261         * Reconstruct the timezone list.
262         */
263        private void loadTimeZoneList() {
264            final CharSequence[][] timezones = getAllTimezones();
265            final ListPreference homeTimezonePref = (ListPreference) findPreference(KEY_HOME_TZ);
266            homeTimezonePref.setEntryValues(timezones[0]);
267            homeTimezonePref.setEntries(timezones[1]);
268            homeTimezonePref.setSummary(homeTimezonePref.getEntry());
269            homeTimezonePref.setOnPreferenceChangeListener(this);
270        }
271
272        /**
273         * Returns an array of ids/time zones. This returns a double indexed array
274         * of ids and time zones for Calendar. It is an inefficient method and
275         * shouldn't be called often, but can be used for one time generation of
276         * this list.
277         *
278         * @return double array of tz ids and tz names
279         */
280        public CharSequence[][] getAllTimezones() {
281            final Resources res = getResources();
282            final String[] ids = res.getStringArray(R.array.timezone_values);
283            final String[] labels = res.getStringArray(R.array.timezone_labels);
284
285            int minLength = ids.length;
286            if (ids.length != labels.length) {
287                minLength = Math.min(minLength, labels.length);
288                LogUtils.e("Timezone ids and labels have different length!");
289            }
290
291            final long currentTimeMillis = System.currentTimeMillis();
292            final List<TimeZoneRow> timezones = new ArrayList<>(minLength);
293            for (int i = 0; i < minLength; i++) {
294                timezones.add(new TimeZoneRow(ids[i], labels[i], currentTimeMillis));
295            }
296            Collections.sort(timezones);
297
298            final CharSequence[][] timeZones = new CharSequence[2][timezones.size()];
299            int i = 0;
300            for (TimeZoneRow row : timezones) {
301                timeZones[0][i] = row.mId;
302                timeZones[1][i++] = row.mDisplayName;
303            }
304            return timeZones;
305        }
306
307        private void refresh() {
308            final ListPreference autoSilencePref =
309                    (ListPreference) findPreference(KEY_AUTO_SILENCE);
310            String delay = autoSilencePref.getValue();
311            updateAutoSnoozeSummary(autoSilencePref, delay);
312            autoSilencePref.setOnPreferenceChangeListener(this);
313
314            final ListPreference clockStylePref = (ListPreference) findPreference(KEY_CLOCK_STYLE);
315            clockStylePref.setSummary(clockStylePref.getEntry());
316            clockStylePref.setOnPreferenceChangeListener(this);
317
318            final Preference autoHomeClockPref = findPreference(KEY_AUTO_HOME_CLOCK);
319            final boolean autoHomeClockEnabled = ((SwitchPreference) autoHomeClockPref).isChecked();
320            autoHomeClockPref.setOnPreferenceChangeListener(this);
321
322            final ListPreference homeTimezonePref = (ListPreference) findPreference(KEY_HOME_TZ);
323            homeTimezonePref.setEnabled(autoHomeClockEnabled);
324            homeTimezonePref.setSummary(homeTimezonePref.getEntry());
325            homeTimezonePref.setOnPreferenceChangeListener(this);
326
327            final ListPreference volumeButtonsPref =
328                    (ListPreference) findPreference(KEY_VOLUME_BUTTONS);
329            volumeButtonsPref.setSummary(volumeButtonsPref.getEntry());
330            volumeButtonsPref.setOnPreferenceChangeListener(this);
331
332            ((SnoozeLengthDialogPreference) findPreference(KEY_ALARM_SNOOZE)).updateSummary();
333            ((CrescendoLengthDialogPreference) findPreference(KEY_ALARM_CRESCENDO)).updateSummary();
334            ((CrescendoLengthDialogPreference) findPreference(KEY_TIMER_CRESCENDO)).updateSummary();
335
336            final Preference dateAndTimeSetting = findPreference(KEY_DATE_TIME);
337            dateAndTimeSetting.setOnPreferenceClickListener(this);
338
339            final ListPreference weekStartPref = (ListPreference) findPreference(KEY_WEEK_START);
340            // Set the default value programmatically
341            final String value = weekStartPref.getValue();
342            final int idx = weekStartPref.findIndexOfValue(
343                    value == null ? String.valueOf(Utils.DEFAULT_WEEK_START) : value);
344            weekStartPref.setValueIndex(idx);
345            weekStartPref.setSummary(weekStartPref.getEntries()[idx]);
346            weekStartPref.setOnPreferenceChangeListener(this);
347
348            final Preference timerRingtonePref = findPreference(KEY_TIMER_RINGTONE);
349            timerRingtonePref.setOnPreferenceClickListener(this);
350            timerRingtonePref.setSummary(DataModel.getDataModel().getTimerRingtoneTitle());
351        }
352
353        private void updateAutoSnoozeSummary(ListPreference listPref, String delay) {
354            int i = Integer.parseInt(delay);
355            if (i == -1) {
356                listPref.setSummary(R.string.auto_silence_never);
357            } else {
358                listPref.setSummary(Utils.getNumberFormattedQuantityString(getActivity(),
359                        R.plurals.auto_silence_summary, i));
360            }
361        }
362
363        private void showDialog(DialogFragment fragment) {
364            fragment.setTargetFragment(this, 0);
365            fragment.show(getFragmentManager(), PREFERENCE_DIALOG_FRAGMENT_TAG);
366        }
367
368        private static class TimeZoneRow implements Comparable<TimeZoneRow> {
369
370            private static final boolean SHOW_DAYLIGHT_SAVINGS_INDICATOR = false;
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(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                if (useDaylightTime && SHOW_DAYLIGHT_SAVINGS_INDICATOR) {
407                    name.append(" \u2600"); // Sun symbol
408                }
409                return name.toString();
410            }
411        }
412    }
413}