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.app.Activity;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.res.Resources;
23 import android.icu.text.DateFormat;
24 import android.icu.text.SimpleDateFormat;
25 import android.icu.util.Calendar;
26 
27 import androidx.annotation.Nullable;
28 
29 import com.android.settings.R;
30 import com.android.settings.datetime.timezone.model.TimeZoneData;
31 
32 import java.util.ArrayList;
33 import java.util.Date;
34 import java.util.List;
35 import java.util.Locale;
36 
37 /**
38  * Render a list of {@class TimeZoneInfo} into the list view in {@class BaseTimeZonePicker}
39  */
40 public abstract class BaseTimeZoneInfoPicker extends BaseTimeZonePicker {
41     protected static final String TAG = "RegionZoneSearchPicker";
42     protected ZoneAdapter mAdapter;
43 
BaseTimeZoneInfoPicker(int titleResId, int searchHintResId, boolean searchEnabled, boolean defaultExpandSearch)44     protected BaseTimeZoneInfoPicker(int titleResId, int searchHintResId,
45             boolean searchEnabled, boolean defaultExpandSearch) {
46         super(titleResId, searchHintResId, searchEnabled, defaultExpandSearch);
47     }
48 
49     @Override
createAdapter(TimeZoneData timeZoneData)50     protected BaseTimeZoneAdapter createAdapter(TimeZoneData timeZoneData) {
51         mAdapter = new ZoneAdapter(getContext(), getAllTimeZoneInfos(timeZoneData),
52                 this::onListItemClick, getLocale(), getHeaderText());
53         return mAdapter;
54     }
55 
56     /**
57      * @return the text shown in the header, or null to show no header.
58      */
getHeaderText()59     protected @Nullable CharSequence getHeaderText() {
60         return null;
61     }
62 
onListItemClick(TimeZoneInfoItem item)63     private void onListItemClick(TimeZoneInfoItem item) {
64         final TimeZoneInfo timeZoneInfo = item.mTimeZoneInfo;
65         getActivity().setResult(Activity.RESULT_OK, prepareResultData(timeZoneInfo));
66         getActivity().finish();
67     }
68 
prepareResultData(TimeZoneInfo selectedTimeZoneInfo)69     protected Intent prepareResultData(TimeZoneInfo selectedTimeZoneInfo) {
70         return new Intent().putExtra(EXTRA_RESULT_TIME_ZONE_ID, selectedTimeZoneInfo.getId());
71     }
72 
getAllTimeZoneInfos(TimeZoneData timeZoneData)73     public abstract List<TimeZoneInfo> getAllTimeZoneInfos(TimeZoneData timeZoneData);
74 
75     protected static class ZoneAdapter extends BaseTimeZoneAdapter<TimeZoneInfoItem> {
76 
ZoneAdapter(Context context, List<TimeZoneInfo> timeZones, OnListItemClickListener<TimeZoneInfoItem> onListItemClickListener, Locale locale, CharSequence headerText)77         public ZoneAdapter(Context context, List<TimeZoneInfo> timeZones,
78                 OnListItemClickListener<TimeZoneInfoItem> onListItemClickListener, Locale locale,
79                 CharSequence headerText) {
80             super(createTimeZoneInfoItems(context, timeZones, locale),
81                     onListItemClickListener, locale,  true /* showItemSummary */,
82                     headerText /* headerText */);
83         }
84 
createTimeZoneInfoItems(Context context, List<TimeZoneInfo> timeZones, Locale locale)85         private static List<TimeZoneInfoItem> createTimeZoneInfoItems(Context context,
86                 List<TimeZoneInfo> timeZones, Locale locale) {
87             final DateFormat currentTimeFormat = new SimpleDateFormat(
88                     android.text.format.DateFormat.getTimeFormatString(context), locale);
89             final ArrayList<TimeZoneInfoItem> results = new ArrayList<>(timeZones.size());
90             final Resources resources = context.getResources();
91             long i = 0;
92             for (TimeZoneInfo timeZone : timeZones) {
93                 results.add(new TimeZoneInfoItem(i++, timeZone, resources, currentTimeFormat));
94             }
95             return results;
96         }
97     }
98 
99     private static class TimeZoneInfoItem implements BaseTimeZoneAdapter.AdapterItem {
100         private final long mItemId;
101         private final TimeZoneInfo mTimeZoneInfo;
102         private final Resources mResources;
103         private final DateFormat mTimeFormat;
104         private final String mTitle;
105         private final String[] mSearchKeys;
106 
TimeZoneInfoItem(long itemId, TimeZoneInfo timeZoneInfo, Resources resources, DateFormat timeFormat)107         private TimeZoneInfoItem(long itemId, TimeZoneInfo timeZoneInfo, Resources resources,
108                 DateFormat timeFormat) {
109             mItemId = itemId;
110             mTimeZoneInfo = timeZoneInfo;
111             mResources = resources;
112             mTimeFormat = timeFormat;
113             mTitle = createTitle(timeZoneInfo);
114             mSearchKeys = new String[] { mTitle };
115         }
116 
createTitle(TimeZoneInfo timeZoneInfo)117         private static String createTitle(TimeZoneInfo timeZoneInfo) {
118             String name = timeZoneInfo.getExemplarLocation();
119             if (name == null) {
120                 name = timeZoneInfo.getGenericName();
121             }
122             if (name == null && timeZoneInfo.getTimeZone().inDaylightTime(new Date())) {
123                 name = timeZoneInfo.getDaylightName();
124             }
125             if (name == null) {
126                 name = timeZoneInfo.getStandardName();
127             }
128             if (name == null) {
129                 name = String.valueOf(timeZoneInfo.getGmtOffset());
130             }
131             return name;
132         }
133 
134         @Override
getTitle()135         public CharSequence getTitle() {
136             return mTitle;
137         }
138 
139         @Override
getSummary()140         public CharSequence getSummary() {
141             String name = mTimeZoneInfo.getGenericName();
142             if (name == null) {
143                 if (mTimeZoneInfo.getTimeZone().inDaylightTime(new Date())) {
144                     name = mTimeZoneInfo.getDaylightName();
145                 } else {
146                     name = mTimeZoneInfo.getStandardName();
147                 }
148             }
149 
150             // Ignore name / GMT offset if the title shows the same information
151             if (name == null || name.equals(mTitle)) {
152                 CharSequence gmtOffset = mTimeZoneInfo.getGmtOffset();
153                 return gmtOffset == null || gmtOffset.toString().equals(mTitle) ? "" : gmtOffset;
154             } else {
155                 return SpannableUtil.getResourcesText(mResources,
156                         R.string.zone_info_offset_and_name, mTimeZoneInfo.getGmtOffset(), name);
157             }
158         }
159 
160         @Override
getIconText()161         public String getIconText() {
162             return null;
163         }
164 
165         @Override
getCurrentTime()166         public String getCurrentTime() {
167             return mTimeFormat.format(Calendar.getInstance(mTimeZoneInfo.getTimeZone()));
168         }
169 
170         @Override
getItemId()171         public long getItemId() {
172             return mItemId;
173         }
174 
175         @Override
getSearchKeys()176         public String[] getSearchKeys() {
177             return mSearchKeys;
178         }
179     }
180 }
181