1 /*
2  * Copyright (C) 2018 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 
17 package com.android.settings.datetime.timezone;
18 
19 import android.content.Context;
20 import android.icu.text.DateFormat;
21 import android.icu.text.DisplayContext;
22 import android.icu.text.SimpleDateFormat;
23 import android.icu.util.BasicTimeZone;
24 import android.icu.util.Calendar;
25 import android.icu.util.TimeZone;
26 import android.icu.util.TimeZoneTransition;
27 
28 import androidx.annotation.VisibleForTesting;
29 import androidx.preference.Preference;
30 
31 import com.android.settings.R;
32 import com.android.settings.core.BasePreferenceController;
33 
34 import java.util.Date;
35 
36 public class TimeZoneInfoPreferenceController extends BasePreferenceController {
37 
38     @VisibleForTesting
39     Date mDate;
40     private TimeZoneInfo mTimeZoneInfo;
41     private final DateFormat mDateFormat;
42 
TimeZoneInfoPreferenceController(Context context, String key)43     public TimeZoneInfoPreferenceController(Context context, String key) {
44         super(context, key);
45         mDateFormat = DateFormat.getDateInstance(SimpleDateFormat.LONG);
46         mDateFormat.setContext(DisplayContext.CAPITALIZATION_NONE);
47         mDate = new Date();
48     }
49 
50     @Override
getAvailabilityStatus()51     public int getAvailabilityStatus() {
52         return mTimeZoneInfo != null ? AVAILABLE_UNSEARCHABLE : UNSUPPORTED_ON_DEVICE;
53     }
54 
55     @Override
getSummary()56     public CharSequence getSummary() {
57         return mTimeZoneInfo == null ? "" : formatInfo(mTimeZoneInfo);
58     }
59 
setTimeZoneInfo(TimeZoneInfo timeZoneInfo)60     public void setTimeZoneInfo(TimeZoneInfo timeZoneInfo) {
61         mTimeZoneInfo = timeZoneInfo;
62     }
63 
formatOffsetAndName(TimeZoneInfo item)64     private CharSequence formatOffsetAndName(TimeZoneInfo item) {
65         String name = item.getGenericName();
66         if (name == null) {
67             if (item.getTimeZone().inDaylightTime(mDate)) {
68                 name = item.getDaylightName();
69             } else {
70                 name = item.getStandardName();
71             }
72         }
73         if (name == null) {
74             return item.getGmtOffset().toString();
75         } else {
76             return SpannableUtil.getResourcesText(mContext.getResources(),
77                     R.string.zone_info_offset_and_name, item.getGmtOffset(),
78                     name);
79         }
80     }
81 
formatInfo(TimeZoneInfo item)82     private CharSequence formatInfo(TimeZoneInfo item) {
83         final CharSequence offsetAndName = formatOffsetAndName(item);
84         final TimeZone timeZone = item.getTimeZone();
85         if (!timeZone.observesDaylightTime()) {
86             return mContext.getString(R.string.zone_info_footer_no_dst, offsetAndName);
87         }
88 
89         final TimeZoneTransition nextDstTransition = findNextDstTransition(timeZone);
90         if (nextDstTransition == null) {
91             return null;
92         }
93         final boolean toDst = nextDstTransition.getTo().getDSTSavings() != 0;
94         String timeType = toDst ? item.getDaylightName() : item.getStandardName();
95         if (timeType == null) {
96             // Fall back to generic "summer time" and "standard time" if the time zone has no
97             // specific names.
98             timeType = toDst ?
99                     mContext.getString(R.string.zone_time_type_dst) :
100                     mContext.getString(R.string.zone_time_type_standard);
101 
102         }
103         final Calendar transitionTime = Calendar.getInstance(timeZone);
104         transitionTime.setTimeInMillis(nextDstTransition.getTime());
105         final String date = mDateFormat.format(transitionTime);
106         return SpannableUtil.getResourcesText(mContext.getResources(),
107                 R.string.zone_info_footer, offsetAndName, timeType, date);
108     }
109 
findNextDstTransition(TimeZone timeZone)110     private TimeZoneTransition findNextDstTransition(TimeZone timeZone) {
111         if (!(timeZone instanceof BasicTimeZone)) {
112             return null;
113         }
114         final BasicTimeZone basicTimeZone = (BasicTimeZone) timeZone;
115         TimeZoneTransition transition = basicTimeZone.getNextTransition(
116                 mDate.getTime(), /* inclusive */ false);
117         do {
118             if (transition.getTo().getDSTSavings() != transition.getFrom().getDSTSavings()) {
119                 break;
120             }
121             transition = basicTimeZone.getNextTransition(
122                     transition.getTime(), /*inclusive */ false);
123         } while (transition != null);
124         return transition;
125     }
126 }
127