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.car.settings.language;
18 
19 import android.content.Context;
20 
21 import androidx.annotation.Nullable;
22 import androidx.annotation.VisibleForTesting;
23 import androidx.preference.Preference;
24 import androidx.preference.PreferenceCategory;
25 import androidx.preference.PreferenceGroup;
26 
27 import com.android.car.settings.common.Logger;
28 import com.android.car.settings.common.PreferenceUtil;
29 import com.android.car.settingslib.R;
30 import com.android.car.settingslib.language.LanguagePickerUtils;
31 import com.android.car.ui.preference.CarUiPreference;
32 import com.android.internal.app.LocaleStore;
33 import com.android.internal.app.SuggestedLocaleAdapter;
34 
35 import java.util.Set;
36 
37 /**
38  * Provides a wrapper around the {@link SuggestedLocaleAdapter} to create Preferences to populate
39  * the Language Settings screen.
40  */
41 public class LocalePreferenceProvider {
42 
43     private static final Logger LOG = new Logger(LanguagePickerPreferenceController.class);
44 
45     /** Creates a new instance of the preference provider. */
newInstance(Context context, Set<LocaleStore.LocaleInfo> localeInfoSet, @Nullable LocaleStore.LocaleInfo parentLocale)46     public static LocalePreferenceProvider newInstance(Context context,
47             Set<LocaleStore.LocaleInfo> localeInfoSet,
48             @Nullable LocaleStore.LocaleInfo parentLocale) {
49         SuggestedLocaleAdapter adapter = LanguagePickerUtils.createSuggestedLocaleAdapter(context,
50                 localeInfoSet, parentLocale);
51         return new LocalePreferenceProvider(context, adapter);
52     }
53 
54     /**
55      * Header types are copied from {@link SuggestedLocaleAdapter} in order to be able to
56      * determine the header rows.
57      */
58     @VisibleForTesting
59     static final int TYPE_HEADER_SUGGESTED = 0;
60     @VisibleForTesting
61     static final int TYPE_HEADER_ALL_OTHERS = 1;
62     @VisibleForTesting
63     static final int TYPE_LOCALE = 2;
64 
65     private final Context mContext;
66     private SuggestedLocaleAdapter mSuggestedLocaleAdapter;
67 
68     @VisibleForTesting
LocalePreferenceProvider(Context context, SuggestedLocaleAdapter localeAdapter)69     LocalePreferenceProvider(Context context, SuggestedLocaleAdapter localeAdapter) {
70         mContext = context;
71         mSuggestedLocaleAdapter = localeAdapter;
72     }
73 
74     /**
75      * Populates the base preference group based on the hierarchy provided by this provider.
76      *
77      * @param base     the preference container which will hold the language preferences created by
78      *                 this provider
79      * @param listener the click listener registered to the language/locale preferences contained in
80      *                 the base preference group
81      */
populateBasePreference(PreferenceGroup base, Set<String> ignorables, Preference.OnPreferenceClickListener listener)82     public void populateBasePreference(PreferenceGroup base, Set<String> ignorables,
83             Preference.OnPreferenceClickListener listener) {
84         /*
85          * LocalePreferenceProvider can give elements to be represented in 2 ways. In the first
86          * way, it simply provides the LocalePreferences which lists the available options. In the
87          * second way, this provider may also provide PreferenceCategories to break up the
88          * options into "Suggested" and "All others". The screen is constructed by taking a look
89          * at the type of Preference that is provided through LocalePreferenceProvider.
90          *
91          * In the first case (no subcategories), preferences are added directly to the base
92          * container. Otherwise, elements are added to the last category that was provided
93          * (stored in "category").
94          */
95         PreferenceCategory category = null;
96         for (int position = 0; position < mSuggestedLocaleAdapter.getCount(); position++) {
97             Preference preference = getPreference(position, ignorables);
98             if (PreferenceUtil.checkPreferenceType(preference, PreferenceCategory.class)) {
99                 category = (PreferenceCategory) preference;
100                 base.addPreference(category);
101             } else {
102                 preference.setOnPreferenceClickListener(listener);
103                 if (category == null) {
104                     base.addPreference(preference);
105                 } else {
106                     category.addPreference(preference);
107                 }
108             }
109         }
110     }
111 
112     /**
113      * Constructs a PreferenceCategory or Preference with locale arguments based on the type of item
114      * provided.
115      */
getPreference(int position, Set<String> ignorables)116     private Preference getPreference(int position, Set<String> ignorables) {
117         int type = mSuggestedLocaleAdapter.getItemViewType(position);
118         switch (type) {
119             case TYPE_HEADER_SUGGESTED:
120             case TYPE_HEADER_ALL_OTHERS:
121                 PreferenceCategory category = new PreferenceCategory(mContext);
122                 category.setTitle(type == TYPE_HEADER_SUGGESTED
123                         ? R.string.language_picker_list_suggested_header
124                         : R.string.language_picker_list_all_header);
125                 return category;
126             case TYPE_LOCALE:
127                 LocaleStore.LocaleInfo info =
128                         (LocaleStore.LocaleInfo) mSuggestedLocaleAdapter.getItem(position);
129                 CarUiPreference preference = new CarUiPreference(mContext);
130                 preference.setTitle(info.getFullNameNative());
131                 // Only locales with multiple sublocales needs to show the chevron, since in those
132                 // cases, the user needs to navigate to the child fragment to select the sublocale.
133                 Set<LocaleStore.LocaleInfo> subLocales = LocaleStore.getLevelLocales(
134                         mContext,
135                         ignorables,
136                         info,
137                         /* translatedOnly */ true);
138                 preference.setShowChevron(subLocales.size() > 1);
139                 LocaleUtil.setLocaleArgument(preference, info);
140                 return preference;
141             default:
142                 LOG.d("Attempting to get unknown type: " + type);
143                 throw new IllegalStateException("Unknown locale list item type");
144         }
145     }
146 }
147