summaryrefslogtreecommitdiff
path: root/src/com/android/calendar/GeneralPreferences.kt
blob: 9406eff6bac2ed7bfb6a1358d73370a2110ba503 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.calendar

import android.app.Activity
import android.app.FragmentManager
import android.app.backup.BackupManager
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
import android.media.Ringtone
import android.media.RingtoneManager
import android.net.Uri
import android.os.Bundle
import android.os.Vibrator
import android.preference.CheckBoxPreference
import android.preference.ListPreference
import android.preference.Preference
import android.preference.Preference.OnPreferenceChangeListener
import android.preference.Preference.OnPreferenceClickListener
import android.preference.PreferenceCategory
import android.preference.PreferenceFragment
import android.preference.PreferenceManager
import android.preference.PreferenceScreen
import android.provider.CalendarContract
import android.provider.CalendarContract.CalendarCache
import android.text.TextUtils
import android.text.format.Time
import com.android.calendar.alerts.AlertReceiver
import com.android.timezonepicker.TimeZoneInfo
import com.android.timezonepicker.TimeZonePickerDialog
import com.android.timezonepicker.TimeZonePickerDialog.OnTimeZoneSetListener
import com.android.timezonepicker.TimeZonePickerUtils

class GeneralPreferences : PreferenceFragment(), OnSharedPreferenceChangeListener,
        OnPreferenceChangeListener, OnTimeZoneSetListener {
    var mAlert: CheckBoxPreference? = null
    var mVibrate: CheckBoxPreference? = null
    var mPopup: CheckBoxPreference? = null
    var mUseHomeTZ: CheckBoxPreference? = null
    var mHideDeclined: CheckBoxPreference? = null
    var mHomeTZ: Preference? = null
    var mTzPickerUtils: TimeZonePickerUtils? = null
    var mWeekStart: ListPreference? = null
    var mDefaultReminder: ListPreference? = null
    private var mTimeZoneId: String? = null

    @Override
    override fun onCreate(icicle: Bundle?) {
        super.onCreate(icicle)
        val activity: Activity = getActivity()

        // Make sure to always use the same preferences file regardless of the package name
        // we're running under
        val preferenceManager: PreferenceManager = getPreferenceManager()
        val sharedPreferences: SharedPreferences? = getSharedPreferences(activity)
        preferenceManager.setSharedPreferencesName(SHARED_PREFS_NAME)

        // Load the preferences from an XML resource
        addPreferencesFromResource(R.xml.general_preferences)
        val preferenceScreen: PreferenceScreen = getPreferenceScreen()
        mAlert = preferenceScreen.findPreference(KEY_ALERTS) as CheckBoxPreference
        mVibrate = preferenceScreen.findPreference(KEY_ALERTS_VIBRATE) as CheckBoxPreference
        val vibrator: Vibrator = activity.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
        if (vibrator == null || !vibrator.hasVibrator()) {
            val mAlertGroup: PreferenceCategory = preferenceScreen
                    .findPreference(KEY_ALERTS_CATEGORY) as PreferenceCategory
            mAlertGroup.removePreference(mVibrate)
        }
        mPopup = preferenceScreen.findPreference(KEY_ALERTS_POPUP) as CheckBoxPreference
        mUseHomeTZ = preferenceScreen.findPreference(KEY_HOME_TZ_ENABLED) as CheckBoxPreference
        mHideDeclined = preferenceScreen.findPreference(KEY_HIDE_DECLINED) as CheckBoxPreference
        mWeekStart = preferenceScreen.findPreference(KEY_WEEK_START_DAY) as ListPreference
        mDefaultReminder = preferenceScreen.findPreference(KEY_DEFAULT_REMINDER) as ListPreference
        mHomeTZ = preferenceScreen.findPreference(KEY_HOME_TZ)
        mWeekStart?.setSummary(mWeekStart?.getEntry())
        mDefaultReminder?.setSummary(mDefaultReminder?.getEntry())

        // This triggers an asynchronous call to the provider to refresh the data in shared pref
        mTimeZoneId = Utils.getTimeZone(activity, null)
        val prefs: SharedPreferences = CalendarUtils.getSharedPreferences(activity,
                Utils.SHARED_PREFS_NAME)

        // Utils.getTimeZone will return the currentTimeZone instead of the one
        // in the shared_pref if home time zone is disabled. So if home tz is
        // off, we will explicitly read it.
        if (!prefs.getBoolean(KEY_HOME_TZ_ENABLED, false)) {
            mTimeZoneId = prefs.getString(KEY_HOME_TZ, Time.getCurrentTimezone())
        }

        mHomeTZ?.setOnPreferenceClickListener(object : Preference.OnPreferenceClickListener {
            @Override
            override fun onPreferenceClick(preference: Preference?): Boolean {
                showTimezoneDialog()
                return true
            }
        })

        if (mTzPickerUtils == null) {
            mTzPickerUtils = TimeZonePickerUtils(getActivity())
        }
        val timezoneName: CharSequence? = mTzPickerUtils?.getGmtDisplayName(getActivity(),
                mTimeZoneId, System.currentTimeMillis(), false)
        mHomeTZ?.setSummary(timezoneName ?: mTimeZoneId)
        val tzpd: TimeZonePickerDialog = activity.getFragmentManager()
                .findFragmentByTag(FRAG_TAG_TIME_ZONE_PICKER) as TimeZonePickerDialog
        if (tzpd != null) {
            tzpd.setOnTimeZoneSetListener(this)
        }
        migrateOldPreferences(sharedPreferences)
        updateChildPreferences()
    }

    private fun showTimezoneDialog() {
        val activity: Activity = getActivity() ?: return
        val b = Bundle()
        b.putLong(TimeZonePickerDialog.BUNDLE_START_TIME_MILLIS, System.currentTimeMillis())
        b.putString(TimeZonePickerDialog.BUNDLE_TIME_ZONE, Utils.getTimeZone(activity, null))
        val fm: FragmentManager = getActivity().getFragmentManager()
        var tzpd: TimeZonePickerDialog? = fm
                .findFragmentByTag(FRAG_TAG_TIME_ZONE_PICKER) as TimeZonePickerDialog
        if (tzpd != null) {
            tzpd.dismiss()
        }
        tzpd = TimeZonePickerDialog()
        tzpd.setArguments(b)
        tzpd.setOnTimeZoneSetListener(this)
        tzpd.show(fm, FRAG_TAG_TIME_ZONE_PICKER)
    }

    @Override
    override fun onStart() {
        super.onStart()
        getPreferenceScreen().getSharedPreferences()
                .registerOnSharedPreferenceChangeListener(this)
        setPreferenceListeners(this)
    }

    /**
     * Sets up all the preference change listeners to use the specified
     * listener.
     */
    private fun setPreferenceListeners(listener: OnPreferenceChangeListener?) {
        mUseHomeTZ?.setOnPreferenceChangeListener(listener)
        mHomeTZ?.setOnPreferenceChangeListener(listener)
        mWeekStart?.setOnPreferenceChangeListener(listener)
        mDefaultReminder?.setOnPreferenceChangeListener(listener)
        mHideDeclined?.setOnPreferenceChangeListener(listener)
        mVibrate?.setOnPreferenceChangeListener(listener)
    }

    @Override
    override fun onStop() {
        getPreferenceScreen().getSharedPreferences()
                .unregisterOnSharedPreferenceChangeListener(this)
        setPreferenceListeners(null)
        super.onStop()
    }

    @Override
    override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String) {
        val a: Activity = getActivity()
        if (key.equals(KEY_ALERTS)) {
            updateChildPreferences()
            if (a != null) {
                val intent = Intent()
                intent.setClass(a, AlertReceiver::class.java)
                if (mAlert?.isChecked() ?: false) {
                    intent.setAction(AlertReceiver.ACTION_DISMISS_OLD_REMINDERS)
                } else {
                    intent.setAction(AlertReceiver.EVENT_REMINDER_APP_ACTION)
                }
                a.sendBroadcast(intent)
            }
        }
        if (a != null) {
            BackupManager.dataChanged(a.getPackageName())
        }
    }

    /**
     * Handles time zone preference changes
     */
    @Override
    override fun onPreferenceChange(preference: Preference, newValue: Any): Boolean {
        val tz: String?
        val activity: Activity = getActivity()
        if (preference === mUseHomeTZ) {
            tz = if (newValue != null) {
                mTimeZoneId
            } else {
                CalendarCache.TIMEZONE_TYPE_AUTO
            }
            Utils.setTimeZone(activity, tz)
            return true
        } else if (preference === mHideDeclined) {
            mHideDeclined?.setChecked(newValue as Boolean)
            val intent = Intent(Utils.getWidgetScheduledUpdateAction(activity))
            intent.setDataAndType(CalendarContract.CONTENT_URI, Utils.APPWIDGET_DATA_TYPE)
            activity.sendBroadcast(intent)
            return true
        } else if (preference === mWeekStart) {
            mWeekStart?.setValue(newValue as String)
            mWeekStart?.setSummary(mWeekStart?.getEntry())
        } else if (preference === mDefaultReminder) {
            mDefaultReminder?.setValue(newValue as String)
            mDefaultReminder?.setSummary(mDefaultReminder?.getEntry())
        } else if (preference === mVibrate) {
            mVibrate?.setChecked(newValue as Boolean)
            return true
        } else {
            return true
        }
        return false
    }

    fun getRingtoneTitleFromUri(context: Context?, uri: String?): String? {
        if (TextUtils.isEmpty(uri)) {
            return null
        }
        val ring: Ringtone = RingtoneManager.getRingtone(getActivity(), Uri.parse(uri))
        return if (ring != null) {
            ring.getTitle(context)
        } else null
    }

    /**
     * If necessary, upgrades previous versions of preferences to the current
     * set of keys and values.
     * @param prefs the preferences to upgrade
     */
    private fun migrateOldPreferences(prefs: SharedPreferences?) {
        // If needed, migrate vibration setting from a previous version
        mVibrate?.setChecked(Utils.getDefaultVibrate(getActivity(), prefs))

        // If needed, migrate the old alerts type settin
        if (prefs?.contains(KEY_ALERTS) == false && prefs.contains(KEY_ALERTS_TYPE) == true) {
            val type: String? = prefs.getString(KEY_ALERTS_TYPE, ALERT_TYPE_STATUS_BAR)
            if (type.equals(ALERT_TYPE_OFF)) {
                mAlert?.setChecked(false)
                mPopup?.setChecked(false)
                mPopup?.setEnabled(false)
            } else if (type.equals(ALERT_TYPE_STATUS_BAR)) {
                mAlert?.setChecked(true)
                mPopup?.setChecked(false)
                mPopup?.setEnabled(true)
            } else if (type.equals(ALERT_TYPE_ALERTS)) {
                mAlert?.setChecked(true)
                mPopup?.setChecked(true)
                mPopup?.setEnabled(true)
            }
            // clear out the old setting
            prefs.edit().remove(KEY_ALERTS_TYPE).commit()
        }
    }

    /**
     * Keeps the dependent settings in sync with the parent preference, so for
     * example, when notifications are turned off, we disable the preferences
     * for configuring the exact notification behavior.
     */
    private fun updateChildPreferences() {
        if (mAlert?.isChecked() ?: false) {
            mVibrate?.setEnabled(true)
            mPopup?.setEnabled(true)
        } else {
            mVibrate?.setEnabled(false)
            mPopup?.setEnabled(false)
        }
    }

    @Override
    override fun onPreferenceTreeClick(
        preferenceScreen: PreferenceScreen?,
        preference: Preference
    ): Boolean {
        val key: String = preference.getKey()
        return super.onPreferenceTreeClick(preferenceScreen, preference)
    }

    @Override
    override fun onTimeZoneSet(tzi: TimeZoneInfo) {
        if (mTzPickerUtils == null) {
            mTzPickerUtils = TimeZonePickerUtils(getActivity())
        }
        val timezoneName: CharSequence? = mTzPickerUtils?.getGmtDisplayName(
                getActivity(), tzi.mTzId, System.currentTimeMillis(), false)
        mHomeTZ?.setSummary(timezoneName)
        Utils.setTimeZone(getActivity(), tzi.mTzId)
    }

    companion object {
        // The name of the shared preferences file. This name must be maintained for historical
        // reasons, as it's what PreferenceManager assigned the first time the file was created.
        const val SHARED_PREFS_NAME = "com.android.calendar_preferences"
        const val SHARED_PREFS_NAME_NO_BACKUP = "com.android.calendar_preferences_no_backup"
        private const val FRAG_TAG_TIME_ZONE_PICKER = "TimeZonePicker"

        // Preference keys
        const val KEY_HIDE_DECLINED = "preferences_hide_declined"
        const val KEY_WEEK_START_DAY = "preferences_week_start_day"
        const val KEY_SHOW_WEEK_NUM = "preferences_show_week_num"
        const val KEY_DAYS_PER_WEEK = "preferences_days_per_week"
        const val KEY_SKIP_SETUP = "preferences_skip_setup"
        const val KEY_CLEAR_SEARCH_HISTORY = "preferences_clear_search_history"
        const val KEY_ALERTS_CATEGORY = "preferences_alerts_category"
        const val KEY_ALERTS = "preferences_alerts"
        const val KEY_ALERTS_VIBRATE = "preferences_alerts_vibrate"
        const val KEY_ALERTS_RINGTONE = "preferences_alerts_ringtone"
        const val KEY_ALERTS_POPUP = "preferences_alerts_popup"
        const val KEY_SHOW_CONTROLS = "preferences_show_controls"
        const val KEY_DEFAULT_REMINDER = "preferences_default_reminder"
        const val NO_REMINDER = -1
        const val NO_REMINDER_STRING = "-1"
        const val REMINDER_DEFAULT_TIME = 10 // in minutes
        const val KEY_DEFAULT_CELL_HEIGHT = "preferences_default_cell_height"
        const val KEY_VERSION = "preferences_version"

        /** Key to SharePreference for default view (CalendarController.ViewType)  */
        const val KEY_START_VIEW = "preferred_startView"

        /**
         * Key to SharePreference for default detail view (CalendarController.ViewType)
         * Typically used by widget
         */
        const val KEY_DETAILED_VIEW = "preferred_detailedView"
        const val KEY_DEFAULT_CALENDAR = "preference_defaultCalendar"

        // These must be in sync with the array preferences_week_start_day_values
        const val WEEK_START_DEFAULT = "-1"
        const val WEEK_START_SATURDAY = "7"
        const val WEEK_START_SUNDAY = "1"
        const val WEEK_START_MONDAY = "2"

        // These keys are kept to enable migrating users from previous versions
        private const val KEY_ALERTS_TYPE = "preferences_alerts_type"
        private const val ALERT_TYPE_ALERTS = "0"
        private const val ALERT_TYPE_STATUS_BAR = "1"
        private const val ALERT_TYPE_OFF = "2"
        const val KEY_HOME_TZ_ENABLED = "preferences_home_tz_enabled"
        const val KEY_HOME_TZ = "preferences_home_tz"

        // Default preference values
        const val DEFAULT_START_VIEW: Int = CalendarController.ViewType.WEEK
        const val DEFAULT_DETAILED_VIEW: Int = CalendarController.ViewType.DAY
        const val DEFAULT_SHOW_WEEK_NUM = false

        // This should match the XML file.
        const val DEFAULT_RINGTONE = "content://settings/system/notification_sound"

        /** Return a properly configured SharedPreferences instance  */
        @JvmStatic
        fun getSharedPreferences(context: Context?): SharedPreferences? {
            return context?.getSharedPreferences(SHARED_PREFS_NAME, Context.MODE_PRIVATE)
        }

        /** Set the default shared preferences in the proper context  */
        @JvmStatic
        fun setDefaultValues(context: Context?) {
            PreferenceManager.setDefaultValues(context, SHARED_PREFS_NAME, Context.MODE_PRIVATE,
                    R.xml.general_preferences, false)
        }
    }
}