1/*
2 * Copyright (C) 2009 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.Context;
21import android.content.Intent;
22import android.content.res.Resources;
23import android.media.AudioManager;
24import android.os.Bundle;
25import android.preference.ListPreference;
26import android.preference.Preference;
27import android.preference.PreferenceFragment;
28import android.preference.RingtonePreference;
29import android.preference.SwitchPreference;
30import android.provider.Settings;
31import android.text.format.DateUtils;
32import android.view.Menu;
33import android.view.MenuItem;
34
35import com.android.deskclock.BaseActivity;
36import com.android.deskclock.LogUtils;
37import com.android.deskclock.R;
38import com.android.deskclock.Utils;
39import com.android.deskclock.actionbarmenu.ActionBarMenuManager;
40import com.android.deskclock.actionbarmenu.MenuItemControllerFactory;
41import com.android.deskclock.actionbarmenu.NavUpMenuItemController;
42import com.android.deskclock.data.DataModel;
43
44import java.util.ArrayList;
45import java.util.Collections;
46import java.util.List;
47import java.util.Locale;
48import java.util.TimeZone;
49
50/**
51 * Settings for the Alarm Clock.
52 */
53public final class SettingsActivity extends BaseActivity {
54
55    public static final String KEY_ALARM_SNOOZE = "snooze_duration";
56    public static final String KEY_ALARM_VOLUME = "volume_setting";
57    public static final String KEY_ALARM_CRESCENDO = "alarm_crescendo_duration";
58    public static final String KEY_TIMER_CRESCENDO = "timer_crescendo_duration";
59    public static final String KEY_TIMER_RINGTONE = "timer_ringtone";
60    public static final String KEY_AUTO_SILENCE = "auto_silence";
61    public static final String KEY_CLOCK_STYLE = "clock_style";
62    public static final String KEY_HOME_TZ = "home_time_zone";
63    public static final String KEY_AUTO_HOME_CLOCK = "automatic_home_clock";
64    public static final String KEY_DATE_TIME = "date_time";
65    public static final String KEY_VOLUME_BUTTONS = "volume_button_setting";
66    public static final String KEY_WEEK_START = "week_start";
67
68    public static final String TIMEZONE_LOCALE = "tz_locale";
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    private final ActionBarMenuManager mActionBarMenuManager = new ActionBarMenuManager(this);
75
76    @Override
77    protected void onCreate(Bundle savedInstanceState) {
78        super.onCreate(savedInstanceState);
79        setVolumeControlStream(AudioManager.STREAM_ALARM);
80        setContentView(R.layout.settings);
81        mActionBarMenuManager.addMenuItemController(new NavUpMenuItemController(this))
82            .addMenuItemController(MenuItemControllerFactory.getInstance()
83                    .buildMenuItemControllers(this));
84    }
85
86    @Override
87    public boolean onCreateOptionsMenu(Menu menu) {
88        mActionBarMenuManager.createOptionsMenu(menu, getMenuInflater());
89        return true;
90    }
91
92    @Override
93    public boolean onPrepareOptionsMenu(Menu menu) {
94        mActionBarMenuManager.prepareShowMenu(menu);
95        return true;
96    }
97
98    @Override
99    public boolean onOptionsItemSelected(MenuItem item) {
100        if (mActionBarMenuManager.handleMenuItemClick(item)) {
101            return true;
102        }
103        return super.onOptionsItemSelected(item);
104    }
105
106    public static class PrefsFragment extends PreferenceFragment
107            implements Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener {
108        @Override
109        public void onCreate(Bundle savedInstanceState) {
110            super.onCreate(savedInstanceState);
111
112            addPreferencesFromResource(R.xml.settings);
113            loadTimeZoneList();
114        }
115
116        @Override
117        public void onActivityCreated(Bundle savedInstanceState) {
118            super.onActivityCreated(savedInstanceState);
119
120            // By default, do not recreate the DeskClock activity
121            getActivity().setResult(RESULT_CANCELED);
122
123        }
124
125        @Override
126        public void onResume() {
127            super.onResume();
128
129            refresh();
130        }
131
132        @Override
133        public boolean onPreferenceChange(Preference pref, Object newValue) {
134            final int idx;
135            switch (pref.getKey()) {
136                case KEY_AUTO_SILENCE:
137                    final ListPreference autoSilencePref = (ListPreference) pref;
138                    String delay = (String) newValue;
139                    updateAutoSnoozeSummary(autoSilencePref, delay);
140                    break;
141                case KEY_CLOCK_STYLE:
142                    final ListPreference clockStylePref = (ListPreference) pref;
143                    idx = clockStylePref.findIndexOfValue((String) newValue);
144                    clockStylePref.setSummary(clockStylePref.getEntries()[idx]);
145                    break;
146                case KEY_HOME_TZ:
147                    final ListPreference homeTimezonePref = (ListPreference) pref;
148                    idx = homeTimezonePref.findIndexOfValue((String) newValue);
149                    homeTimezonePref.setSummary(homeTimezonePref.getEntries()[idx]);
150                    break;
151                case KEY_AUTO_HOME_CLOCK:
152                    final boolean autoHomeClockEnabled = ((SwitchPreference) pref).isChecked();
153                    final Preference homeTimeZonePref = findPreference(KEY_HOME_TZ);
154                    homeTimeZonePref.setEnabled(!autoHomeClockEnabled);
155                    break;
156                case KEY_VOLUME_BUTTONS:
157                    final ListPreference volumeButtonsPref = (ListPreference) pref;
158                    final int index = volumeButtonsPref.findIndexOfValue((String) newValue);
159                    volumeButtonsPref.setSummary(volumeButtonsPref.getEntries()[index]);
160                    break;
161                case KEY_WEEK_START:
162                    final ListPreference weekStartPref = (ListPreference)
163                            findPreference(KEY_WEEK_START);
164                    idx = weekStartPref.findIndexOfValue((String) newValue);
165                    weekStartPref.setSummary(weekStartPref.getEntries()[idx]);
166                    break;
167                case KEY_TIMER_RINGTONE:
168                    final RingtonePreference timerRingtonePref = (RingtonePreference)
169                            findPreference(KEY_TIMER_RINGTONE);
170                    timerRingtonePref.setSummary(DataModel.getDataModel().getTimerRingtoneTitle());
171                    break;
172            }
173            // Set result so DeskClock knows to refresh itself
174            getActivity().setResult(RESULT_OK);
175            return true;
176        }
177
178        @Override
179        public boolean onPreferenceClick(Preference pref) {
180            final Activity activity = getActivity();
181            if (activity == null) {
182                return false;
183            }
184
185            switch (pref.getKey()) {
186                case KEY_DATE_TIME:
187                    final Intent dialogIntent = new Intent(Settings.ACTION_DATE_SETTINGS);
188                    dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
189                    startActivity(dialogIntent);
190                    return true;
191                case KEY_ALARM_VOLUME:
192                    final AudioManager audioManager =
193                            (AudioManager) activity.getSystemService(Context.AUDIO_SERVICE);
194                    audioManager.adjustStreamVolume(AudioManager.STREAM_ALARM,
195                            AudioManager.ADJUST_SAME, AudioManager.FLAG_SHOW_UI);
196                    return true;
197                default:
198                    return false;
199            }
200        }
201
202        /**
203         * Reconstruct the timezone list.
204         */
205        private void loadTimeZoneList() {
206            final CharSequence[][] timezones = getAllTimezones();
207            final ListPreference homeTimezonePref = (ListPreference) findPreference(KEY_HOME_TZ);
208            homeTimezonePref.setEntryValues(timezones[0]);
209            homeTimezonePref.setEntries(timezones[1]);
210            homeTimezonePref.setSummary(homeTimezonePref.getEntry());
211            homeTimezonePref.setOnPreferenceChangeListener(this);
212        }
213
214        /**
215         * Returns an array of ids/time zones. This returns a double indexed array
216         * of ids and time zones for Calendar. It is an inefficient method and
217         * shouldn't be called often, but can be used for one time generation of
218         * this list.
219         *
220         * @return double array of tz ids and tz names
221         */
222        public CharSequence[][] getAllTimezones() {
223            final Resources res = getResources();
224            final String[] ids = res.getStringArray(R.array.timezone_values);
225            final String[] labels = res.getStringArray(R.array.timezone_labels);
226
227            int minLength = ids.length;
228            if (ids.length != labels.length) {
229                minLength = Math.min(minLength, labels.length);
230                LogUtils.e("Timezone ids and labels have different length!");
231            }
232
233            final long currentTimeMillis = System.currentTimeMillis();
234            final List<TimeZoneRow> timezones = new ArrayList<>(minLength);
235            for (int i = 0; i < minLength; i++) {
236                timezones.add(new TimeZoneRow(ids[i], labels[i], currentTimeMillis));
237            }
238            Collections.sort(timezones);
239
240            final CharSequence[][] timeZones = new CharSequence[2][timezones.size()];
241            int i = 0;
242            for (TimeZoneRow row : timezones) {
243                timeZones[0][i] = row.mId;
244                timeZones[1][i++] = row.mDisplayName;
245            }
246            return timeZones;
247        }
248
249        private void refresh() {
250            final ListPreference autoSilencePref =
251                    (ListPreference) findPreference(KEY_AUTO_SILENCE);
252            String delay = autoSilencePref.getValue();
253            updateAutoSnoozeSummary(autoSilencePref, delay);
254            autoSilencePref.setOnPreferenceChangeListener(this);
255
256            final ListPreference clockStylePref = (ListPreference) findPreference(KEY_CLOCK_STYLE);
257            clockStylePref.setSummary(clockStylePref.getEntry());
258            clockStylePref.setOnPreferenceChangeListener(this);
259
260            final Preference autoHomeClockPref = findPreference(KEY_AUTO_HOME_CLOCK);
261            final boolean autoHomeClockEnabled = ((SwitchPreference) autoHomeClockPref).isChecked();
262            autoHomeClockPref.setOnPreferenceChangeListener(this);
263
264            final ListPreference homeTimezonePref = (ListPreference) findPreference(KEY_HOME_TZ);
265            homeTimezonePref.setEnabled(autoHomeClockEnabled);
266            homeTimezonePref.setSummary(homeTimezonePref.getEntry());
267            homeTimezonePref.setOnPreferenceChangeListener(this);
268
269            final ListPreference volumeButtonsPref =
270                    (ListPreference) findPreference(KEY_VOLUME_BUTTONS);
271            volumeButtonsPref.setSummary(volumeButtonsPref.getEntry());
272            volumeButtonsPref.setOnPreferenceChangeListener(this);
273
274            final Preference volumePref = findPreference(KEY_ALARM_VOLUME);
275            volumePref.setOnPreferenceClickListener(this);
276
277            final SnoozeLengthDialog snoozePref =
278                    (SnoozeLengthDialog) findPreference(KEY_ALARM_SNOOZE);
279            snoozePref.setSummary();
280
281            final CrescendoLengthDialog alarmCrescendoPref =
282                    (CrescendoLengthDialog) findPreference(KEY_ALARM_CRESCENDO);
283            alarmCrescendoPref.setSummary();
284
285            final CrescendoLengthDialog timerCrescendoPref =
286                    (CrescendoLengthDialog) findPreference(KEY_TIMER_CRESCENDO);
287            timerCrescendoPref.setSummary();
288
289            final Preference dateAndTimeSetting = findPreference(KEY_DATE_TIME);
290            dateAndTimeSetting.setOnPreferenceClickListener(this);
291
292            final ListPreference weekStartPref = (ListPreference) findPreference(KEY_WEEK_START);
293            // Set the default value programmatically
294            final String value = weekStartPref.getValue();
295            final int idx = weekStartPref.findIndexOfValue(
296                    value == null ? String.valueOf(Utils.DEFAULT_WEEK_START) : value);
297            weekStartPref.setValueIndex(idx);
298            weekStartPref.setSummary(weekStartPref.getEntries()[idx]);
299            weekStartPref.setOnPreferenceChangeListener(this);
300
301            final RingtonePreference timerRingtonePref =
302                    (RingtonePreference) findPreference(KEY_TIMER_RINGTONE);
303            timerRingtonePref.setSummary(DataModel.getDataModel().getTimerRingtoneTitle());
304            timerRingtonePref.setOnPreferenceChangeListener(this);
305        }
306
307        private void updateAutoSnoozeSummary(ListPreference listPref, String delay) {
308            int i = Integer.parseInt(delay);
309            if (i == -1) {
310                listPref.setSummary(R.string.auto_silence_never);
311            } else {
312                listPref.setSummary(Utils.getNumberFormattedQuantityString(getActivity(),
313                        R.plurals.auto_silence_summary, i));
314            }
315        }
316
317        private static class TimeZoneRow implements Comparable<TimeZoneRow> {
318
319            private static final boolean SHOW_DAYLIGHT_SAVINGS_INDICATOR = false;
320
321            public final String mId;
322            public final String mDisplayName;
323            public final int mOffset;
324
325            public TimeZoneRow(String id, String name, long currentTimeMillis) {
326                final TimeZone tz = TimeZone.getTimeZone(id);
327                final boolean useDaylightTime = tz.useDaylightTime();
328                mId = id;
329                mOffset = tz.getOffset(currentTimeMillis);
330                mDisplayName = buildGmtDisplayName(name, useDaylightTime);
331            }
332
333            @Override
334            public int compareTo(TimeZoneRow another) {
335                return mOffset - another.mOffset;
336            }
337
338            public String buildGmtDisplayName(String displayName, boolean useDaylightTime) {
339                final int p = Math.abs(mOffset);
340                final StringBuilder name = new StringBuilder("(GMT");
341                name.append(mOffset < 0 ? '-' : '+');
342
343                name.append(p / DateUtils.HOUR_IN_MILLIS);
344                name.append(':');
345
346                int min = p / 60000;
347                min %= 60;
348
349                if (min < 10) {
350                    name.append('0');
351                }
352                name.append(min);
353                name.append(") ");
354                name.append(displayName);
355                if (useDaylightTime && SHOW_DAYLIGHT_SAVINGS_INDICATOR) {
356                    name.append(" \u2600"); // Sun symbol
357                }
358                return name.toString();
359            }
360        }
361    }
362}
363