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