SettingsActivity.java revision ef0635176bcfc9b64f8790c487f63ee81433d295
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.content.Context;
20import android.content.Intent;
21import android.content.res.Resources;
22import android.os.Bundle;
23import android.os.Vibrator;
24import android.provider.Settings;
25import android.support.annotation.NonNull;
26import android.support.v7.preference.ListPreference;
27import android.support.v7.preference.ListPreferenceDialogFragmentCompat;
28import android.support.v7.preference.Preference;
29import android.support.v7.preference.PreferenceDialogFragmentCompat;
30import android.support.v7.preference.PreferenceFragmentCompat;
31import android.support.v7.preference.TwoStatePreference;
32import android.text.format.DateUtils;
33import android.view.Menu;
34import android.view.MenuItem;
35import android.view.View;
36
37import com.android.deskclock.BaseActivity;
38import com.android.deskclock.DropShadowController;
39import com.android.deskclock.LogUtils;
40import com.android.deskclock.R;
41import com.android.deskclock.Utils;
42import com.android.deskclock.actionbarmenu.MenuItemControllerFactory;
43import com.android.deskclock.actionbarmenu.NavUpMenuItemController;
44import com.android.deskclock.actionbarmenu.OptionsMenuManager;
45import com.android.deskclock.data.DataModel;
46import com.android.deskclock.data.Weekdays;
47import com.android.deskclock.ringtone.RingtonePickerActivity;
48
49import java.util.ArrayList;
50import java.util.Collections;
51import java.util.List;
52import java.util.TimeZone;
53
54/**
55 * Settings for the Alarm Clock.
56 */
57public final class SettingsActivity extends BaseActivity {
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_TIMER_VIBRATE = "timer_vibrate";
64    public static final String KEY_AUTO_SILENCE = "auto_silence";
65    public static final String KEY_CLOCK_STYLE = "clock_style";
66    public static final String KEY_CLOCK_DISPLAY_SECONDS = "display_clock_seconds";
67    public static final String KEY_HOME_TZ = "home_time_zone";
68    public static final String KEY_AUTO_HOME_CLOCK = "automatic_home_clock";
69    public static final String KEY_DATE_TIME = "date_time";
70    public static final String KEY_VOLUME_BUTTONS = "volume_button_setting";
71    public static final String KEY_WEEK_START = "week_start";
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 OptionsMenuManager mOptionsMenuManager = new OptionsMenuManager();
81
82    /**
83     * The controller that shows the drop shadow when content is not scrolled to the top.
84     */
85    private DropShadowController mDropShadowController;
86
87    @Override
88    protected void onCreate(Bundle savedInstanceState) {
89        super.onCreate(savedInstanceState);
90        setContentView(R.layout.settings);
91
92        mOptionsMenuManager.addMenuItemController(new NavUpMenuItemController(this))
93                .addMenuItemController(MenuItemControllerFactory.getInstance()
94                        .buildMenuItemControllers(this));
95
96        // Create the prefs fragment in code to ensure it's created before PreferenceDialogFragment
97        if (savedInstanceState == null) {
98            getSupportFragmentManager().beginTransaction()
99                    .replace(R.id.main, new PrefsFragment(), PREFS_FRAGMENT_TAG)
100                    .disallowAddToBackStack()
101                    .commit();
102        }
103    }
104
105    @Override
106    protected void onResume() {
107        super.onResume();
108
109        final View dropShadow = findViewById(R.id.drop_shadow);
110        final PrefsFragment fragment =
111                (PrefsFragment) getSupportFragmentManager().findFragmentById(R.id.main);
112        mDropShadowController = new DropShadowController(dropShadow, fragment.getListView());
113    }
114
115    @Override
116    protected void onPause() {
117        mDropShadowController.stop();
118        super.onPause();
119    }
120
121    @Override
122    public boolean onCreateOptionsMenu(Menu menu) {
123        mOptionsMenuManager.onCreateOptionsMenu(menu);
124        return true;
125    }
126
127    @Override
128    public boolean onPrepareOptionsMenu(Menu menu) {
129        mOptionsMenuManager.onPrepareOptionsMenu(menu);
130        return true;
131    }
132
133    @Override
134    public boolean onOptionsItemSelected(MenuItem item) {
135        return mOptionsMenuManager.onOptionsItemSelected(item)
136                || super.onOptionsItemSelected(item);
137    }
138
139    public static class PrefsFragment extends PreferenceFragmentCompat implements
140            Preference.OnPreferenceChangeListener,
141            Preference.OnPreferenceClickListener {
142
143        @Override
144        public void onCreatePreferences(Bundle bundle, String rootKey) {
145            getPreferenceManager().setStorageDeviceProtected();
146            addPreferencesFromResource(R.xml.settings);
147            final Preference timerVibrate = findPreference(KEY_TIMER_VIBRATE);
148            final boolean hasVibrator = ((Vibrator) timerVibrate.getContext()
149                    .getSystemService(VIBRATOR_SERVICE)).hasVibrator();
150            timerVibrate.setVisible(hasVibrator);
151            loadTimeZoneList();
152        }
153
154        @Override
155        public void onActivityCreated(Bundle savedInstanceState) {
156            super.onActivityCreated(savedInstanceState);
157
158            // By default, do not recreate the DeskClock activity
159            getActivity().setResult(RESULT_CANCELED);
160        }
161
162        @Override
163        public void onResume() {
164            super.onResume();
165            refresh();
166        }
167
168        @Override
169        public boolean onPreferenceChange(Preference pref, Object newValue) {
170            switch (pref.getKey()) {
171                case KEY_ALARM_CRESCENDO:
172                case KEY_HOME_TZ:
173                case KEY_ALARM_SNOOZE:
174                case KEY_TIMER_CRESCENDO:
175                    final ListPreference preference = (ListPreference) pref;
176                    final int index = preference.findIndexOfValue((String) newValue);
177                    preference.setSummary(preference.getEntries()[index]);
178                    break;
179                case KEY_CLOCK_STYLE:
180                case KEY_WEEK_START:
181                case KEY_VOLUME_BUTTONS:
182                    final SimpleMenuPreference simpleMenuPreference = (SimpleMenuPreference) pref;
183                    final int i = simpleMenuPreference.findIndexOfValue((String) newValue);
184                    pref.setSummary(simpleMenuPreference.getEntries()[i]);
185                    break;
186                case KEY_CLOCK_DISPLAY_SECONDS:
187                    DataModel.getDataModel().setDisplayClockSeconds((boolean) newValue);
188                    break;
189                case KEY_AUTO_SILENCE:
190                    final String delay = (String) newValue;
191                    updateAutoSnoozeSummary((ListPreference) pref, delay);
192                    break;
193                case KEY_AUTO_HOME_CLOCK:
194                    final boolean autoHomeClockEnabled = ((TwoStatePreference) pref).isChecked();
195                    final Preference homeTimeZonePref = findPreference(KEY_HOME_TZ);
196                    homeTimeZonePref.setEnabled(!autoHomeClockEnabled);
197                    break;
198                case KEY_TIMER_VIBRATE:
199                    final TwoStatePreference timerVibratePref = (TwoStatePreference) pref;
200                    DataModel.getDataModel().setTimerVibrate(timerVibratePref.isChecked());
201                    break;
202                case KEY_TIMER_RINGTONE:
203                    pref.setSummary(DataModel.getDataModel().getTimerRingtoneTitle());
204                    break;
205            }
206            // Set result so DeskClock knows to refresh itself
207            getActivity().setResult(RESULT_OK);
208            return true;
209        }
210
211        @Override
212        public boolean onPreferenceClick(Preference pref) {
213            final Context context = getActivity();
214            if (context == null) {
215                return false;
216            }
217
218            switch (pref.getKey()) {
219                case KEY_DATE_TIME:
220                    final Intent dialogIntent = new Intent(Settings.ACTION_DATE_SETTINGS);
221                    dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
222                    startActivity(dialogIntent);
223                    return true;
224                case KEY_TIMER_RINGTONE:
225                    startActivity(RingtonePickerActivity.createTimerRingtonePickerIntent(context));
226                    return true;
227            }
228
229            return false;
230        }
231
232        @Override
233        public void onDisplayPreferenceDialog(Preference preference) {
234            // Only single-selection lists are currently supported.
235            final PreferenceDialogFragmentCompat f;
236            if (preference instanceof ListPreference) {
237                f = ListPreferenceDialogFragmentCompat.newInstance(preference.getKey());
238            } else {
239                throw new IllegalArgumentException("Unsupported DialogPreference type");
240            }
241            showDialog(f);
242        }
243
244        private void showDialog(PreferenceDialogFragmentCompat fragment) {
245            // Don't show dialog if one is already shown.
246            if (getFragmentManager().findFragmentByTag(PREFERENCE_DIALOG_FRAGMENT_TAG) != null) {
247                return;
248            }
249            // Always set the target fragment, this is required by PreferenceDialogFragment
250            // internally.
251            fragment.setTargetFragment(this, 0);
252            // Don't use getChildFragmentManager(), it causes issues on older platforms when the
253            // target fragment is being restored after an orientation change.
254            fragment.show(getFragmentManager(), PREFERENCE_DIALOG_FRAGMENT_TAG);
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 SimpleMenuPreference clockStylePref = (SimpleMenuPreference)
312                    findPreference(KEY_CLOCK_STYLE);
313            clockStylePref.setSummary(clockStylePref.getEntry());
314            clockStylePref.setOnPreferenceChangeListener(this);
315
316            final SimpleMenuPreference volumeButtonsPref = (SimpleMenuPreference)
317                    findPreference(KEY_VOLUME_BUTTONS);
318            volumeButtonsPref.setSummary(volumeButtonsPref.getEntry());
319            volumeButtonsPref.setOnPreferenceChangeListener(this);
320
321            final Preference clockSecondsPref = findPreference(KEY_CLOCK_DISPLAY_SECONDS);
322            clockSecondsPref.setOnPreferenceChangeListener(this);
323
324            final Preference autoHomeClockPref = findPreference(KEY_AUTO_HOME_CLOCK);
325            final boolean autoHomeClockEnabled =
326                    ((TwoStatePreference) autoHomeClockPref).isChecked();
327            autoHomeClockPref.setOnPreferenceChangeListener(this);
328
329            final ListPreference homeTimezonePref = (ListPreference) findPreference(KEY_HOME_TZ);
330            homeTimezonePref.setEnabled(autoHomeClockEnabled);
331            refreshListPreference(homeTimezonePref);
332
333            refreshListPreference((ListPreference) findPreference(KEY_ALARM_CRESCENDO));
334            refreshListPreference((ListPreference) findPreference(KEY_TIMER_CRESCENDO));
335            refreshListPreference((ListPreference) findPreference(KEY_ALARM_SNOOZE));
336
337            final Preference dateAndTimeSetting = findPreference(KEY_DATE_TIME);
338            dateAndTimeSetting.setOnPreferenceClickListener(this);
339
340            final SimpleMenuPreference weekStartPref = (SimpleMenuPreference)
341                    findPreference(KEY_WEEK_START);
342            // Set the default value programmatically
343            final Weekdays.Order weekdayOrder = DataModel.getDataModel().getWeekdayOrder();
344            final Integer firstDay = weekdayOrder.getCalendarDays().get(0);
345            final String value = String.valueOf(firstDay);
346            final int idx = weekStartPref.findIndexOfValue(value);
347            weekStartPref.setValueIndex(idx);
348            weekStartPref.setSummary(weekStartPref.getEntries()[idx]);
349            weekStartPref.setOnPreferenceChangeListener(this);
350
351            final Preference timerRingtonePref = findPreference(KEY_TIMER_RINGTONE);
352            timerRingtonePref.setOnPreferenceClickListener(this);
353            timerRingtonePref.setSummary(DataModel.getDataModel().getTimerRingtoneTitle());
354        }
355
356        private void refreshListPreference(ListPreference preference) {
357            preference.setSummary(preference.getEntry());
358            preference.setOnPreferenceChangeListener(this);
359        }
360
361        private void updateAutoSnoozeSummary(ListPreference listPref, String delay) {
362            int i = Integer.parseInt(delay);
363            if (i == -1) {
364                listPref.setSummary(R.string.auto_silence_never);
365            } else {
366                listPref.setSummary(Utils.getNumberFormattedQuantityString(getActivity(),
367                        R.plurals.auto_silence_summary, i));
368            }
369        }
370
371        private static class TimeZoneRow implements Comparable<TimeZoneRow> {
372
373            public final String mId;
374            public final String mDisplayName;
375            public final int mOffset;
376
377            public TimeZoneRow(String id, String name, long currentTimeMillis) {
378                final TimeZone tz = TimeZone.getTimeZone(id);
379                final boolean useDaylightTime = tz.useDaylightTime();
380                mId = id;
381                mOffset = tz.getOffset(currentTimeMillis);
382                mDisplayName = buildGmtDisplayName(name, useDaylightTime);
383            }
384
385            @Override
386            public int compareTo(@NonNull TimeZoneRow another) {
387                return mOffset - another.mOffset;
388            }
389
390            public String buildGmtDisplayName(String displayName, boolean useDaylightTime) {
391                final int p = Math.abs(mOffset);
392                final StringBuilder name = new StringBuilder("(GMT");
393                name.append(mOffset < 0 ? '-' : '+');
394
395                name.append(p / DateUtils.HOUR_IN_MILLIS);
396                name.append(':');
397
398                int min = p / 60000;
399                min %= 60;
400
401                if (min < 10) {
402                    name.append('0');
403                }
404                name.append(min);
405                name.append(") ");
406                name.append(displayName);
407                return name.toString();
408            }
409        }
410    }
411}