• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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.localepicker;
18 
19 import static com.android.settings.flags.Flags.localeNotificationEnabled;
20 
21 import android.app.FragmentTransaction;
22 import android.app.LocaleManager;
23 import android.app.NotificationChannel;
24 import android.app.NotificationManager;
25 import android.app.PendingIntent;
26 import android.app.settings.SettingsEnums;
27 import android.content.Intent;
28 import android.content.pm.PackageManager;
29 import android.net.Uri;
30 import android.os.Bundle;
31 import android.os.LocaleList;
32 import android.os.SystemClock;
33 import android.text.TextUtils;
34 import android.util.Log;
35 import android.view.MenuItem;
36 import android.view.View;
37 import android.widget.FrameLayout;
38 import android.widget.ListView;
39 
40 import androidx.annotation.Nullable;
41 import androidx.core.app.NotificationCompat;
42 import androidx.core.view.ViewCompat;
43 
44 import com.android.internal.app.LocalePickerWithRegion;
45 import com.android.internal.app.LocaleStore;
46 import com.android.settings.R;
47 import com.android.settings.applications.AppLocaleUtil;
48 import com.android.settings.applications.appinfo.AppLocaleDetails;
49 import com.android.settings.core.SettingsBaseActivity;
50 import com.android.settings.overlay.FeatureFactory;
51 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
52 
53 @Deprecated
54 public class AppLocalePickerActivity extends SettingsBaseActivity
55         implements LocalePickerWithRegion.LocaleSelectedListener, MenuItem.OnActionExpandListener {
56     private static final String TAG = AppLocalePickerActivity.class.getSimpleName();
57     private static final String CHANNEL_ID_SUGGESTION = "suggestion";
58     private static final String CHANNEL_ID_SUGGESTION_TO_USER = "Locale suggestion";
59     private static final int SIM_LOCALE = 1 << 0;
60     private static final int SYSTEM_LOCALE = 1 << 1;
61     private static final int APP_LOCALE = 1 << 2;
62     private static final int IME_LOCALE = 1 << 3;
63     static final String EXTRA_APP_LOCALE = "app_locale";
64     static final String EXTRA_NOTIFICATION_ID = "notification_id";
65 
66     private String mPackageName;
67     private LocalePickerWithRegion mLocalePickerWithRegion;
68     private AppLocaleDetails mAppLocaleDetails;
69     private View mAppLocaleDetailContainer;
70     private NotificationController mNotificationController;
71     private MetricsFeatureProvider mMetricsFeatureProvider;
72     @Nullable private String mParentLocale;
73 
74     @Override
onCreate(Bundle savedInstanceState)75     public void onCreate(Bundle savedInstanceState) {
76         super.onCreate(savedInstanceState);
77         Uri data = getIntent().getData();
78         if (data == null) {
79             Log.d(TAG, "There is no uri data.");
80             finish();
81             return;
82         }
83         mPackageName = data.getSchemeSpecificPart();
84         if (TextUtils.isEmpty(mPackageName)) {
85             Log.d(TAG, "There is no package name.");
86             finish();
87             return;
88         }
89 
90         if (!canDisplayLocaleUi()) {
91             Log.w(TAG, "Not allow to display Locale Settings UI.");
92             finish();
93             return;
94         }
95 
96         setTitle(R.string.app_locale_picker_title);
97         getActionBar().setDisplayHomeAsUpEnabled(true);
98         mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
99         mNotificationController = NotificationController.getInstance(this);
100 
101         mLocalePickerWithRegion = LocalePickerWithRegion.createLanguagePicker(
102                 this,
103                 this,
104                 false /* translate only */,
105                 null,
106                 mPackageName,
107                 this);
108         mAppLocaleDetails = AppLocaleDetails.newInstance(mPackageName, getUserId());
109         mAppLocaleDetailContainer = launchAppLocaleDetailsPage();
110         // Launch Locale picker part.
111         launchLocalePickerPage();
112     }
113 
114     @Override
onOptionsItemSelected(MenuItem item)115     public boolean onOptionsItemSelected(MenuItem item) {
116         if (item.getItemId() == android.R.id.home) {
117             super.onBackPressed();
118             return true;
119         }
120         return super.onOptionsItemSelected(item);
121     }
122 
123     @Override
onLocaleSelected(LocaleStore.LocaleInfo localeInfo)124     public void onLocaleSelected(LocaleStore.LocaleInfo localeInfo) {
125         if (localeInfo == null || localeInfo.getLocale() == null || localeInfo.isSystemLocale()) {
126             setAppDefaultLocale("");
127         } else {
128             logLocaleSource(localeInfo);
129             setAppDefaultLocale(localeInfo.getLocale().toLanguageTag());
130             broadcastAppLocaleChange(localeInfo);
131         }
132         finish();
133     }
134 
135     @Override
onParentLocaleSelected(LocaleStore.LocaleInfo localeInfo)136     public void onParentLocaleSelected(LocaleStore.LocaleInfo localeInfo) {
137         mParentLocale = localeInfo.getFullNameNative();
138     }
139 
140     @Override
onMenuItemActionCollapse(MenuItem item)141     public boolean onMenuItemActionCollapse(MenuItem item) {
142         mAppBarLayout.setExpanded(false /*expanded*/, false /*animate*/);
143         ViewCompat.setNestedScrollingEnabled(mAppLocaleDetails.getListView(), true);
144         ViewCompat.setNestedScrollingEnabled(mLocalePickerWithRegion.getListView(), true);
145         return true;
146     }
147 
148     @Override
onMenuItemActionExpand(MenuItem item)149     public boolean onMenuItemActionExpand(MenuItem item) {
150         mAppBarLayout.setExpanded(false /*expanded*/, false /*animate*/);
151         ViewCompat.setNestedScrollingEnabled(mAppLocaleDetails.getListView(), false);
152         ViewCompat.setNestedScrollingEnabled(mLocalePickerWithRegion.getListView(), false);
153         return true;
154     }
155 
156     /** Sets the app's locale to the supplied language tag */
setAppDefaultLocale(String languageTag)157     private void setAppDefaultLocale(String languageTag) {
158         Log.d(TAG, "setAppDefaultLocale: " + languageTag);
159         LocaleManager localeManager = getSystemService(LocaleManager.class);
160         if (localeManager == null) {
161             Log.w(TAG, "LocaleManager is null, cannot set default app locale");
162             return;
163         }
164         localeManager.setApplicationLocales(mPackageName, LocaleList.forLanguageTags(languageTag));
165     }
166 
broadcastAppLocaleChange(LocaleStore.LocaleInfo localeInfo)167     private void broadcastAppLocaleChange(LocaleStore.LocaleInfo localeInfo) {
168         if (!localeNotificationEnabled()) {
169             Log.w(TAG, "Locale notification is not enabled");
170             return;
171         }
172         if (localeInfo.isAppCurrentLocale()) {
173             return;
174         }
175         try {
176             String localeTag = localeInfo.getLocale().toLanguageTag();
177             int uid = getPackageManager().getApplicationInfo(mPackageName,
178                     PackageManager.GET_META_DATA).uid;
179             boolean launchNotification = mNotificationController.shouldTriggerNotification(
180                     uid, localeTag);
181             if (launchNotification) {
182                 triggerNotification(
183                         mNotificationController.getNotificationId(localeTag),
184                         getString(R.string.title_system_locale_addition,
185                                 localeInfo.getFullNameNative()),
186                         getString(R.string.desc_system_locale_addition),
187                         localeTag);
188                 mMetricsFeatureProvider.action(this,
189                         SettingsEnums.ACTION_NOTIFICATION_FOR_SYSTEM_LOCALE);
190             }
191         } catch (PackageManager.NameNotFoundException e) {
192             Log.e(TAG, "Unable to find info for package: " + mPackageName);
193         }
194     }
195 
triggerNotification( int notificationId, String title, String description, String localeTag)196     private void triggerNotification(
197             int notificationId,
198             String title,
199             String description,
200             String localeTag) {
201         NotificationManager notificationManager = getSystemService(NotificationManager.class);
202         final boolean channelExist =
203                 notificationManager.getNotificationChannel(CHANNEL_ID_SUGGESTION) != null;
204 
205         // Create an alert channel if it does not exist
206         if (!channelExist) {
207             NotificationChannel channel =
208                     new NotificationChannel(
209                             CHANNEL_ID_SUGGESTION,
210                             CHANNEL_ID_SUGGESTION_TO_USER,
211                             NotificationManager.IMPORTANCE_DEFAULT);
212             channel.setSound(/* sound */ null, /* audioAttributes */ null); // silent notification
213             notificationManager.createNotificationChannel(channel);
214         }
215         final NotificationCompat.Builder builder =
216                 new NotificationCompat.Builder(this, CHANNEL_ID_SUGGESTION)
217                         .setSmallIcon(R.drawable.ic_settings_language)
218                         .setAutoCancel(true)
219                         .setContentTitle(title)
220                         .setContentText(description)
221                         .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
222                         .setContentIntent(
223                                 createPendingIntent(localeTag, notificationId, false))
224                         .setDeleteIntent(
225                                 createPendingIntent(localeTag, notificationId, true));
226         notificationManager.notify(notificationId, builder.build());
227     }
228 
createPendingIntent(String locale, int notificationId, boolean isDeleteIntent)229     private PendingIntent createPendingIntent(String locale, int notificationId,
230             boolean isDeleteIntent) {
231         Intent intent = isDeleteIntent
232                 ? new Intent(this, NotificationCancelReceiver.class)
233                 : new Intent(this, NotificationActionActivity.class)
234                         .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
235 
236         intent.putExtra(EXTRA_APP_LOCALE, locale)
237                 .putExtra(EXTRA_NOTIFICATION_ID, notificationId);
238         int flag = PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT;
239         int elapsedTime = (int) SystemClock.elapsedRealtimeNanos();
240 
241         return isDeleteIntent
242                 ? PendingIntent.getBroadcast(this, elapsedTime, intent, flag)
243                 : PendingIntent.getActivity(this, elapsedTime, intent, flag);
244     }
245 
launchAppLocaleDetailsPage()246     private View launchAppLocaleDetailsPage() {
247         FrameLayout appLocaleDetailsContainer = new FrameLayout(this);
248         appLocaleDetailsContainer.setId(R.id.layout_app_locale_details);
249         getSupportFragmentManager()
250                 .beginTransaction()
251                 .replace(R.id.layout_app_locale_details, mAppLocaleDetails)
252                 .commit();
253         return appLocaleDetailsContainer;
254     }
255 
launchLocalePickerPage()256     private void launchLocalePickerPage() {
257         // LocalePickerWithRegion use android.app.ListFragment. Thus, it can not use
258         // getSupportFragmentManager() to add this into container.
259         android.app.FragmentManager fragmentManager = getFragmentManager();
260         fragmentManager.registerFragmentLifecycleCallbacks(
261                 new android.app.FragmentManager.FragmentLifecycleCallbacks() {
262                     @Override
263                     public void onFragmentViewCreated(
264                             android.app.FragmentManager fm,
265                             android.app.Fragment f, View v, Bundle s) {
266                         super.onFragmentViewCreated(fm, f, v, s);
267                         ListView listView = (ListView) v.findViewById(android.R.id.list);
268                         if (listView != null) {
269                             if (mParentLocale != null) {
270                                 mAppLocaleDetails = AppLocaleDetails.newInstance(mPackageName,
271                                         getUserId());
272                                 mAppLocaleDetailContainer = launchAppLocaleDetailsPage();
273                                 mAppLocaleDetails.setParentLocale(mParentLocale);
274                             }
275                             listView.addHeaderView(mAppLocaleDetailContainer);
276                         }
277                     }
278                 }, true);
279         fragmentManager.beginTransaction()
280                 .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
281                 .replace(R.id.content_frame, mLocalePickerWithRegion)
282                 .commit();
283     }
284 
canDisplayLocaleUi()285     private boolean canDisplayLocaleUi() {
286         try {
287             PackageManager packageManager = getPackageManager();
288             return AppLocaleUtil.canDisplayLocaleUi(this,
289                     packageManager.getApplicationInfo(mPackageName, 0),
290                     packageManager.queryIntentActivities(AppLocaleUtil.LAUNCHER_ENTRY_INTENT,
291                             PackageManager.GET_META_DATA));
292         } catch (PackageManager.NameNotFoundException e) {
293             Log.e(TAG, "Unable to find info for package: " + mPackageName);
294         }
295 
296         return false;
297     }
298 
logLocaleSource(LocaleStore.LocaleInfo localeInfo)299     private void logLocaleSource(LocaleStore.LocaleInfo localeInfo) {
300         if (!localeInfo.isSuggested() || localeInfo.isAppCurrentLocale()) {
301             return;
302         }
303         int localeSource = 0;
304         if (hasSuggestionType(localeInfo,
305                 LocaleStore.LocaleInfo.SUGGESTION_TYPE_SYSTEM_AVAILABLE_LANGUAGE)) {
306             localeSource |= SYSTEM_LOCALE;
307         }
308         if (hasSuggestionType(localeInfo,
309                 LocaleStore.LocaleInfo.SUGGESTION_TYPE_OTHER_APP_LANGUAGE)) {
310             localeSource |= APP_LOCALE;
311         }
312         if (hasSuggestionType(localeInfo, LocaleStore.LocaleInfo.SUGGESTION_TYPE_IME_LANGUAGE)) {
313             localeSource |= IME_LOCALE;
314         }
315         if (hasSuggestionType(localeInfo, LocaleStore.LocaleInfo.SUGGESTION_TYPE_SIM)) {
316             localeSource |= SIM_LOCALE;
317         }
318         mMetricsFeatureProvider.action(this,
319                 SettingsEnums.ACTION_CHANGE_APP_LANGUAGE_FROM_SUGGESTED, localeSource);
320     }
321 
hasSuggestionType(LocaleStore.LocaleInfo localeInfo, int suggestionType)322     private static boolean hasSuggestionType(LocaleStore.LocaleInfo localeInfo,
323             int suggestionType) {
324         return localeInfo.isSuggestionOfType(suggestionType);
325     }
326 }
327