1 /* 2 * Copyright (C) 2014 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 androidx.appcompat.app; 18 19 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED; 20 import static android.content.pm.PackageManager.DONT_KILL_APP; 21 22 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX; 23 24 import static java.util.Objects.requireNonNull; 25 26 import android.app.Activity; 27 import android.app.Dialog; 28 import android.app.LocaleManager; 29 import android.content.ComponentName; 30 import android.content.Context; 31 import android.content.pm.PackageManager; 32 import android.content.pm.ServiceInfo; 33 import android.content.res.Configuration; 34 import android.os.Build; 35 import android.os.Bundle; 36 import android.os.LocaleList; 37 import android.util.AttributeSet; 38 import android.util.Log; 39 import android.view.MenuInflater; 40 import android.view.View; 41 import android.view.ViewGroup; 42 import android.view.Window; 43 import android.window.OnBackInvokedDispatcher; 44 45 import androidx.annotation.AnyThread; 46 import androidx.annotation.CallSuper; 47 import androidx.annotation.IdRes; 48 import androidx.annotation.IntDef; 49 import androidx.annotation.LayoutRes; 50 import androidx.annotation.RequiresApi; 51 import androidx.annotation.RestrictTo; 52 import androidx.annotation.StyleRes; 53 import androidx.annotation.VisibleForTesting; 54 import androidx.appcompat.view.ActionMode; 55 import androidx.appcompat.widget.Toolbar; 56 import androidx.appcompat.widget.VectorEnabledTintResources; 57 import androidx.collection.ArraySet; 58 import androidx.core.app.AppLocalesStorageHelper; 59 import androidx.core.os.LocaleListCompat; 60 import androidx.core.view.WindowCompat; 61 import androidx.fragment.app.FragmentActivity; 62 63 import org.jspecify.annotations.NonNull; 64 import org.jspecify.annotations.Nullable; 65 66 import java.lang.annotation.Retention; 67 import java.lang.annotation.RetentionPolicy; 68 import java.lang.ref.WeakReference; 69 import java.util.ArrayDeque; 70 import java.util.Iterator; 71 import java.util.Queue; 72 import java.util.concurrent.Executor; 73 74 /** 75 * This class represents a delegate which you can use to extend AppCompat's support to any 76 * {@link android.app.Activity}. 77 * 78 * <p>When using an {@link AppCompatDelegate}, you should call the following methods instead of the 79 * {@link android.app.Activity} method of the same name:</p> 80 * <ul> 81 * <li>{@link #addContentView(android.view.View, android.view.ViewGroup.LayoutParams)}</li> 82 * <li>{@link #setContentView(int)}</li> 83 * <li>{@link #setContentView(android.view.View)}</li> 84 * <li>{@link #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)}</li> 85 * <li>{@link #requestWindowFeature(int)}</li> 86 * <li>{@link #hasWindowFeature(int)}</li> 87 * <li>{@link #invalidateOptionsMenu()}</li> 88 * <li>{@link #startSupportActionMode(androidx.appcompat.view.ActionMode.Callback)}</li> 89 * <li>{@link #setSupportActionBar(androidx.appcompat.widget.Toolbar)}</li> 90 * <li>{@link #getSupportActionBar()}</li> 91 * <li>{@link #getMenuInflater()}</li> 92 * <li>{@link #findViewById(int)}</li> 93 * </ul> 94 * 95 * <p>The following methods should be called from the {@link android.app.Activity} method of the 96 * same name:</p> 97 * <ul> 98 * <li>{@link #onCreate(android.os.Bundle)}</li> 99 * <li>{@link #onPostCreate(android.os.Bundle)}</li> 100 * <li>{@link #onConfigurationChanged(android.content.res.Configuration)}</li> 101 * <li>{@link #onStart()}</li> 102 * <li>{@link #onStop()}</li> 103 * <li>{@link #onPostResume()}</li> 104 * <li>{@link #onSaveInstanceState(Bundle)}</li> 105 * <li>{@link #setTitle(CharSequence)}</li> 106 * <li>{@link #onStop()}</li> 107 * <li>{@link #onDestroy()}</li> 108 * </ul> 109 * 110 * <p>An {@link Activity} can only be linked with one {@link AppCompatDelegate} instance, 111 * therefore the instance returned from {@link #create(Activity, AppCompatCallback)} should be 112 * retained until the Activity is destroyed.</p> 113 */ 114 public abstract class AppCompatDelegate { 115 static final boolean DEBUG = false; 116 static final String TAG = "AppCompatDelegate"; 117 118 static SerialExecutor sSerialExecutorForLocalesStorage = new 119 SerialExecutor(new ThreadPerTaskExecutor()); 120 121 static final String APP_LOCALES_META_DATA_HOLDER_SERVICE_NAME = "androidx.appcompat.app" 122 + ".AppLocalesMetadataHolderService"; 123 124 /** 125 * Implementation of {@link java.util.concurrent.Executor} that executes runnables serially 126 * by synchronizing the {@link Executor#execute(Runnable)} method and maintaining a tasks 127 * queue. 128 */ 129 static class SerialExecutor implements Executor { 130 private final Object mLock = new Object(); 131 final Queue<Runnable> mTasks = new ArrayDeque<>(); 132 final Executor mExecutor; 133 Runnable mActive; 134 SerialExecutor(Executor executor)135 SerialExecutor(Executor executor) { 136 this.mExecutor = executor; 137 } 138 139 @Override execute(final Runnable r)140 public void execute(final Runnable r) { 141 synchronized (mLock) { 142 mTasks.add(() -> { 143 try { 144 r.run(); 145 } finally { 146 scheduleNext(); 147 } 148 }); 149 if (mActive == null) { 150 scheduleNext(); 151 } 152 } 153 } 154 scheduleNext()155 protected void scheduleNext() { 156 synchronized (mLock) { 157 if ((mActive = mTasks.poll()) != null) { 158 mExecutor.execute(mActive); 159 } 160 } 161 } 162 } 163 164 /** 165 * Implementation of {@link java.util.concurrent.Executor} that executes each runnable on a 166 * new thread. 167 */ 168 static class ThreadPerTaskExecutor implements Executor { 169 @Override execute(Runnable r)170 public void execute(Runnable r) { 171 new Thread(r).start(); 172 } 173 } 174 175 /** 176 * Mode which uses the system's night mode setting to determine if it is night or not. 177 * 178 * @see #setLocalNightMode(int) 179 */ 180 public static final int MODE_NIGHT_FOLLOW_SYSTEM = -1; 181 182 /** 183 * Night mode which switches between dark and light mode depending on the time of day 184 * (dark at night, light in the day). 185 * 186 * <p>The calculation used to determine whether it is night or not makes use of the location 187 * APIs (if this app has the necessary permissions). This allows us to generate accurate 188 * sunrise and sunset times. If this app does not have permission to access the location APIs 189 * then we use hardcoded times which will be less accurate.</p> 190 * 191 * @deprecated Automatic switching of dark/light based on the current time is deprecated. 192 * Considering using an explicit setting, or {@link #MODE_NIGHT_AUTO_BATTERY}. 193 */ 194 @Deprecated 195 public static final int MODE_NIGHT_AUTO_TIME = 0; 196 197 /** 198 * @deprecated Use {@link AppCompatDelegate#MODE_NIGHT_AUTO_TIME} instead 199 */ 200 @SuppressWarnings("deprecation") 201 @Deprecated 202 public static final int MODE_NIGHT_AUTO = MODE_NIGHT_AUTO_TIME; 203 204 /** 205 * Night mode which uses always uses a light mode, enabling {@code notnight} qualified 206 * resources regardless of the time. 207 * 208 * @see #setLocalNightMode(int) 209 */ 210 public static final int MODE_NIGHT_NO = 1; 211 212 /** 213 * Night mode which uses always uses a dark mode, enabling {@code night} qualified 214 * resources regardless of the time. 215 * 216 * @see #setLocalNightMode(int) 217 */ 218 public static final int MODE_NIGHT_YES = 2; 219 220 /** 221 * Night mode which uses a dark mode when the system's 'Battery Saver' feature is enabled, 222 * otherwise it uses a 'light mode'. This mode can help the device to decrease power usage, 223 * depending on the display technology in the device. 224 * 225 * <em>Please note: this mode should only be used when running on devices which do not 226 * provide a similar device-wide setting.</em> 227 * 228 * @see #setLocalNightMode(int) 229 */ 230 public static final int MODE_NIGHT_AUTO_BATTERY = 3; 231 232 /** 233 * An unspecified mode for night mode. This is primarily used with 234 * {@link #setLocalNightMode(int)}, to allow the default night mode to be used. 235 * If both the default and local night modes are set to this value, then the default value of 236 * {@link #MODE_NIGHT_FOLLOW_SYSTEM} is applied. 237 * 238 * @see AppCompatDelegate#setDefaultNightMode(int) 239 */ 240 public static final int MODE_NIGHT_UNSPECIFIED = -100; 241 242 @NightMode 243 private static int sDefaultNightMode = MODE_NIGHT_UNSPECIFIED; 244 245 private static LocaleListCompat sRequestedAppLocales = null; 246 private static LocaleListCompat sStoredAppLocales = null; 247 private static Boolean sIsAutoStoreLocalesOptedIn = null; 248 private static boolean sIsFrameworkSyncChecked = false; 249 250 /** 251 * All AppCompatDelegate instances associated with a "live" Activity, e.g. lifecycle state is 252 * post-onCreate and pre-onDestroy. These instances are used to instrument night mode's uiMode 253 * configuration changes. 254 */ 255 private static final ArraySet<WeakReference<AppCompatDelegate>> sActivityDelegates = 256 new ArraySet<>(); 257 private static final Object sActivityDelegatesLock = new Object(); 258 private static final Object sAppLocalesStorageSyncLock = new Object(); 259 260 @SuppressWarnings("deprecation") 261 @RestrictTo(LIBRARY_GROUP_PREFIX) 262 @IntDef({MODE_NIGHT_NO, MODE_NIGHT_YES, MODE_NIGHT_AUTO_TIME, MODE_NIGHT_FOLLOW_SYSTEM, 263 MODE_NIGHT_UNSPECIFIED, MODE_NIGHT_AUTO_BATTERY}) 264 @Retention(RetentionPolicy.SOURCE) 265 public @interface NightMode {} 266 267 @IntDef({MODE_NIGHT_NO, MODE_NIGHT_YES, MODE_NIGHT_FOLLOW_SYSTEM}) 268 @Retention(RetentionPolicy.SOURCE) 269 @interface ApplyableNightMode {} 270 271 /** 272 * Flag for enabling the support Action Bar. 273 * 274 * <p>This is enabled by default for some devices. The Action Bar replaces the title bar and 275 * provides an alternate location for an on-screen menu button on some devices. 276 */ 277 public static final int FEATURE_SUPPORT_ACTION_BAR = 100 + WindowCompat.FEATURE_ACTION_BAR; 278 279 /** 280 * Flag for requesting an support Action Bar that overlays window content. 281 * Normally an Action Bar will sit in the space above window content, but if this 282 * feature is requested along with {@link #FEATURE_SUPPORT_ACTION_BAR} it will be layered over 283 * the window content itself. This is useful if you would like your app to have more control 284 * over how the Action Bar is displayed, such as letting application content scroll beneath 285 * an Action Bar with a transparent background or otherwise displaying a transparent/translucent 286 * Action Bar over application content. 287 * 288 * <p>This mode is especially useful with {@code View.SYSTEM_UI_FLAG_FULLSCREEN}, which allows 289 * you to seamlessly hide the action bar in conjunction with other screen decorations. 290 * When an ActionBar is in this mode it will adjust the insets provided to 291 * {@link View#fitSystemWindows(android.graphics.Rect) View.fitSystemWindows(Rect)} 292 * to include the content covered by the action bar, so you can do layout within 293 * that space. 294 */ 295 public static final int FEATURE_SUPPORT_ACTION_BAR_OVERLAY = 296 100 + WindowCompat.FEATURE_ACTION_BAR_OVERLAY; 297 298 /** 299 * Flag for specifying the behavior of action modes when an Action Bar is not present. 300 * If overlay is enabled, the action mode UI will be allowed to cover existing window content. 301 */ 302 public static final int FEATURE_ACTION_MODE_OVERLAY = WindowCompat.FEATURE_ACTION_MODE_OVERLAY; 303 304 /** 305 * Create an {@link androidx.appcompat.app.AppCompatDelegate} to use with {@code activity}. 306 * 307 * @param activity The activity linked to the returned instance. 308 * @param callback An optional callback for AppCompat specific events 309 */ create(@onNull Activity activity, @Nullable AppCompatCallback callback)310 public static @NonNull AppCompatDelegate create(@NonNull Activity activity, 311 @Nullable AppCompatCallback callback) { 312 return new AppCompatDelegateImpl(activity, callback); 313 } 314 315 /** 316 * Create an {@link androidx.appcompat.app.AppCompatDelegate} to use with {@code dialog}. 317 * 318 * @param dialog The dialog to be linked to the returned instance instead of an activity. 319 * @param callback An optional callback for AppCompat specific events 320 */ create(@onNull Dialog dialog, @Nullable AppCompatCallback callback)321 public static @NonNull AppCompatDelegate create(@NonNull Dialog dialog, 322 @Nullable AppCompatCallback callback) { 323 return new AppCompatDelegateImpl(dialog, callback); 324 } 325 326 /** 327 * Create an {@link androidx.appcompat.app.AppCompatDelegate} to use with a {@code context} 328 * and a {@code window}. 329 * 330 * @param context The context of the associated window 331 * @param window The window to be linked to the returned instance instead of an activity. 332 * @param callback An optional callback for AppCompat specific events 333 */ create(@onNull Context context, @NonNull Window window, @Nullable AppCompatCallback callback)334 public static @NonNull AppCompatDelegate create(@NonNull Context context, 335 @NonNull Window window, @Nullable AppCompatCallback callback) { 336 return new AppCompatDelegateImpl(context, window, callback); 337 } 338 339 /** 340 * Create an {@link androidx.appcompat.app.AppCompatDelegate} to use with a {@code context} 341 * and hosted by an {@code Activity}. 342 * 343 * @param context The context of the associated activity 344 * @param activity The activity to be linked to the returned instance 345 * @param callback An optional callback for AppCompat specific events 346 */ create(@onNull Context context, @NonNull Activity activity, @Nullable AppCompatCallback callback)347 public static @NonNull AppCompatDelegate create(@NonNull Context context, 348 @NonNull Activity activity, @Nullable AppCompatCallback callback) { 349 return new AppCompatDelegateImpl(context, activity, callback); 350 } 351 352 /** 353 * Private constructor 354 */ AppCompatDelegate()355 AppCompatDelegate() {} 356 357 /** 358 * Support library version of {@link Activity#getActionBar}. 359 * 360 * @return AppCompat's action bar, or null if it does not have one. 361 */ getSupportActionBar()362 public abstract @Nullable ActionBar getSupportActionBar(); 363 364 /** 365 * Set a {@link Toolbar} to act as the {@link ActionBar} for this delegate. 366 * 367 * <p>When set to a non-null value the {@link #getSupportActionBar()} ()} method will return 368 * an {@link ActionBar} object that can be used to control the given toolbar as if it were 369 * a traditional window decor action bar. The toolbar's menu will be populated with the 370 * Activity's options menu and the navigation button will be wired through the standard 371 * {@link android.R.id#home home} menu select action.</p> 372 * 373 * <p>In order to use a Toolbar within the Activity's window content the application 374 * must not request the window feature 375 * {@link AppCompatDelegate#FEATURE_SUPPORT_ACTION_BAR FEATURE_SUPPORT_ACTION_BAR}.</p> 376 * 377 * @param toolbar Toolbar to set as the Activity's action bar, or {@code null} to clear it 378 */ setSupportActionBar(@ullable Toolbar toolbar)379 public abstract void setSupportActionBar(@Nullable Toolbar toolbar); 380 381 /** 382 * Return the value of this call from your {@link Activity#getMenuInflater()} 383 */ getMenuInflater()384 public abstract MenuInflater getMenuInflater(); 385 386 /** 387 * Should be called from {@link Activity#onCreate Activity.onCreate()}. 388 * 389 * <p>This should be called before {@code super.onCreate()} as so:</p> 390 * <pre class="prettyprint"> 391 * protected void onCreate(Bundle savedInstanceState) { 392 * getDelegate().onCreate(savedInstanceState); 393 * super.onCreate(savedInstanceState); 394 * // ... 395 * } 396 * </pre> 397 */ onCreate(Bundle savedInstanceState)398 public abstract void onCreate(Bundle savedInstanceState); 399 400 /** 401 * Should be called from {@link Activity#onPostCreate(android.os.Bundle)} 402 */ onPostCreate(Bundle savedInstanceState)403 public abstract void onPostCreate(Bundle savedInstanceState); 404 405 /** 406 * Should be called from 407 * {@link Activity#onConfigurationChanged} 408 */ onConfigurationChanged(Configuration newConfig)409 public abstract void onConfigurationChanged(Configuration newConfig); 410 411 /** 412 * Should be called from {@link Activity#onStart()} Activity.onStart()} 413 */ onStart()414 public abstract void onStart(); 415 416 /** 417 * Should be called from {@link Activity#onStop Activity.onStop()} 418 */ onStop()419 public abstract void onStop(); 420 421 /** 422 * Should be called from {@link Activity#onPostResume()} 423 */ onPostResume()424 public abstract void onPostResume(); 425 426 /** 427 * This should be called from {@link Activity#setTheme(int)} to notify AppCompat of what 428 * the current theme resource id is. 429 */ setTheme(@tyleRes int themeResId)430 public void setTheme(@StyleRes int themeResId) { 431 } 432 433 /** 434 * Finds a view that was identified by the id attribute from the XML that 435 * was processed in {@link #onCreate}. 436 * 437 * @return The view if found or null otherwise. 438 */ 439 @SuppressWarnings("TypeParameterUnusedInFormals") findViewById(@dRes int id)440 public abstract <T extends View> @Nullable T findViewById(@IdRes int id); 441 442 /** 443 * Should be called instead of {@link Activity#setContentView(android.view.View)}} 444 */ setContentView(View v)445 public abstract void setContentView(View v); 446 447 /** 448 * Should be called instead of {@link Activity#setContentView(int)}} 449 */ setContentView(@ayoutRes int resId)450 public abstract void setContentView(@LayoutRes int resId); 451 452 /** 453 * Should be called instead of 454 * {@link Activity#setContentView(android.view.View, android.view.ViewGroup.LayoutParams)}} 455 */ setContentView(View v, ViewGroup.LayoutParams lp)456 public abstract void setContentView(View v, ViewGroup.LayoutParams lp); 457 458 /** 459 * Should be called instead of 460 * {@link Activity#addContentView(android.view.View, android.view.ViewGroup.LayoutParams)}} 461 */ addContentView(View v, ViewGroup.LayoutParams lp)462 public abstract void addContentView(View v, ViewGroup.LayoutParams lp); 463 464 /** 465 * @deprecated use {@link #attachBaseContext2(Context)} instead. 466 */ 467 @Deprecated attachBaseContext(Context context)468 public void attachBaseContext(Context context) { 469 } 470 471 /** 472 * Should be called from {@link Activity#attachBaseContext(Context)}. 473 */ 474 @CallSuper attachBaseContext2(@onNull Context context)475 public @NonNull Context attachBaseContext2(@NonNull Context context) { 476 attachBaseContext(context); 477 return context; 478 } 479 480 /** 481 * Should be called from {@link Activity#onTitleChanged(CharSequence, int)}} 482 */ setTitle(@ullable CharSequence title)483 public abstract void setTitle(@Nullable CharSequence title); 484 485 /** 486 * Should be called from {@link Activity#invalidateOptionsMenu()}} or 487 * {@link FragmentActivity#supportInvalidateOptionsMenu()}. 488 */ invalidateOptionsMenu()489 public abstract void invalidateOptionsMenu(); 490 491 /** 492 * Should be called from {@link Activity#onDestroy()} 493 */ onDestroy()494 public abstract void onDestroy(); 495 496 /** 497 * Returns an {@link ActionBarDrawerToggle.Delegate} which can be returned from your Activity 498 * if it implements {@link ActionBarDrawerToggle.DelegateProvider}. 499 */ getDrawerToggleDelegate()500 public abstract ActionBarDrawerToggle.@Nullable Delegate getDrawerToggleDelegate(); 501 502 /** 503 * Enable extended window features. This should be called instead of 504 * {@link android.app.Activity#requestWindowFeature(int)} or 505 * {@link android.view.Window#requestFeature getWindow().requestFeature()}. 506 * 507 * @param featureId The desired feature as defined in {@link android.view.Window}. 508 * @return Returns true if the requested feature is supported and now 509 * enabled. 510 */ requestWindowFeature(int featureId)511 public abstract boolean requestWindowFeature(int featureId); 512 513 /** 514 * Query for the availability of a certain feature. 515 * 516 * <p>This should be called instead of {@link android.view.Window#hasFeature(int)}.</p> 517 * 518 * @param featureId The feature ID to check 519 * @return true if the feature is enabled, false otherwise. 520 */ hasWindowFeature(int featureId)521 public abstract boolean hasWindowFeature(int featureId); 522 523 /** 524 * Start an action mode. 525 * 526 * @param callback Callback that will manage lifecycle events for this context mode 527 * @return The ContextMode that was started, or null if it was canceled 528 */ startSupportActionMode( ActionMode.@onNull Callback callback)529 public abstract @Nullable ActionMode startSupportActionMode( 530 ActionMode.@NonNull Callback callback); 531 532 /** 533 * Installs AppCompat's {@link android.view.LayoutInflater} Factory so that it can replace 534 * the framework widgets with compatible tinted versions. This should be called before 535 * {@code super.onCreate()} as so: 536 * <pre class="prettyprint"> 537 * protected void onCreate(Bundle savedInstanceState) { 538 * getDelegate().installViewFactory(); 539 * getDelegate().onCreate(savedInstanceState); 540 * super.onCreate(savedInstanceState); 541 * 542 * // ... 543 * } 544 * </pre> 545 * If you are using your own {@link android.view.LayoutInflater.Factory Factory} or 546 * {@link android.view.LayoutInflater.Factory2 Factory2} then you can omit this call, and instead call 547 * {@link #createView(android.view.View, String, android.content.Context, android.util.AttributeSet)} 548 * from your factory to return any compatible widgets. 549 */ installViewFactory()550 public abstract void installViewFactory(); 551 552 /** 553 * This should be called from a 554 * {@link android.view.LayoutInflater.Factory2 LayoutInflater.Factory2} in order 555 * to return tint-aware widgets. 556 * <p> 557 * This is only needed if you are using your own 558 * {@link android.view.LayoutInflater LayoutInflater} factory, and have therefore not 559 * installed the default factory via {@link #installViewFactory()}. 560 */ createView(@ullable View parent, String name, @NonNull Context context, @NonNull AttributeSet attrs)561 public abstract View createView(@Nullable View parent, String name, @NonNull Context context, 562 @NonNull AttributeSet attrs); 563 564 /** 565 * Whether AppCompat handles any native action modes itself. 566 * <p>This methods only takes effect on 567 * {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH} and above. 568 * 569 * @param enabled whether AppCompat should handle native action modes. 570 */ setHandleNativeActionModesEnabled(boolean enabled)571 public abstract void setHandleNativeActionModesEnabled(boolean enabled); 572 573 /** 574 * Returns whether AppCompat handles any native action modes itself. 575 * 576 * @return true if AppCompat should handle native action modes. 577 */ isHandleNativeActionModesEnabled()578 public abstract boolean isHandleNativeActionModesEnabled(); 579 580 /** 581 * Allows AppCompat to save instance state. 582 */ onSaveInstanceState(Bundle outState)583 public abstract void onSaveInstanceState(Bundle outState); 584 585 /** 586 * Applies the currently selected night mode to this delegate's host component. 587 * 588 * <p>This enables the 589 * {@link 590 * androidx.appcompat.R.style#Theme_AppCompat_DayNight Theme.AppCompat.DayNight} 591 * family of themes to work, using the specified mode.</p> 592 * 593 * <p>You can be notified when the night changes by overriding the 594 * {@link AppCompatActivity#onNightModeChanged(int)} method.</p> 595 * 596 * @see #setDefaultNightMode(int) 597 * @see #setLocalNightMode(int) 598 * 599 * @return true if the night mode was applied, false if not 600 */ applyDayNight()601 public abstract boolean applyDayNight(); 602 603 /** 604 * Sets the {@link OnBackInvokedDispatcher} for handling system back for Android SDK 33 and 605 * above. 606 * <p> 607 * If the delegate is hosted by an {@link Activity}, the default dispatcher is obtained via 608 * {@link Activity#getOnBackInvokedDispatcher()}. 609 * 610 * @param dispatcher the OnBackInvokedDispatcher to be set on this delegate, or {@code null} 611 * to use the default dispatcher 612 */ 613 @CallSuper 614 @RequiresApi(33) setOnBackInvokedDispatcher(@ullable OnBackInvokedDispatcher dispatcher)615 public void setOnBackInvokedDispatcher(@Nullable OnBackInvokedDispatcher dispatcher) { 616 // Stub. 617 } 618 619 /** 620 * Applies the current locales to this delegate's host component. 621 * 622 * <p>Apps can be notified when the locales are changed by overriding the 623 * {@link AppCompatActivity#onLocalesChanged(LocaleListCompat)} method.</p> 624 * 625 * <p>This is a default implementation and it is overridden atin 626 * {@link AppCompatDelegateImpl#applyAppLocales()} </p> 627 * 628 * @see #setApplicationLocales(LocaleListCompat) 629 * 630 * @return true if requested app-specific locales were applied, false if not. 631 */ applyAppLocales()632 boolean applyAppLocales() { 633 return false; 634 } 635 636 /** 637 * Returns the context for the current delegate. 638 */ getContextForDelegate()639 public @Nullable Context getContextForDelegate() { 640 return null; 641 } 642 643 /** 644 * Override the night mode used for this delegate's host component. 645 * 646 * <p>When setting a mode to be used across an entire app, the 647 * {@link #setDefaultNightMode(int)} method is preferred.</p> 648 * 649 * <p>If this is called after the host component has been created, a {@code uiMode} 650 * configuration change will occur, which may result in the component being recreated.</p> 651 * 652 * <p>It is not recommended to use this method on a delegate attached to a {@link Dialog}. 653 * Dialogs use the host Activity as their context, resulting in the dialog's night mode 654 * overriding the Activity's night mode. 655 * 656 * <p><strong>Note:</strong> This method is not recommended for use on devices running SDK 16 657 * or earlier, as the specified night mode configuration may leak to other activities. Instead, 658 * consider using {@link #setDefaultNightMode(int)} to specify an app-wide night mode. 659 * 660 * @see #getLocalNightMode() 661 * @see #setDefaultNightMode(int) 662 */ setLocalNightMode(@ightMode int mode)663 public abstract void setLocalNightMode(@NightMode int mode); 664 665 /** 666 * Returns the night mode previously set via {@link #getLocalNightMode()}. 667 */ 668 @NightMode getLocalNightMode()669 public int getLocalNightMode() { 670 return MODE_NIGHT_UNSPECIFIED; 671 } 672 673 /** 674 * Sets the default night mode. This is the default value used for all components, but can 675 * be overridden locally via {@link #setLocalNightMode(int)}. 676 * 677 * <p>This is the primary method to control the DayNight functionality, since it allows 678 * the delegates to avoid unnecessary recreations when possible.</p> 679 * 680 * <p>If this method is called after any host components with attached 681 * {@link AppCompatDelegate}s have been 'created', a {@code uiMode} configuration change 682 * will occur in each. This may result in those components being recreated, depending 683 * on their manifest configuration.</p> 684 * 685 * <p>Defaults to {@link #MODE_NIGHT_FOLLOW_SYSTEM}.</p> 686 * 687 * @see #setLocalNightMode(int) 688 * @see #getDefaultNightMode() 689 */ 690 @SuppressWarnings("deprecation") setDefaultNightMode(@ightMode int mode)691 public static void setDefaultNightMode(@NightMode int mode) { 692 if (DEBUG) { 693 Log.d(TAG, String.format("setDefaultNightMode. New:%d, Current:%d", 694 mode, sDefaultNightMode)); 695 } 696 switch (mode) { 697 case MODE_NIGHT_NO: 698 case MODE_NIGHT_YES: 699 case MODE_NIGHT_FOLLOW_SYSTEM: 700 case MODE_NIGHT_AUTO_TIME: 701 case MODE_NIGHT_AUTO_BATTERY: 702 if (sDefaultNightMode != mode) { 703 sDefaultNightMode = mode; 704 applyDayNightToActiveDelegates(); 705 } else if (DEBUG) { 706 Log.d(TAG, String.format("Not applying changes, sDefaultNightMode already %d", 707 mode)); 708 } 709 break; 710 default: 711 Log.d(TAG, "setDefaultNightMode() called with an unknown mode"); 712 break; 713 } 714 } 715 716 /** 717 * Sets the current locales for the calling app. 718 * 719 * <p>If this method is called after any host components with attached 720 * {@link AppCompatDelegate}s have been 'created', a {@link LocaleList} configuration 721 * change will occur in each. This may result in those components being recreated, depending 722 * on their manifest configuration.</p> 723 * 724 * <p>This method accepts {@link LocaleListCompat} as an input parameter.</p> 725 * 726 * <p>Apps should continue to read Locales via their in-process {@link LocaleList}s.</p> 727 * 728 * <p>Pass a {@link LocaleListCompat#getEmptyLocaleList()} to reset to the system locale.</p> 729 * 730 * <p><b>Note: This API should always be called after Activity.onCreate(), apart from any 731 * exceptions explicitly mentioned in this documentation.</b></p> 732 * 733 * <p>On API level 33 and above, this API will handle storage automatically.</p> 734 * 735 * <p>For API levels below that, the developer has two options:</p> 736 * <ul> 737 * <li>They can opt-in to automatic storage handled through the library. They can do this by 738 * adding a special metaData entry in their {@code AndroidManifest.xml}, similar to : 739 * <pre><code> 740 * <service 741 * android:name="androidx.appcompat.app.AppLocalesMetadataHolderService" 742 * android:enabled="false" 743 * android:exported="false"> 744 * <meta-data 745 * android:name="autoStoreLocales" 746 * android:value="true" /> 747 * </service> 748 * </code></pre> 749 * They should be mindful that this will cause a blocking diskRead and diskWrite 750 * strictMode violation, and they might need to suppress it at their end.</li> 751 * 752 * <li>The second option is that they can choose to handle storage themselves. In order to 753 * do so they must use this API to initialize locales during app-start up and provide 754 * their stored locales. In this case, API should be called before Activity.onCreate() 755 * in the activity lifecycle, e.g. in attachBaseContext(). 756 * <b>Note: Developers should gate this to API versions < 33.</b> 757 * <p><b>This API should be called after Activity.onCreate() for all other cases.</b></p> 758 * </li> 759 * </ul> 760 * 761 * <p>When the application using this API with API versions < 33 updates to a 762 * version >= 33, then there can be two scenarios for this transition: 763 * <ul> 764 * <li>If the developer has opted-in for autoStorage then the locales will be automatically 765 * synced to the framework. Developers must specify android:enabled="false" for the 766 * AppLocalesMetadataHolderService as shown in the meta-data entry above.</li> 767 * <li>If the developer has not opted-in for autoStorage then they will need to handle 768 * this transition on their end.</li> 769 * </ul> 770 * 771 * <p><b>Note: This API work with the AppCompatActivity context, not for others context, for 772 * Android 12 (API level 32) and earlier. If there is a requirement to get the localized 773 * string which respects the per-app locale in non-AppCompatActivity context, please consider 774 * using {@link androidx.core.content.ContextCompat#getString(Context, int)} or 775 * {@link androidx.core.content.ContextCompat#getContextForLanguage(Context)}. </b></p> 776 * 777 * @param locales a list of locales. 778 */ setApplicationLocales(@onNull LocaleListCompat locales)779 public static void setApplicationLocales(@NonNull LocaleListCompat locales) { 780 requireNonNull(locales); 781 if (Build.VERSION.SDK_INT >= 33) { 782 // If the API version is 33 (version for T) or above we want to redirect the call to 783 // the framework API. 784 Object localeManager = getLocaleManagerForApplication(); 785 if (localeManager != null) { 786 Api33Impl.localeManagerSetApplicationLocales(localeManager, 787 Api24Impl.localeListForLanguageTags(locales.toLanguageTags())); 788 } 789 } else { 790 if (DEBUG) { 791 Log.d(TAG, String.format("sRequestedAppLocales. New:%s, Current:%s", 792 locales, sRequestedAppLocales)); 793 } 794 if (!locales.equals(sRequestedAppLocales)) { 795 synchronized (sActivityDelegatesLock) { 796 sRequestedAppLocales = locales; 797 applyLocalesToActiveDelegates(); 798 } 799 } else if (DEBUG) { 800 Log.d(TAG, String.format("Not applying changes, sRequestedAppLocales is already %s", 801 locales)); 802 } 803 } 804 } 805 806 /** 807 * Returns application locales for the calling app as a {@link LocaleListCompat}. 808 * 809 * <p>Returns a {@link LocaleListCompat#getEmptyLocaleList()} if no app-specific locales are 810 * set. 811 * 812 * <p><b>Note: This API only work at AppCompatDelegate and it should always be called after 813 * Activity.onCreate().</b></p> 814 */ 815 @AnyThread getApplicationLocales()816 public static @NonNull LocaleListCompat getApplicationLocales() { 817 if (Build.VERSION.SDK_INT >= 33) { 818 // If the API version is 33 or above we want to redirect the call to the framework API. 819 Object localeManager = getLocaleManagerForApplication(); 820 if (localeManager != null) { 821 return LocaleListCompat.wrap(Api33Impl.localeManagerGetApplicationLocales( 822 localeManager)); 823 } 824 } else { 825 if (sRequestedAppLocales != null) { 826 // If app-specific locales exists then sRequestedApplicationLocales contains the 827 // latest locales. 828 return sRequestedAppLocales; 829 } 830 } 831 return LocaleListCompat.getEmptyLocaleList(); 832 } 833 834 /** 835 * Returns the default night mode. 836 * 837 * @see #setDefaultNightMode(int) 838 */ 839 @NightMode getDefaultNightMode()840 public static int getDefaultNightMode() { 841 return sDefaultNightMode; 842 } 843 844 /** 845 * Returns the requested app locales. 846 * 847 * @see #setApplicationLocales(LocaleListCompat) 848 */ getRequestedAppLocales()849 static @Nullable LocaleListCompat getRequestedAppLocales() { 850 return sRequestedAppLocales; 851 } 852 853 /** 854 * Returns the stored app locales. 855 * 856 * @see #setApplicationLocales(LocaleListCompat) 857 */ getStoredAppLocales()858 static @Nullable LocaleListCompat getStoredAppLocales() { 859 return sStoredAppLocales; 860 } 861 862 /** 863 * Resets the static variables for requested and stored locales to null. This method is used 864 * for testing as it mimics activity restart which is difficult to do in a test. 865 */ 866 @VisibleForTesting resetStaticRequestedAndStoredLocales()867 static void resetStaticRequestedAndStoredLocales() { 868 sRequestedAppLocales = null; 869 sStoredAppLocales = null; 870 } 871 872 /** 873 * Sets {@link AppCompatDelegate#sIsAutoStoreLocalesOptedIn} to the provided value. This method 874 * is used for testing, setting sIsAutoStoreLocalesOptedIn to true mimics adding an opt-in 875 * "autoStoreLocales" meta-data entry. 876 * 877 * see {@link AppCompatDelegate#setApplicationLocales(LocaleListCompat)}. 878 */ 879 @VisibleForTesting setIsAutoStoreLocalesOptedIn(boolean isAutoStoreLocalesOptedIn)880 static void setIsAutoStoreLocalesOptedIn(boolean isAutoStoreLocalesOptedIn) { 881 sIsAutoStoreLocalesOptedIn = isAutoStoreLocalesOptedIn; 882 } 883 884 /** 885 * Returns the localeManager for the current application using active delegates to fetch 886 * context, returns null if no active delegates present. 887 */ 888 @RequiresApi(33) getLocaleManagerForApplication()889 static Object getLocaleManagerForApplication() { 890 for (WeakReference<AppCompatDelegate> activeDelegate : sActivityDelegates) { 891 final AppCompatDelegate delegate = activeDelegate.get(); 892 if (delegate != null) { 893 Context context = delegate.getContextForDelegate(); 894 if (context != null) { 895 return context.getSystemService(Context.LOCALE_SERVICE); 896 } 897 } 898 } 899 return null; 900 } 901 902 /** 903 * Returns true is the "autoStoreLocales" metaData is marked true in the app manifest. 904 */ isAutoStorageOptedIn(Context context)905 static boolean isAutoStorageOptedIn(Context context) { 906 if (sIsAutoStoreLocalesOptedIn == null) { 907 try { 908 ServiceInfo serviceInfo = AppLocalesMetadataHolderService.getServiceInfo( 909 context); 910 if (serviceInfo.metaData != null) { 911 sIsAutoStoreLocalesOptedIn = serviceInfo.metaData.getBoolean( 912 /* key= */ "autoStoreLocales"); 913 } 914 } catch (PackageManager.NameNotFoundException e) { 915 Log.d(TAG, "Checking for metadata for AppLocalesMetadataHolderService " 916 + ": Service not found"); 917 sIsAutoStoreLocalesOptedIn = false; 918 } 919 } 920 return sIsAutoStoreLocalesOptedIn; 921 } 922 923 /** 924 * Executes {@link AppCompatDelegate#syncRequestedAndStoredLocales(Context)} asynchronously 925 * on a worker thread, serialized using {@link 926 * AppCompatDelegate#sSerialExecutorForLocalesStorage}. 927 * 928 * <p>This is done to perform the storage read operation without blocking the main thread.</p> 929 */ asyncExecuteSyncRequestedAndStoredLocales(Context context)930 void asyncExecuteSyncRequestedAndStoredLocales(Context context) { 931 sSerialExecutorForLocalesStorage.execute(() -> syncRequestedAndStoredLocales(context)); 932 } 933 934 /** 935 * Syncs requested and persisted app-specific locales. 936 * 937 * <p>This sync is only performed if the developer has opted in to use the autoStoredLocales 938 * feature, marked by the metaData "autoStoreLocales" wrapped in the service 939 * "AppLocalesMetadataHolderService". If the metaData is not found in the manifest or holds 940 * the value false then we return from this function without doing anything. If the metaData 941 * is set to true, then we perform a sync for app-locales.</p> 942 * 943 * <p>If the API version is >=33, then the storage is checked for app-specific locales, if 944 * found they are synced to the framework by calling the 945 * {@link AppCompatDelegate#setApplicationLocales(LocaleListCompat)}</p> 946 * 947 * <p>If the API version is <33, then there are two scenarios:</p> 948 * <ul> 949 * <li>If the requestedAppLocales are not set then the app-specific locales are read from 950 * storage. If persisted app-specific locales are found then they are used to 951 * update the requestedAppLocales.</li> 952 * <li>If the requestedAppLocales are populated and are different from the stored locales 953 * then in that case the requestedAppLocales are stored and the static variable for 954 * storedAppLocales is updated accordingly.</li> 955 * </ul> 956 */ syncRequestedAndStoredLocales(Context context)957 static void syncRequestedAndStoredLocales(Context context) { 958 if (!isAutoStorageOptedIn(context)) { 959 return; 960 } else if (Build.VERSION.SDK_INT >= 33) { 961 if (!sIsFrameworkSyncChecked) { 962 // syncs locales from androidX to framework, it only happens once after the 963 // device is updated to API version 33(Tiramisu) or above. 964 sSerialExecutorForLocalesStorage.execute(() -> { 965 syncLocalesToFramework(context); 966 sIsFrameworkSyncChecked = true; 967 }); 968 } 969 } else { 970 synchronized (sAppLocalesStorageSyncLock) { 971 if (sRequestedAppLocales == null) { 972 if (sStoredAppLocales == null) { 973 sStoredAppLocales = 974 LocaleListCompat.forLanguageTags( 975 AppLocalesStorageHelper.readLocales(context)); 976 } 977 if (sStoredAppLocales.isEmpty()) { 978 // if both requestedLocales and storedLocales not set, then the user has not 979 // specified any application-specific locales. So no alterations in current 980 // application locales should take place. 981 return; 982 } 983 sRequestedAppLocales = sStoredAppLocales; 984 } else if (!sRequestedAppLocales.equals(sStoredAppLocales)) { 985 // if requestedLocales is set and is not equal to the storedLocales then in this 986 // case we need to store these locales in storage. 987 sStoredAppLocales = sRequestedAppLocales; 988 AppLocalesStorageHelper.persistLocales(context, 989 sRequestedAppLocales.toLanguageTags()); 990 } 991 } 992 } 993 } 994 995 /** 996 * Sets whether vector drawables on older platforms (< API 21) can be used within 997 * {@link android.graphics.drawable.DrawableContainer} resources. 998 * 999 * <p>When enabled, AppCompat can intercept some drawable inflation from the framework, which 1000 * enables implicit inflation of vector drawables within 1001 * {@link android.graphics.drawable.DrawableContainer} resources. You can then use those 1002 * drawables in places such as {@code android:src} on {@link android.widget.ImageView}, 1003 * or {@code android:drawableLeft} on {@link android.widget.TextView}. Example usage:</p> 1004 * 1005 * <pre> 1006 * <selector xmlns:android="..."> 1007 * <item android:state_checked="true" 1008 * android:drawable="@drawable/vector_checked_icon" /> 1009 * <item android:drawable="@drawable/vector_icon" /> 1010 * </selector> 1011 * 1012 * <TextView 1013 * ... 1014 * android:drawableLeft="@drawable/vector_state_list_icon" /> 1015 * </pre> 1016 * 1017 * <p>This feature defaults to disabled, since enabling it can cause issues with memory usage, 1018 * and problems updating {@link Configuration} instances. If you update the configuration 1019 * manually, then you probably do not want to enable this. You have been warned.</p> 1020 * 1021 * <p>Even with this disabled, you can still use vector resources through 1022 * {@link androidx.appcompat.widget.AppCompatImageView#setImageResource(int)} and its 1023 * {@code app:srcCompat} attribute. They can also be used in anything which AppCompat inflates 1024 * for you, such as menu resources.</p> 1025 * 1026 * <p>Please note: this only takes effect in Activities created after this call.</p> 1027 */ setCompatVectorFromResourcesEnabled(boolean enabled)1028 public static void setCompatVectorFromResourcesEnabled(boolean enabled) { 1029 VectorEnabledTintResources.setCompatVectorFromResourcesEnabled(enabled); 1030 } 1031 1032 /** 1033 * Returns whether vector drawables on older platforms (< API 21) can be accessed from within 1034 * resources. 1035 * 1036 * @see #setCompatVectorFromResourcesEnabled(boolean) 1037 */ isCompatVectorFromResourcesEnabled()1038 public static boolean isCompatVectorFromResourcesEnabled() { 1039 return VectorEnabledTintResources.isCompatVectorFromResourcesEnabled(); 1040 } 1041 addActiveDelegate(@onNull AppCompatDelegate delegate)1042 static void addActiveDelegate(@NonNull AppCompatDelegate delegate) { 1043 synchronized (sActivityDelegatesLock) { 1044 // Remove any existing records pointing to the delegate. 1045 // There should not be any, but we'll make sure 1046 removeDelegateFromActives(delegate); 1047 // Add a new record to the set 1048 sActivityDelegates.add(new WeakReference<>(delegate)); 1049 } 1050 } 1051 removeActivityDelegate(@onNull AppCompatDelegate delegate)1052 static void removeActivityDelegate(@NonNull AppCompatDelegate delegate) { 1053 synchronized (sActivityDelegatesLock) { 1054 // Remove any WeakRef records pointing to the delegate in the set 1055 removeDelegateFromActives(delegate); 1056 } 1057 } 1058 1059 /** 1060 * Syncs app-specific locales from androidX to framework. This is used to maintain a smooth 1061 * transition for a device that updates from pre-T API versions to T. 1062 * 1063 * <p><b>NOTE:</b> This should only be called when auto-storage is opted-in. This method 1064 * uses the meta-data service provided during the opt-in and hence if the service is not found 1065 * this method will throw an error.</p> 1066 */ syncLocalesToFramework(Context context)1067 static void syncLocalesToFramework(Context context) { 1068 if (Build.VERSION.SDK_INT >= 33) { 1069 ComponentName app_locales_component = new ComponentName( 1070 context, APP_LOCALES_META_DATA_HOLDER_SERVICE_NAME); 1071 1072 if (context.getPackageManager().getComponentEnabledSetting(app_locales_component) 1073 != COMPONENT_ENABLED_STATE_ENABLED) { 1074 // ComponentEnabledSetting for the app component app_locales_component is used as a 1075 // marker to represent that the locales has been synced from AndroidX to framework 1076 // If this marker is found in ENABLED state then we do not need to sync again. 1077 if (AppCompatDelegate.getApplicationLocales().isEmpty()) { 1078 // We check if some locales are applied by the framework or not (this is done to 1079 // ensure that we don't overwrite newer locales set by the framework). If no 1080 // app-locales are found then we need to sync the app-specific locales from 1081 // androidX to framework. 1082 1083 String appLocales = AppLocalesStorageHelper.readLocales(context); 1084 // if locales are present in storage, call the setApplicationLocales() API. As 1085 // the API version is >= 33, this call will be directed to the framework API and 1086 // the locales will be persisted there. 1087 Object localeManager = context.getSystemService(Context.LOCALE_SERVICE); 1088 if (localeManager != null) { 1089 AppCompatDelegate.Api33Impl.localeManagerSetApplicationLocales( 1090 localeManager, 1091 AppCompatDelegate.Api24Impl.localeListForLanguageTags(appLocales)); 1092 } 1093 } 1094 // setting ComponentEnabledSetting for app component using 1095 // AppLocalesMetadataHolderService (used only for locales, thus minimizing 1096 // the chances of conflicts). Setting it as ENABLED marks the success of app-locales 1097 // sync from AndroidX to framework. 1098 // Flag DONT_KILL_APP indicates that you don't want to kill the app containing the 1099 // component. 1100 context.getPackageManager().setComponentEnabledSetting(app_locales_component, 1101 COMPONENT_ENABLED_STATE_ENABLED, /* flags= */ DONT_KILL_APP); 1102 } 1103 } 1104 } 1105 removeDelegateFromActives(@onNull AppCompatDelegate toRemove)1106 private static void removeDelegateFromActives(@NonNull AppCompatDelegate toRemove) { 1107 synchronized (sActivityDelegatesLock) { 1108 final Iterator<WeakReference<AppCompatDelegate>> i = sActivityDelegates.iterator(); 1109 while (i.hasNext()) { 1110 final AppCompatDelegate delegate = i.next().get(); 1111 if (delegate == toRemove || delegate == null) { 1112 // If the delegate is the one to remove, or it is null (because of the WeakRef), 1113 // remove it from the set 1114 i.remove(); 1115 } 1116 } 1117 } 1118 } 1119 applyDayNightToActiveDelegates()1120 private static void applyDayNightToActiveDelegates() { 1121 synchronized (sActivityDelegatesLock) { 1122 for (WeakReference<AppCompatDelegate> activeDelegate : sActivityDelegates) { 1123 final AppCompatDelegate delegate = activeDelegate.get(); 1124 if (delegate != null) { 1125 if (DEBUG) { 1126 Log.d(TAG, "applyDayNightToActiveDelegates. Applying to " + delegate); 1127 } 1128 delegate.applyDayNight(); 1129 } 1130 } 1131 } 1132 } 1133 applyLocalesToActiveDelegates()1134 private static void applyLocalesToActiveDelegates() { 1135 for (WeakReference<AppCompatDelegate> activeDelegate : sActivityDelegates) { 1136 final AppCompatDelegate delegate = activeDelegate.get(); 1137 if (delegate != null) { 1138 if (DEBUG) { 1139 Log.d(TAG, "applyLocalesToActiveDelegates. Applying to " + delegate); 1140 } 1141 delegate.applyAppLocales(); 1142 } 1143 } 1144 } 1145 1146 @RequiresApi(24) 1147 static class Api24Impl { Api24Impl()1148 private Api24Impl() { 1149 // This class is not instantiable. 1150 } 1151 localeListForLanguageTags(String list)1152 static LocaleList localeListForLanguageTags(String list) { 1153 return LocaleList.forLanguageTags(list); 1154 } 1155 } 1156 1157 @RequiresApi(33) 1158 static class Api33Impl { Api33Impl()1159 private Api33Impl() { 1160 // This class is not instantiable. 1161 } 1162 localeManagerSetApplicationLocales(Object localeManager, LocaleList locales)1163 static void localeManagerSetApplicationLocales(Object localeManager, 1164 LocaleList locales) { 1165 LocaleManager mLocaleManager = (LocaleManager) localeManager; 1166 mLocaleManager.setApplicationLocales(locales); 1167 } 1168 localeManagerGetApplicationLocales(Object localeManager)1169 static LocaleList localeManagerGetApplicationLocales(Object localeManager) { 1170 LocaleManager mLocaleManager = (LocaleManager) localeManager; 1171 return mLocaleManager.getApplicationLocales(); 1172 } 1173 } 1174 } 1175