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