blob: f7e32f3262d5389d02cf14606867ec660e42c60e [file] [log] [blame]
cretin45d4cea552016-04-25 11:00:04 -07001/*
2 * Copyright (C) 2016 The CyanogenMod Project
Christian Odercff63862020-11-09 17:14:42 +01003 * Copyright (C) 2017-2018,2020 The LineageOS Project
cretin45d4cea552016-04-25 11:00:04 -07004 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
Michael Bestasc83309e2018-02-03 17:42:13 +020018package org.lineageos.setupwizard;
cretin45d4cea552016-04-25 11:00:04 -070019
20import android.app.AlarmManager;
21import android.app.DatePickerDialog;
22import android.app.Dialog;
23import android.app.DialogFragment;
24import android.app.TimePickerDialog;
25import android.content.BroadcastReceiver;
26import android.content.Context;
27import android.content.Intent;
28import android.content.IntentFilter;
cretin45d4cea552016-04-25 11:00:04 -070029import android.os.Bundle;
30import android.os.Handler;
31import android.text.format.DateFormat;
32import android.util.Log;
33import android.view.View;
34import android.widget.AdapterView;
35import android.widget.DatePicker;
cretin45d4cea552016-04-25 11:00:04 -070036import android.widget.SimpleAdapter;
37import android.widget.Spinner;
38import android.widget.TextView;
39import android.widget.TimePicker;
40
Michael W8dc3cf82020-05-11 14:44:18 +020041import com.android.settingslib.datetime.ZoneGetter;
42
Michael Bestasc83309e2018-02-03 17:42:13 +020043import org.lineageos.setupwizard.util.SetupWizardUtils;
Michael Wc3b20082017-10-09 21:13:00 +020044
cretin45d4cea552016-04-25 11:00:04 -070045import java.util.Calendar;
46import java.util.Collections;
47import java.util.Comparator;
cretin45d4cea552016-04-25 11:00:04 -070048import java.util.List;
49import java.util.Map;
50import java.util.TimeZone;
51
52public class DateTimeActivity extends BaseSetupWizardActivity implements
53 TimePickerDialog.OnTimeSetListener, DatePickerDialog.OnDateSetListener {
54
55 public static final String TAG = DateTimeActivity.class.getSimpleName();
56
57 private static final String KEY_ID = "id"; // value: String
58 private static final String KEY_DISPLAYNAME = "name"; // value: String
59 private static final String KEY_GMT = "gmt"; // value: String
60 private static final String KEY_OFFSET = "offset"; // value: int (Integer)
61 private static final String XMLTAG_TIMEZONE = "timezone";
62
63 private static final int HOURS_1 = 60 * 60000;
64
65 private TimeZone mCurrentTimeZone;
cretin45d4cea552016-04-25 11:00:04 -070066 private TextView mDateTextView;
67 private TextView mTimeTextView;
68
69
70 private final Handler mHandler = new Handler();
71
72 private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
73 @Override
74 public void onReceive(Context context, Intent intent) {
75 updateTimeAndDateDisplay();
76 }
77 };
78
79 @Override
80 protected void onCreate(Bundle savedInstanceState) {
81 super.onCreate(savedInstanceState);
82 setNextText(R.string.next);
83
84 final Spinner spinner = (Spinner) findViewById(R.id.timezone_list);
Michael W8dc3cf82020-05-11 14:44:18 +020085 final SimpleAdapter adapter = constructTimezoneAdapter(this);
cretin45d4cea552016-04-25 11:00:04 -070086 mCurrentTimeZone = TimeZone.getDefault();
Michael W2236d292018-07-15 15:09:20 +020087 View dateView = findViewById(R.id.date_item);
88 dateView.setOnClickListener((view) -> showDatePicker());
89 View timeView = findViewById(R.id.time_item);
90 timeView.setOnClickListener((view) -> showTimePicker());
cretin45d4cea552016-04-25 11:00:04 -070091 mDateTextView = (TextView)findViewById(R.id.date_text);
92 mTimeTextView = (TextView)findViewById(R.id.time_text);
93 // Pre-select current/default timezone
Michael W2236d292018-07-15 15:09:20 +020094 mHandler.post(() -> {
95 int tzIndex = getTimeZoneIndex(adapter, mCurrentTimeZone);
96 spinner.setAdapter(adapter);
97 if (tzIndex != -1) {
98 spinner.setSelection(tzIndex);
cretin45d4cea552016-04-25 11:00:04 -070099 }
Michael W2236d292018-07-15 15:09:20 +0200100 spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
101 @Override
102 public void onItemSelected(AdapterView<?> adapterView, View view, int position,
103 long id) {
104 final Map<?, ?> map = (Map<?, ?>) adapterView.getItemAtPosition(position);
105 final String tzId = (String) map.get(KEY_ID);
106 if (mCurrentTimeZone != null && !mCurrentTimeZone.getID().equals(tzId)) {
107 // Update the system timezone value
108 final AlarmManager alarm =
109 (AlarmManager) getSystemService(Context.ALARM_SERVICE);
110 alarm.setTimeZone(tzId);
111 mCurrentTimeZone = TimeZone.getTimeZone(tzId);
112 }
113
114 }
115
116 @Override
117 public void onNothingSelected(AdapterView<?> adapterView) {
118 }
119 });
cretin45d4cea552016-04-25 11:00:04 -0700120 });
121 // Pre-select current/default date if epoch
Michael W2236d292018-07-15 15:09:20 +0200122 mHandler.post(() -> {
123 final Calendar calendar = Calendar.getInstance();
124 final boolean isEpoch = calendar.get(Calendar.YEAR) == 1970;
125 if (isEpoch) {
126 // If epoch, set date to build date
127 long timestamp = SetupWizardUtils.getBuildDateTimestamp();
128 if (timestamp > 0) {
129 calendar.setTimeInMillis(timestamp * 1000);
130 setDate(DateTimeActivity.this, calendar.get(Calendar.YEAR),
131 calendar.get(Calendar.MONTH), calendar.get(Calendar.DAY_OF_MONTH));
132 } else {
133 // no build date available, use a sane default
134 setDate(DateTimeActivity.this, 2017, Calendar.JANUARY, 1);
cretin45d4cea552016-04-25 11:00:04 -0700135 }
136 }
137 });
138 }
139
140 @Override
141 public void onResume() {
142 super.onResume();
143 // Register for time ticks and other reasons for time change
144 IntentFilter filter = new IntentFilter();
145 filter.addAction(Intent.ACTION_TIME_TICK);
146 filter.addAction(Intent.ACTION_TIME_CHANGED);
147 filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
148 registerReceiver(mIntentReceiver, filter, null, null);
149
150 updateTimeAndDateDisplay();
151 }
152
153 @Override
154 public void onPause() {
155 super.onPause();
156 unregisterReceiver(mIntentReceiver);
157 }
158
159 @Override
cretin45d4cea552016-04-25 11:00:04 -0700160 protected int getLayoutResId() {
161 return R.layout.setup_datetime_page;
162 }
163
164 @Override
165 protected int getTitleResId() {
166 return R.string.setup_datetime;
167 }
168
169 @Override
170 protected int getIconResId() {
171 return R.drawable.ic_datetime;
172 }
173
174 @Override
175 public void onDateSet(DatePicker view, int year, int month, int day) {
176 setDate(this, year, month, day);
177 updateTimeAndDateDisplay();
178 }
179
180 @Override
181 public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
182 setTime(this, hourOfDay, minute);
183 updateTimeAndDateDisplay();
184 }
185
186 private void showDatePicker() {
187 DatePickerFragment datePickerFragment = DatePickerFragment.newInstance();
188 datePickerFragment.show(getFragmentManager(), DatePickerFragment.TAG);
189 }
190
191 private void showTimePicker() {
192 TimePickerFragment timePickerFragment = TimePickerFragment.newInstance();
193 timePickerFragment.show(getFragmentManager(), TimePickerFragment.TAG);
194 }
195
196 private void updateTimeAndDateDisplay() {
197 java.text.DateFormat shortDateFormat = DateFormat.getDateFormat(this);
198 final Calendar now = Calendar.getInstance();
199 mTimeTextView.setText(DateFormat.getTimeFormat(this).format(now.getTime()));
200 mDateTextView.setText(shortDateFormat.format(now.getTime()));
201 }
202
Michael W8dc3cf82020-05-11 14:44:18 +0200203 private static SimpleAdapter constructTimezoneAdapter(Context context) {
cretin45d4cea552016-04-25 11:00:04 -0700204 final String[] from = new String[] {KEY_DISPLAYNAME, KEY_GMT};
205 final int[] to = new int[] {android.R.id.text1, android.R.id.text2};
206
Michael W8dc3cf82020-05-11 14:44:18 +0200207 final TimeZoneComparator comparator = new TimeZoneComparator(KEY_OFFSET);
208 final List<Map<String, Object>> sortedList = ZoneGetter.getZonesList(context);
cretin45d4cea552016-04-25 11:00:04 -0700209 Collections.sort(sortedList, comparator);
210 final SimpleAdapter adapter = new SimpleAdapter(context,
211 sortedList,
212 R.layout.date_time_setup_custom_list_item_2,
213 from,
214 to);
215
216 return adapter;
217 }
218
cretin45d4cea552016-04-25 11:00:04 -0700219 private static int getTimeZoneIndex(SimpleAdapter adapter, TimeZone tz) {
220 final String defaultId = tz.getID();
221 final int listSize = adapter.getCount();
222 for (int i = 0; i < listSize; i++) {
Michael W8dc3cf82020-05-11 14:44:18 +0200223 final Map<?,?> map = (Map<?,?>)adapter.getItem(i);
cretin45d4cea552016-04-25 11:00:04 -0700224 final String id = (String)map.get(KEY_ID);
225 if (defaultId.equals(id)) {
226 // If current timezone is in this list, move focus to it
227 return i;
228 }
229 }
230 return -1;
231 }
232
233 private static void setDate(Context context, int year, int month, int day) {
234 Calendar c = Calendar.getInstance();
235
236 c.set(Calendar.YEAR, year);
237 c.set(Calendar.MONTH, month);
238 c.set(Calendar.DAY_OF_MONTH, day);
239 long when = c.getTimeInMillis();
240
241 if (when / 1000 < Integer.MAX_VALUE) {
242 ((AlarmManager) context.getSystemService(Context.ALARM_SERVICE)).setTime(when);
243 }
244 }
245
246 private static void setTime(Context context, int hourOfDay, int minute) {
247 Calendar c = Calendar.getInstance();
248
249 c.set(Calendar.HOUR_OF_DAY, hourOfDay);
250 c.set(Calendar.MINUTE, minute);
251 c.set(Calendar.SECOND, 0);
252 c.set(Calendar.MILLISECOND, 0);
253 long when = c.getTimeInMillis();
254
255 if (when / 1000 < Integer.MAX_VALUE) {
256 ((AlarmManager) context.getSystemService(Context.ALARM_SERVICE)).setTime(when);
257 }
258 }
259
Michael W8dc3cf82020-05-11 14:44:18 +0200260 private static class TimeZoneComparator implements Comparator<Map<?, ?>> {
cretin45d4cea552016-04-25 11:00:04 -0700261 private String mSortingKey;
262
263 public TimeZoneComparator(String sortingKey) {
264 mSortingKey = sortingKey;
265 }
266
267 public void setSortingKey(String sortingKey) {
268 mSortingKey = sortingKey;
269 }
270
Michael W8dc3cf82020-05-11 14:44:18 +0200271 public int compare(Map<?, ?> map1, Map<?, ?> map2) {
cretin45d4cea552016-04-25 11:00:04 -0700272 Object value1 = map1.get(mSortingKey);
273 Object value2 = map2.get(mSortingKey);
274
275 /*
276 * This should never happen, but just in-case, put non-comparable
277 * items at the end.
278 */
279 if (!isComparable(value1)) {
280 return isComparable(value2) ? 1 : 0;
281 } else if (!isComparable(value2)) {
282 return -1;
283 }
284
285 return ((Comparable) value1).compareTo(value2);
286 }
287
288 private boolean isComparable(Object value) {
289 return (value != null) && (value instanceof Comparable);
290 }
291 }
292
293 public static class TimePickerFragment extends DialogFragment
294 implements TimePickerDialog.OnTimeSetListener {
295
296 private static String TAG = TimePickerFragment.class.getSimpleName();
297
298 public static TimePickerFragment newInstance() {
299 TimePickerFragment frag = new TimePickerFragment();
300 return frag;
301 }
302
303 @Override
304 public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
305 ((DateTimeActivity)getActivity()).onTimeSet(view, hourOfDay, minute);
306 }
307
308 @Override
309 public Dialog onCreateDialog(Bundle savedInstanceState) {
310 final Calendar calendar = Calendar.getInstance();
311 return new TimePickerDialog(
312 getActivity(),
313 this,
314 calendar.get(Calendar.HOUR_OF_DAY),
315 calendar.get(Calendar.MINUTE),
316 DateFormat.is24HourFormat(getActivity()));
317 }
318
319 }
320
321 public static class DatePickerFragment extends DialogFragment
322 implements DatePickerDialog.OnDateSetListener {
323
324 private static String TAG = DatePickerFragment.class.getSimpleName();
325
326 public static DatePickerFragment newInstance() {
327 DatePickerFragment frag = new DatePickerFragment();
328 return frag;
329 }
330
331 @Override
332 public void onDateSet(DatePicker view, int year, int month, int day) {
333 ((DateTimeActivity)getActivity()).onDateSet(view, year, month, day);
334 }
335
336 @Override
337 public Dialog onCreateDialog(Bundle savedInstanceState) {
338 final Calendar calendar = Calendar.getInstance();
339 return new DatePickerDialog(
340 getActivity(),
341 this,
342 calendar.get(Calendar.YEAR),
343 calendar.get(Calendar.MONTH),
344 calendar.get(Calendar.DAY_OF_MONTH));
345 }
346 }
347}