blob: cc60a761cf0c48bc4a82833d4960bc133f3de66b [file] [log] [blame]
/*
* SPDX-FileCopyrightText: 2016 The CyanogenMod Project
* SPDX-FileCopyrightText: 2017-2024 The LineageOS Project
* SPDX-License-Identifier: Apache-2.0
*/
package org.lineageos.setupwizard;
import android.app.AlarmManager;
import android.app.DatePickerDialog;
import android.app.Dialog;
import android.app.TimePickerDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.text.format.DateFormat;
import android.view.View;
import android.widget.AdapterView;
import android.widget.DatePicker;
import android.widget.SimpleAdapter;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.TimePicker;
import androidx.annotation.NonNull;
import androidx.fragment.app.DialogFragment;
import com.android.settingslib.datetime.ZoneGetter;
import org.lineageos.setupwizard.util.SetupWizardUtils;
import java.util.Calendar;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
public class DateTimeActivity extends BaseSetupWizardActivity implements
TimePickerDialog.OnTimeSetListener, DatePickerDialog.OnDateSetListener {
private static final String KEY_ID = "id"; // value: String
private static final String KEY_DISPLAYNAME = "name"; // value: String
private static final String KEY_GMT = "gmt"; // value: String
private static final String KEY_OFFSET = "offset"; // value: int (Integer)
private TimeZone mCurrentTimeZone;
private TextView mDateTextView;
private TextView mTimeTextView;
private final Handler mHandler = new Handler(Looper.getMainLooper());
private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
updateTimeAndDateDisplay();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setNextText(R.string.next);
getGlifLayout().setDescriptionText(getString(R.string.date_time_summary));
final Spinner spinner = findViewById(R.id.timezone_list);
final SimpleAdapter adapter = constructTimezoneAdapter(this);
mCurrentTimeZone = TimeZone.getDefault();
View dateView = findViewById(R.id.date_item);
dateView.setOnClickListener((view) -> showDatePicker());
View timeView = findViewById(R.id.time_item);
timeView.setOnClickListener((view) -> showTimePicker());
mDateTextView = findViewById(R.id.date_text);
mTimeTextView = findViewById(R.id.time_text);
// Pre-select current/default timezone
mHandler.post(() -> {
int tzIndex = getTimeZoneIndex(adapter, mCurrentTimeZone);
spinner.setAdapter(adapter);
if (tzIndex != -1) {
spinner.setSelection(tzIndex);
}
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int position,
long id) {
final Map<?, ?> map = (Map<?, ?>) adapterView.getItemAtPosition(position);
final String tzId = (String) map.get(KEY_ID);
if (mCurrentTimeZone != null && !mCurrentTimeZone.getID().equals(tzId)) {
// Update the system timezone value
final AlarmManager alarm =
(AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarm.setTimeZone(tzId);
mCurrentTimeZone = TimeZone.getTimeZone(tzId);
}
}
@Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
});
});
// Pre-select current/default date if epoch
mHandler.post(() -> {
final Calendar calendar = Calendar.getInstance();
final boolean isEpoch = calendar.get(Calendar.YEAR) == 1970;
if (isEpoch) {
// If epoch, set date to build date
long timestamp = SetupWizardUtils.getBuildDateTimestamp();
if (timestamp > 0) {
calendar.setTimeInMillis(timestamp * 1000);
setDate(DateTimeActivity.this, calendar.get(Calendar.YEAR),
calendar.get(Calendar.MONTH), calendar.get(Calendar.DAY_OF_MONTH));
} else {
// no build date available, use a sane default
setDate(DateTimeActivity.this, 2017, Calendar.JANUARY, 1);
}
}
});
}
@Override
public void onResume() {
super.onResume();
// Register for time ticks and other reasons for time change
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_TIME_TICK);
filter.addAction(Intent.ACTION_TIME_CHANGED);
filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
registerReceiver(mIntentReceiver, filter, null, null);
updateTimeAndDateDisplay();
}
@Override
public void onPause() {
super.onPause();
unregisterReceiver(mIntentReceiver);
}
@Override
protected int getLayoutResId() {
return R.layout.setup_datetime_page;
}
@Override
protected int getTitleResId() {
return R.string.setup_datetime;
}
@Override
protected int getIconResId() {
return R.drawable.ic_datetime;
}
@Override
public void onDateSet(DatePicker view, int year, int month, int day) {
setDate(this, year, month, day);
updateTimeAndDateDisplay();
}
@Override
public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
setTime(this, hourOfDay, minute);
updateTimeAndDateDisplay();
}
private void showDatePicker() {
DatePickerFragment datePickerFragment = DatePickerFragment.newInstance();
datePickerFragment.show(getSupportFragmentManager(), DatePickerFragment.TAG);
}
private void showTimePicker() {
TimePickerFragment timePickerFragment = TimePickerFragment.newInstance();
timePickerFragment.show(getSupportFragmentManager(), TimePickerFragment.TAG);
}
private void updateTimeAndDateDisplay() {
java.text.DateFormat shortDateFormat = DateFormat.getDateFormat(this);
final Calendar now = Calendar.getInstance();
mTimeTextView.setText(DateFormat.getTimeFormat(this).format(now.getTime()));
mDateTextView.setText(shortDateFormat.format(now.getTime()));
}
private static SimpleAdapter constructTimezoneAdapter(Context context) {
final String[] from = new String[]{KEY_DISPLAYNAME, KEY_GMT};
final int[] to = new int[]{android.R.id.text1, android.R.id.text2};
final TimeZoneComparator comparator = new TimeZoneComparator(KEY_OFFSET);
final List<Map<String, Object>> sortedList = ZoneGetter.getZonesList(context);
sortedList.sort(comparator);
return new SimpleAdapter(context,
sortedList,
R.layout.date_time_setup_custom_list_item_2,
from,
to);
}
private static int getTimeZoneIndex(SimpleAdapter adapter, TimeZone tz) {
final String defaultId = tz.getID();
final int listSize = adapter.getCount();
for (int i = 0; i < listSize; i++) {
final Map<?, ?> map = (Map<?, ?>) adapter.getItem(i);
final String id = (String) map.get(KEY_ID);
if (defaultId.equals(id)) {
// If current timezone is in this list, move focus to it
return i;
}
}
return -1;
}
private static void setDate(Context context, int year, int month, int day) {
Calendar c = Calendar.getInstance();
c.set(Calendar.YEAR, year);
c.set(Calendar.MONTH, month);
c.set(Calendar.DAY_OF_MONTH, day);
long when = c.getTimeInMillis();
if (when / 1000 < Integer.MAX_VALUE) {
((AlarmManager) context.getSystemService(Context.ALARM_SERVICE)).setTime(when);
}
}
private static void setTime(Context context, int hourOfDay, int minute) {
Calendar c = Calendar.getInstance();
c.set(Calendar.HOUR_OF_DAY, hourOfDay);
c.set(Calendar.MINUTE, minute);
c.set(Calendar.SECOND, 0);
c.set(Calendar.MILLISECOND, 0);
long when = c.getTimeInMillis();
if (when / 1000 < Integer.MAX_VALUE) {
((AlarmManager) context.getSystemService(Context.ALARM_SERVICE)).setTime(when);
}
}
private static class TimeZoneComparator implements Comparator<Map<?, ?>> {
private final String mSortingKey;
public TimeZoneComparator(String sortingKey) {
mSortingKey = sortingKey;
}
public int compare(Map<?, ?> map1, Map<?, ?> map2) {
Object value1 = map1.get(mSortingKey);
Object value2 = map2.get(mSortingKey);
/*
* This should never happen, but just in-case, put non-comparable
* items at the end.
*/
if (!isComparable(value1)) {
return isComparable(value2) ? 1 : 0;
} else if (!isComparable(value2)) {
return -1;
}
return ((Comparable) value1).compareTo(value2);
}
private boolean isComparable(Object value) {
return (value instanceof Comparable);
}
}
public static class TimePickerFragment extends DialogFragment
implements TimePickerDialog.OnTimeSetListener {
private static final String TAG = TimePickerFragment.class.getSimpleName();
public static TimePickerFragment newInstance() {
return new TimePickerFragment();
}
@Override
public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
((DateTimeActivity) getActivity()).onTimeSet(view, hourOfDay, minute);
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Calendar calendar = Calendar.getInstance();
return new TimePickerDialog(
getActivity(),
this,
calendar.get(Calendar.HOUR_OF_DAY),
calendar.get(Calendar.MINUTE),
DateFormat.is24HourFormat(getActivity()));
}
}
public static class DatePickerFragment extends DialogFragment
implements DatePickerDialog.OnDateSetListener {
private static final String TAG = DatePickerFragment.class.getSimpleName();
public static DatePickerFragment newInstance() {
return new DatePickerFragment();
}
@Override
public void onDateSet(DatePicker view, int year, int month, int day) {
((DateTimeActivity) getActivity()).onDateSet(view, year, month, day);
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Calendar calendar = Calendar.getInstance();
return new DatePickerDialog(
getActivity(),
this,
calendar.get(Calendar.YEAR),
calendar.get(Calendar.MONTH),
calendar.get(Calendar.DAY_OF_MONTH));
}
}
}