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 static com.android.settingslib.datetime.ZoneGetter.capitalizeForStandaloneDisplay;
20 
21 import android.app.Activity;
22 import android.app.settings.SettingsEnums;
23 import android.content.Intent;
24 import android.icu.text.Collator;
25 import android.icu.text.LocaleDisplayNames;
26 import android.os.Bundle;
27 import android.util.Log;
28 
29 import androidx.annotation.VisibleForTesting;
30 
31 import com.android.settings.R;
32 import com.android.settings.core.SubSettingLauncher;
33 import com.android.settings.datetime.timezone.BaseTimeZoneAdapter.AdapterItem;
34 import com.android.settings.datetime.timezone.model.FilteredCountryTimeZones;
35 import com.android.settings.datetime.timezone.model.TimeZoneData;
36 
37 import java.util.ArrayList;
38 import java.util.Comparator;
39 import java.util.List;
40 import java.util.Set;
41 import java.util.TreeSet;
42 
43 /**
44  * Render a list of regions into a list view.
45  */
46 public class RegionSearchPicker extends BaseTimeZonePicker {
47     private static final int REQUEST_CODE_ZONE_PICKER = 1;
48     private static final String TAG = "RegionSearchPicker";
49 
50     private BaseTimeZoneAdapter<RegionItem> mAdapter;
51     private TimeZoneData mTimeZoneData;
52 
RegionSearchPicker()53     public RegionSearchPicker() {
54         super(R.string.date_time_select_region, R.string.date_time_search_region, true, true);
55     }
56 
57     @Override
getMetricsCategory()58     public int getMetricsCategory() {
59         return SettingsEnums.SETTINGS_ZONE_PICKER_REGION;
60     }
61 
62     @Override
createAdapter(TimeZoneData timeZoneData)63     protected BaseTimeZoneAdapter createAdapter(TimeZoneData timeZoneData) {
64         mTimeZoneData = timeZoneData;
65         mAdapter = new BaseTimeZoneAdapter<>(createAdapterItem(timeZoneData.getRegionIds()),
66                 this::onListItemClick, getLocale(), false /* showItemSummary */,
67                     null /* headerText */);
68         return mAdapter;
69     }
70 
onListItemClick(RegionItem item)71     private void onListItemClick(RegionItem item) {
72         final String regionId = item.getId();
73         final FilteredCountryTimeZones countryTimeZones = mTimeZoneData.lookupCountryTimeZones(
74                 regionId);
75         final Activity activity = getActivity();
76         if (countryTimeZones == null || countryTimeZones.getPreferredTimeZoneIds().isEmpty()) {
77             Log.e(TAG, "Region has no time zones: " + regionId);
78             activity.setResult(Activity.RESULT_CANCELED);
79             activity.finish();
80             return;
81         }
82 
83         List<String> timeZoneIds = countryTimeZones.getPreferredTimeZoneIds();
84         // Choose the time zone associated the region if there is only one time zone in that region
85         if (timeZoneIds.size() == 1) {
86             final Intent resultData = new Intent()
87                     .putExtra(EXTRA_RESULT_REGION_ID, regionId)
88                     .putExtra(EXTRA_RESULT_TIME_ZONE_ID, timeZoneIds.get(0));
89             getActivity().setResult(Activity.RESULT_OK, resultData);
90             getActivity().finish();
91         } else {
92             // Launch the zone picker and let the user choose a time zone from the list of
93             // time zones associated with the region.
94             final Bundle args = new Bundle();
95             args.putString(RegionZonePicker.EXTRA_REGION_ID, regionId);
96             new SubSettingLauncher(getContext())
97                     .setDestination(RegionZonePicker.class.getCanonicalName())
98                     .setArguments(args)
99                     .setSourceMetricsCategory(getMetricsCategory())
100                     .setResultListener(this, REQUEST_CODE_ZONE_PICKER)
101                     .launch();
102         }
103     }
104 
105     @Override
onActivityResult(int requestCode, int resultCode, Intent data)106     public void onActivityResult(int requestCode, int resultCode, Intent data) {
107         if (requestCode == REQUEST_CODE_ZONE_PICKER) {
108             if (resultCode == Activity.RESULT_OK) {
109                 getActivity().setResult(Activity.RESULT_OK, data);
110             }
111             getActivity().finish();
112         }
113     }
114 
createAdapterItem(Set<String> regionIds)115     private List<RegionItem> createAdapterItem(Set<String> regionIds) {
116         final Collator collator = Collator.getInstance(getLocale());
117         final TreeSet<RegionItem> items = new TreeSet<>(new RegionInfoComparator(collator));
118         final LocaleDisplayNames localeDisplayNames = LocaleDisplayNames.getInstance(getLocale());
119         long i = 0;
120         for (String regionId : regionIds) {
121             String name = capitalizeForStandaloneDisplay(
122                     mLocale, localeDisplayNames.regionDisplayName(regionId));
123             items.add(new RegionItem(i++, regionId, name));
124         }
125         return new ArrayList<>(items);
126     }
127 
128     @VisibleForTesting
129     static class RegionItem implements AdapterItem {
130 
131         private final String mId;
132         private final String mName;
133         private final long mItemId;
134         private final String[] mSearchKeys;
135 
RegionItem(long itemId, String id, String name)136         RegionItem(long itemId, String id, String name) {
137             mId = id;
138             mName = name;
139             mItemId = itemId;
140             // Allow to search with ISO_3166-1 alpha-2 code. It's handy for english users in some
141             // countries, e.g. US for United States. It's not best search keys for users, but
142             // ICU doesn't have the data for the alias names of a region.
143             mSearchKeys = new String[] {mId, mName};
144         }
145 
getId()146         public String getId() {
147             return mId;
148         }
149 
150         @Override
getTitle()151         public CharSequence getTitle() {
152             return mName;
153         }
154 
155         @Override
getSummary()156         public CharSequence getSummary() {
157             return null;
158         }
159 
160         @Override
getIconText()161         public String getIconText() {
162             return null;
163         }
164 
165         @Override
getCurrentTime()166         public String getCurrentTime() {
167             return null;
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     private static class RegionInfoComparator implements Comparator<RegionItem> {
182         private final Collator mCollator;
183 
RegionInfoComparator(Collator collator)184         RegionInfoComparator(Collator collator) {
185             mCollator = collator;
186         }
187 
188         @Override
compare(RegionItem r1, RegionItem r2)189         public int compare(RegionItem r1, RegionItem r2) {
190             return mCollator.compare(r1.getTitle(), r2.getTitle());
191         }
192     }
193 }
194