Merge "Introduce ConfigurationController" into sc-dev
This commit is contained in:
@@ -17,6 +17,8 @@
|
||||
package android.app;
|
||||
|
||||
import static android.app.ActivityManager.PROCESS_STATE_UNKNOWN;
|
||||
import static android.app.ConfigurationController.createNewConfigAndUpdateIfNotNull;
|
||||
import static android.app.ConfigurationController.freeTextLayoutCachesIfNeeded;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
|
||||
import static android.app.servertransaction.ActivityLifecycleItem.ON_CREATE;
|
||||
@@ -79,7 +81,6 @@ import android.content.res.AssetManager;
|
||||
import android.content.res.CompatibilityInfo;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.Resources.Theme;
|
||||
import android.content.res.loader.ResourcesLoader;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteDebug;
|
||||
@@ -162,7 +163,6 @@ import android.util.SuperNotCalledException;
|
||||
import android.util.UtilConfig;
|
||||
import android.util.proto.ProtoOutputStream;
|
||||
import android.view.Choreographer;
|
||||
import android.view.ContextThemeWrapper;
|
||||
import android.view.Display;
|
||||
import android.view.DisplayAdjustments;
|
||||
import android.view.DisplayAdjustments.FixedRotationAdjustments;
|
||||
@@ -228,7 +228,6 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.TimeZone;
|
||||
@@ -251,7 +250,8 @@ final class RemoteServiceException extends AndroidRuntimeException {
|
||||
*
|
||||
* {@hide}
|
||||
*/
|
||||
public final class ActivityThread extends ClientTransactionHandler {
|
||||
public final class ActivityThread extends ClientTransactionHandler
|
||||
implements ActivityThreadInternal {
|
||||
/** @hide */
|
||||
public static final String TAG = "ActivityThread";
|
||||
private static final android.graphics.Bitmap.Config THUMBNAIL_FORMAT = Bitmap.Config.RGB_565;
|
||||
@@ -363,13 +363,15 @@ public final class ActivityThread extends ClientTransactionHandler {
|
||||
@UnsupportedAppUsage
|
||||
AppBindData mBoundApplication;
|
||||
Profiler mProfiler;
|
||||
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
|
||||
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553,
|
||||
publicAlternatives = "Use {@code Context#getResources()#getConfiguration()#densityDpi} "
|
||||
+ "instead.")
|
||||
int mCurDefaultDisplayDpi;
|
||||
@UnsupportedAppUsage
|
||||
boolean mDensityCompatMode;
|
||||
@UnsupportedAppUsage
|
||||
@UnsupportedAppUsage(trackingBug = 176961850, maxTargetSdk = Build.VERSION_CODES.R,
|
||||
publicAlternatives = "Use {@code Context#getResources()#getConfiguration()} instead.")
|
||||
Configuration mConfiguration;
|
||||
Configuration mCompatConfiguration;
|
||||
@UnsupportedAppUsage
|
||||
Application mInitialApplication;
|
||||
@UnsupportedAppUsage
|
||||
@@ -420,7 +422,8 @@ public final class ActivityThread extends ClientTransactionHandler {
|
||||
@GuardedBy("mResourcesManager")
|
||||
final ArrayList<ActivityClientRecord> mRelaunchingActivities = new ArrayList<>();
|
||||
@GuardedBy("mResourcesManager")
|
||||
@UnsupportedAppUsage
|
||||
@UnsupportedAppUsage(trackingBug = 176961850, maxTargetSdk = Build.VERSION_CODES.R,
|
||||
publicAlternatives = "Use {@code Context#getResources()#getConfiguration()} instead.")
|
||||
Configuration mPendingConfiguration = null;
|
||||
// An executor that performs multi-step transactions.
|
||||
private final TransactionExecutor mTransactionExecutor = new TransactionExecutor(this);
|
||||
@@ -516,6 +519,9 @@ public final class ActivityThread extends ClientTransactionHandler {
|
||||
|
||||
private IContentCaptureOptionsCallback.Stub mContentCaptureOptionsCallback = null;
|
||||
|
||||
/** A client side controller to handle process level configuration changes. */
|
||||
private ConfigurationController mConfigurationController;
|
||||
|
||||
/** Activity client record, used for bookkeeping for the real {@link Activity} instance. */
|
||||
public static final class ActivityClientRecord {
|
||||
@UnsupportedAppUsage
|
||||
@@ -2058,7 +2064,7 @@ public final class ActivityThread extends ClientTransactionHandler {
|
||||
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
|
||||
break;
|
||||
case CONFIGURATION_CHANGED:
|
||||
handleConfigurationChanged((Configuration) msg.obj);
|
||||
mConfigurationController.handleConfigurationChanged((Configuration) msg.obj);
|
||||
break;
|
||||
case CLEAN_UP_CONTEXT:
|
||||
ContextCleanupInfo cci = (ContextCleanupInfo)msg.obj;
|
||||
@@ -2539,6 +2545,7 @@ public final class ActivityThread extends ClientTransactionHandler {
|
||||
return mExecutor;
|
||||
}
|
||||
|
||||
@Override
|
||||
@UnsupportedAppUsage
|
||||
public Application getApplication() {
|
||||
return mInitialApplication;
|
||||
@@ -2549,6 +2556,7 @@ public final class ActivityThread extends ClientTransactionHandler {
|
||||
return mBoundApplication.processName;
|
||||
}
|
||||
|
||||
@Override
|
||||
@UnsupportedAppUsage
|
||||
public ContextImpl getSystemContext() {
|
||||
synchronized (this) {
|
||||
@@ -2559,6 +2567,7 @@ public final class ActivityThread extends ClientTransactionHandler {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContextImpl getSystemUiContext() {
|
||||
synchronized (this) {
|
||||
if (mSystemUiContext == null) {
|
||||
@@ -3217,15 +3226,16 @@ public final class ActivityThread extends ClientTransactionHandler {
|
||||
|
||||
@VisibleForTesting(visibility = PACKAGE)
|
||||
public Configuration getConfiguration() {
|
||||
return mConfiguration;
|
||||
return mConfigurationController.getConfiguration();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updatePendingConfiguration(Configuration config) {
|
||||
synchronized (mResourcesManager) {
|
||||
if (mPendingConfiguration == null || mPendingConfiguration.isOtherSeqNewer(config)) {
|
||||
mPendingConfiguration = config;
|
||||
}
|
||||
final Configuration updatedConfig =
|
||||
mConfigurationController.updatePendingConfiguration(config);
|
||||
// This is only done to maintain @UnsupportedAppUsage and should be removed someday.
|
||||
if (updatedConfig != null) {
|
||||
mPendingConfiguration = updatedConfig;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3233,6 +3243,7 @@ public final class ActivityThread extends ClientTransactionHandler {
|
||||
* Returns {@code true} if the {@link android.app.ActivityManager.ProcessState} of the current
|
||||
* process is cached.
|
||||
*/
|
||||
@Override
|
||||
@VisibleForTesting
|
||||
public boolean isCachedProcessState() {
|
||||
synchronized (mAppThread) {
|
||||
@@ -3269,10 +3280,8 @@ public final class ActivityThread extends ClientTransactionHandler {
|
||||
// non-cached. Except the case where there is a launching activity because the
|
||||
// LaunchActivityItem will handle it.
|
||||
if (wasCached && !isCachedProcessState() && mNumLaunchingActivities.get() == 0) {
|
||||
final Configuration pendingConfig;
|
||||
synchronized (mResourcesManager) {
|
||||
pendingConfig = mPendingConfiguration;
|
||||
}
|
||||
final Configuration pendingConfig =
|
||||
mConfigurationController.getPendingConfiguration(false /* clearPending */);
|
||||
if (pendingConfig == null) {
|
||||
return;
|
||||
}
|
||||
@@ -3498,7 +3507,8 @@ public final class ActivityThread extends ClientTransactionHandler {
|
||||
|
||||
if (activity != null) {
|
||||
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
|
||||
Configuration config = new Configuration(mCompatConfiguration);
|
||||
Configuration config =
|
||||
new Configuration(mConfigurationController.getCompatConfiguration());
|
||||
if (r.overrideConfig != null) {
|
||||
config.updateFrom(r.overrideConfig);
|
||||
}
|
||||
@@ -3711,7 +3721,7 @@ public final class ActivityThread extends ClientTransactionHandler {
|
||||
}
|
||||
|
||||
// Make sure we are running with the most recent config.
|
||||
handleConfigurationChanged(null, null);
|
||||
mConfigurationController.handleConfigurationChanged(null, null);
|
||||
|
||||
if (localLOGV) Slog.v(
|
||||
TAG, "Handling launch of " + r);
|
||||
@@ -3729,7 +3739,7 @@ public final class ActivityThread extends ClientTransactionHandler {
|
||||
final Activity a = performLaunchActivity(r, customIntent);
|
||||
|
||||
if (a != null) {
|
||||
r.createdConfig = new Configuration(mConfiguration);
|
||||
r.createdConfig = new Configuration(mConfigurationController.getConfiguration());
|
||||
reportSizeConfigurations(r);
|
||||
if (!r.activity.mFinished && pendingActions != null) {
|
||||
pendingActions.setOldState(r.state);
|
||||
@@ -4962,10 +4972,10 @@ public final class ActivityThread extends ClientTransactionHandler {
|
||||
r.setState(ON_PAUSE);
|
||||
}
|
||||
|
||||
// TODO(b/127877792): Make LocalActivityManager call performStopActivityInner. We cannot remove
|
||||
// TODO(b/176961850): Make LocalActivityManager call performStopActivityInner. We cannot remove
|
||||
// this since it's a high usage hidden API.
|
||||
/** Called from {@link LocalActivityManager}. */
|
||||
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 127877792,
|
||||
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 176961850,
|
||||
publicAlternatives = "{@code N/A}")
|
||||
final void performStopActivity(IBinder token, boolean saveState, String reason) {
|
||||
ActivityClientRecord r = mActivities.get(token);
|
||||
@@ -5193,8 +5203,7 @@ public final class ActivityThread extends ClientTransactionHandler {
|
||||
if (apk != null) {
|
||||
apk.setCompatibilityInfo(data.info);
|
||||
}
|
||||
handleConfigurationChanged(mConfiguration, data.info);
|
||||
WindowManagerGlobal.getInstance().reportNewConfiguration(mConfiguration);
|
||||
mConfigurationController.handleConfigurationChanged(data.info);
|
||||
}
|
||||
|
||||
private void deliverResults(ActivityClientRecord r, List<ResultInfo> results, String reason) {
|
||||
@@ -5451,7 +5460,6 @@ public final class ActivityThread extends ClientTransactionHandler {
|
||||
unscheduleGcIdler();
|
||||
mSomeActivitiesChanged = true;
|
||||
|
||||
Configuration changedConfig = null;
|
||||
int configChanges = 0;
|
||||
|
||||
// First: make sure we have the most recent configuration and most
|
||||
@@ -5480,20 +5488,20 @@ public final class ActivityThread extends ClientTransactionHandler {
|
||||
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Relaunching activity "
|
||||
+ tmp.token + " with configChanges=0x"
|
||||
+ Integer.toHexString(configChanges));
|
||||
|
||||
if (mPendingConfiguration != null) {
|
||||
changedConfig = mPendingConfiguration;
|
||||
mPendingConfiguration = null;
|
||||
}
|
||||
}
|
||||
|
||||
Configuration changedConfig = mConfigurationController.getPendingConfiguration(
|
||||
true /* clearPending */);
|
||||
mPendingConfiguration = null;
|
||||
|
||||
if (tmp.createdConfig != null) {
|
||||
// If the activity manager is passing us its current config,
|
||||
// assume that is really what we want regardless of what we
|
||||
// may have pending.
|
||||
if (mConfiguration == null
|
||||
|| (tmp.createdConfig.isOtherSeqNewer(mConfiguration)
|
||||
&& mConfiguration.diff(tmp.createdConfig) != 0)) {
|
||||
final Configuration config = mConfigurationController.getConfiguration();
|
||||
if (config == null
|
||||
|| (tmp.createdConfig.isOtherSeqNewer(config)
|
||||
&& config.diff(tmp.createdConfig) != 0)) {
|
||||
if (changedConfig == null
|
||||
|| tmp.createdConfig.isOtherSeqNewer(changedConfig)) {
|
||||
changedConfig = tmp.createdConfig;
|
||||
@@ -5506,9 +5514,12 @@ public final class ActivityThread extends ClientTransactionHandler {
|
||||
|
||||
// If there was a pending configuration change, execute it first.
|
||||
if (changedConfig != null) {
|
||||
mCurDefaultDisplayDpi = changedConfig.densityDpi;
|
||||
updateDefaultDensity();
|
||||
handleConfigurationChanged(changedConfig, null);
|
||||
mConfigurationController.updateDefaultDensity(changedConfig.densityDpi);
|
||||
mConfigurationController.handleConfigurationChanged(changedConfig, null);
|
||||
|
||||
// These are only done to maintain @UnsupportedAppUsage and should be removed someday.
|
||||
mCurDefaultDisplayDpi = mConfigurationController.getCurDefaultDisplayDpi();
|
||||
mConfiguration = mConfigurationController.getConfiguration();
|
||||
}
|
||||
|
||||
ActivityClientRecord r = mActivities.get(tmp.token);
|
||||
@@ -5596,7 +5607,8 @@ public final class ActivityThread extends ClientTransactionHandler {
|
||||
|
||||
// Initialize a relaunch request.
|
||||
final MergedConfiguration mergedConfiguration = new MergedConfiguration(
|
||||
r.createdConfig != null ? r.createdConfig : mConfiguration,
|
||||
r.createdConfig != null
|
||||
? r.createdConfig : mConfigurationController.getConfiguration(),
|
||||
r.overrideConfig);
|
||||
final ActivityRelaunchItem activityRelaunchItem = ActivityRelaunchItem.obtain(
|
||||
null /* pendingResults */, null /* pendingIntents */, 0 /* configChanges */,
|
||||
@@ -5672,7 +5684,8 @@ public final class ActivityThread extends ClientTransactionHandler {
|
||||
}
|
||||
}
|
||||
|
||||
ArrayList<ComponentCallbacks2> collectComponentCallbacks(boolean includeActivities) {
|
||||
@Override
|
||||
public ArrayList<ComponentCallbacks2> collectComponentCallbacks(boolean includeActivities) {
|
||||
ArrayList<ComponentCallbacks2> callbacks
|
||||
= new ArrayList<ComponentCallbacks2>();
|
||||
|
||||
@@ -5731,44 +5744,6 @@ public final class ActivityThread extends ClientTransactionHandler {
|
||||
return reportedConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Configuration only if override would modify base. Otherwise returns base.
|
||||
* @param base The base configuration.
|
||||
* @param override The update to apply to the base configuration. Can be null.
|
||||
* @return A Configuration representing base with override applied.
|
||||
*/
|
||||
private static Configuration createNewConfigAndUpdateIfNotNull(@NonNull Configuration base,
|
||||
@Nullable Configuration override) {
|
||||
if (override == null) {
|
||||
return base;
|
||||
}
|
||||
Configuration newConfig = new Configuration(base);
|
||||
newConfig.updateFrom(override);
|
||||
return newConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decides whether to update a component's configuration and whether to inform it.
|
||||
* @param cb The component callback to notify of configuration change.
|
||||
* @param newConfig The new configuration.
|
||||
*/
|
||||
private void performConfigurationChanged(ComponentCallbacks2 cb, Configuration newConfig) {
|
||||
// ContextThemeWrappers may override the configuration for that context. We must check and
|
||||
// apply any overrides defined.
|
||||
Configuration contextThemeWrapperOverrideConfig = null;
|
||||
if (cb instanceof ContextThemeWrapper) {
|
||||
final ContextThemeWrapper contextThemeWrapper = (ContextThemeWrapper) cb;
|
||||
contextThemeWrapperOverrideConfig = contextThemeWrapper.getOverrideConfiguration();
|
||||
}
|
||||
|
||||
// Apply the ContextThemeWrapper override if necessary.
|
||||
// NOTE: Make sure the configurations are not modified, as they are treated as immutable
|
||||
// in many places.
|
||||
final Configuration configToReport = createNewConfigAndUpdateIfNotNull(
|
||||
newConfig, contextThemeWrapperOverrideConfig);
|
||||
cb.onConfigurationChanged(configToReport);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decides whether to update an Activity's configuration and whether to inform it.
|
||||
* @param activity The activity to notify of configuration change.
|
||||
@@ -5887,128 +5862,15 @@ public final class ActivityThread extends ClientTransactionHandler {
|
||||
}
|
||||
}
|
||||
|
||||
final Configuration applyCompatConfiguration(int displayDensity) {
|
||||
Configuration config = mConfiguration;
|
||||
if (mCompatConfiguration == null) {
|
||||
mCompatConfiguration = new Configuration();
|
||||
}
|
||||
mCompatConfiguration.setTo(mConfiguration);
|
||||
if (mResourcesManager.applyCompatConfigurationLocked(displayDensity,
|
||||
mCompatConfiguration)) {
|
||||
config = mCompatConfiguration;
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleConfigurationChanged(Configuration config) {
|
||||
if (isCachedProcessState()) {
|
||||
updatePendingConfiguration(config);
|
||||
// If the process is in a cached state, delay the handling until the process is no
|
||||
// longer cached.
|
||||
return;
|
||||
}
|
||||
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "configChanged");
|
||||
mCurDefaultDisplayDpi = config.densityDpi;
|
||||
handleConfigurationChanged(config, null /* compat */);
|
||||
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
|
||||
}
|
||||
mConfigurationController.handleConfigurationChanged(config);
|
||||
|
||||
private void handleConfigurationChanged(Configuration config, CompatibilityInfo compat) {
|
||||
|
||||
int configDiff;
|
||||
boolean equivalent;
|
||||
|
||||
final Theme systemTheme = getSystemContext().getTheme();
|
||||
final Theme systemUiTheme = getSystemUiContext().getTheme();
|
||||
|
||||
synchronized (mResourcesManager) {
|
||||
if (mPendingConfiguration != null) {
|
||||
if (!mPendingConfiguration.isOtherSeqNewer(config)) {
|
||||
config = mPendingConfiguration;
|
||||
mCurDefaultDisplayDpi = config.densityDpi;
|
||||
updateDefaultDensity();
|
||||
}
|
||||
mPendingConfiguration = null;
|
||||
}
|
||||
|
||||
if (config == null) {
|
||||
// TODO (b/135719017): Temporary log for debugging IME service.
|
||||
if (Build.IS_DEBUGGABLE && mHasImeComponent) {
|
||||
Log.w(TAG, "handleConfigurationChanged for IME app but config is null");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// This flag tracks whether the new configuration is fundamentally equivalent to the
|
||||
// existing configuration. This is necessary to determine whether non-activity callbacks
|
||||
// should receive notice when the only changes are related to non-public fields.
|
||||
// We do not gate calling {@link #performActivityConfigurationChanged} based on this
|
||||
// flag as that method uses the same check on the activity config override as well.
|
||||
equivalent = mConfiguration != null && (0 == mConfiguration.diffPublicOnly(config));
|
||||
|
||||
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Handle configuration changed: "
|
||||
+ config);
|
||||
|
||||
final Resources appResources = mInitialApplication.getResources();
|
||||
if (appResources.hasOverrideDisplayAdjustments()) {
|
||||
// The value of Display#getRealSize will be adjusted by FixedRotationAdjustments,
|
||||
// but Display#getSize refers to DisplayAdjustments#mConfiguration. So the rotated
|
||||
// configuration also needs to set to the adjustments for consistency.
|
||||
appResources.getDisplayAdjustments().getConfiguration().updateFrom(config);
|
||||
}
|
||||
mResourcesManager.applyConfigurationToResourcesLocked(config, compat,
|
||||
appResources.getDisplayAdjustments());
|
||||
updateLocaleListFromAppContext(mInitialApplication.getApplicationContext(),
|
||||
mResourcesManager.getConfiguration().getLocales());
|
||||
|
||||
if (mConfiguration == null) {
|
||||
mConfiguration = new Configuration();
|
||||
}
|
||||
if (!mConfiguration.isOtherSeqNewer(config) && compat == null) {
|
||||
// TODO (b/135719017): Temporary log for debugging IME service.
|
||||
if (Build.IS_DEBUGGABLE && mHasImeComponent) {
|
||||
Log.w(TAG, "handleConfigurationChanged for IME app but config seq is obsolete "
|
||||
+ ", config=" + config
|
||||
+ ", mConfiguration=" + mConfiguration);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
configDiff = mConfiguration.updateFrom(config);
|
||||
config = applyCompatConfiguration(mCurDefaultDisplayDpi);
|
||||
HardwareRenderer.sendDeviceConfigurationForDebugging(config);
|
||||
|
||||
if ((systemTheme.getChangingConfigurations() & configDiff) != 0) {
|
||||
systemTheme.rebase();
|
||||
}
|
||||
|
||||
if ((systemUiTheme.getChangingConfigurations() & configDiff) != 0) {
|
||||
systemUiTheme.rebase();
|
||||
}
|
||||
}
|
||||
|
||||
final ArrayList<ComponentCallbacks2> callbacks =
|
||||
collectComponentCallbacks(false /* includeActivities */);
|
||||
|
||||
freeTextLayoutCachesIfNeeded(configDiff);
|
||||
|
||||
if (callbacks != null) {
|
||||
final int N = callbacks.size();
|
||||
for (int i=0; i<N; i++) {
|
||||
ComponentCallbacks2 cb = callbacks.get(i);
|
||||
if (!equivalent) {
|
||||
performConfigurationChanged(cb, config);
|
||||
} else {
|
||||
// TODO (b/135719017): Temporary log for debugging IME service.
|
||||
if (Build.IS_DEBUGGABLE && cb instanceof InputMethodService) {
|
||||
Log.w(TAG, "performConfigurationChanged didn't callback to IME "
|
||||
+ ", configDiff=" + configDiff
|
||||
+ ", mConfiguration=" + mConfiguration);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// These are only done to maintain @UnsupportedAppUsage and should be removed someday.
|
||||
mCurDefaultDisplayDpi = mConfigurationController.getCurDefaultDisplayDpi();
|
||||
mConfiguration = mConfigurationController.getConfiguration();
|
||||
mPendingConfiguration = mConfigurationController.getPendingConfiguration(
|
||||
false /* clearPending */);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -6093,25 +5955,15 @@ public final class ActivityThread extends ClientTransactionHandler {
|
||||
// so that we actually call through to all components.
|
||||
// TODO(adamlesinski): Change this to make use of ActivityManager's upcoming ability to
|
||||
// store configurations per-process.
|
||||
final Configuration config = mConfigurationController.getConfiguration();
|
||||
Configuration newConfig = new Configuration();
|
||||
newConfig.assetsSeq = (mConfiguration != null ? mConfiguration.assetsSeq : 0) + 1;
|
||||
handleConfigurationChanged(newConfig, null);
|
||||
newConfig.assetsSeq = (config != null ? config.assetsSeq : 0) + 1;
|
||||
mConfigurationController.handleConfigurationChanged(newConfig, null /* compat */);
|
||||
|
||||
// Preserve windows to avoid black flickers when overlays change.
|
||||
relaunchAllActivities(true /* preserveWindows */, "handleApplicationInfoChanged");
|
||||
}
|
||||
|
||||
static void freeTextLayoutCachesIfNeeded(int configDiff) {
|
||||
if (configDiff != 0) {
|
||||
// Ask text layout engine to free its caches if there is a locale change
|
||||
boolean hasLocaleConfigChange = ((configDiff & ActivityInfo.CONFIG_LOCALE) != 0);
|
||||
if (hasLocaleConfigChange) {
|
||||
Canvas.freeTextLayoutCaches();
|
||||
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Cleared TextLayout Caches");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the supplied {@code overrideConfig} as pending for the {@code activityToken}. Calling
|
||||
* this method prevents any calls to
|
||||
@@ -6188,7 +6040,7 @@ public final class ActivityThread extends ClientTransactionHandler {
|
||||
+ ", config=" + overrideConfig);
|
||||
}
|
||||
final Configuration reportedConfig = performConfigurationChangedForActivity(r,
|
||||
mCompatConfiguration,
|
||||
mConfigurationController.getCompatConfiguration(),
|
||||
movedToDifferentDisplay ? displayId : r.activity.getDisplayId());
|
||||
// Notify the ViewRootImpl instance about configuration changes. It may have initiated this
|
||||
// update to make sure that resources are updated before updating itself.
|
||||
@@ -6483,16 +6335,6 @@ public final class ActivityThread extends ClientTransactionHandler {
|
||||
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
|
||||
}
|
||||
|
||||
private void updateDefaultDensity() {
|
||||
final int densityDpi = mCurDefaultDisplayDpi;
|
||||
if (!mDensityCompatMode
|
||||
&& densityDpi != Configuration.DENSITY_DPI_UNDEFINED
|
||||
&& densityDpi != DisplayMetrics.DENSITY_DEVICE) {
|
||||
DisplayMetrics.DENSITY_DEVICE = densityDpi;
|
||||
Bitmap.setDefaultDensity(densityDpi);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the correct library directory for the current ABI.
|
||||
* <p>
|
||||
@@ -6519,27 +6361,6 @@ public final class ActivityThread extends ClientTransactionHandler {
|
||||
return insInfo.nativeLibraryDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* The LocaleList set for the app's resources may have been shuffled so that the preferred
|
||||
* Locale is at position 0. We must find the index of this preferred Locale in the
|
||||
* original LocaleList.
|
||||
*/
|
||||
private void updateLocaleListFromAppContext(Context context, LocaleList newLocaleList) {
|
||||
final Locale bestLocale = context.getResources().getConfiguration().getLocales().get(0);
|
||||
final int newLocaleListSize = newLocaleList.size();
|
||||
for (int i = 0; i < newLocaleListSize; i++) {
|
||||
if (bestLocale.equals(newLocaleList.get(i))) {
|
||||
LocaleList.setDefault(newLocaleList, i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// The app may have overridden the LocaleList with its own Locale
|
||||
// (not present in the available list). Push the chosen Locale
|
||||
// to the front of the list.
|
||||
LocaleList.setDefault(new LocaleList(bestLocale, newLocaleList));
|
||||
}
|
||||
|
||||
@UnsupportedAppUsage
|
||||
private void handleBindApplication(AppBindData data) {
|
||||
// Register the UI Thread as a sensitive thread to the runtime.
|
||||
@@ -6560,8 +6381,9 @@ public final class ActivityThread extends ClientTransactionHandler {
|
||||
AppSpecializationHooks.handleCompatChangesBeforeBindingApplication();
|
||||
|
||||
mBoundApplication = data;
|
||||
mConfiguration = new Configuration(data.config);
|
||||
mCompatConfiguration = new Configuration(data.config);
|
||||
mConfigurationController.setConfiguration(data.config);
|
||||
mConfigurationController.setCompatConfiguration(data.config);
|
||||
mConfiguration = mConfigurationController.getConfiguration();
|
||||
|
||||
mProfiler = new Profiler();
|
||||
String agent = null;
|
||||
@@ -6641,7 +6463,7 @@ public final class ActivityThread extends ClientTransactionHandler {
|
||||
mCurDefaultDisplayDpi = data.config.densityDpi;
|
||||
|
||||
// This calls mResourcesManager so keep it within the synchronized block.
|
||||
applyCompatConfiguration(mCurDefaultDisplayDpi);
|
||||
mConfigurationController.applyCompatConfiguration();
|
||||
}
|
||||
|
||||
data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
|
||||
@@ -6658,7 +6480,7 @@ public final class ActivityThread extends ClientTransactionHandler {
|
||||
mDensityCompatMode = true;
|
||||
Bitmap.setDefaultDensity(DisplayMetrics.DENSITY_DEFAULT);
|
||||
}
|
||||
updateDefaultDensity();
|
||||
mConfigurationController.updateDefaultDensity(data.config.densityDpi);
|
||||
|
||||
// mCoreSettings is only updated from the main thread, while this function is only called
|
||||
// from main thread as well, so no need to lock here.
|
||||
@@ -6735,8 +6557,7 @@ public final class ActivityThread extends ClientTransactionHandler {
|
||||
}
|
||||
|
||||
final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
|
||||
updateLocaleListFromAppContext(appContext,
|
||||
mResourcesManager.getConfiguration().getLocales());
|
||||
mConfigurationController.updateLocaleListFromAppContext(appContext);
|
||||
|
||||
// Initialize the default http proxy in this process.
|
||||
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Setup proxies");
|
||||
@@ -7611,6 +7432,7 @@ public final class ActivityThread extends ClientTransactionHandler {
|
||||
@UnsupportedAppUsage
|
||||
private void attach(boolean system, long startSeq) {
|
||||
sCurrentActivityThread = this;
|
||||
mConfigurationController = new ConfigurationController(this);
|
||||
mSystemThread = system;
|
||||
if (!system) {
|
||||
android.ddm.DdmHandleAppName.setAppName("<pre-initialized>",
|
||||
@@ -7662,8 +7484,7 @@ public final class ActivityThread extends ClientTransactionHandler {
|
||||
}
|
||||
}
|
||||
|
||||
ViewRootImpl.ConfigChangedCallback configChangedCallback
|
||||
= (Configuration globalConfig) -> {
|
||||
ViewRootImpl.ConfigChangedCallback configChangedCallback = (Configuration globalConfig) -> {
|
||||
synchronized (mResourcesManager) {
|
||||
// TODO (b/135719017): Temporary log for debugging IME service.
|
||||
if (Build.IS_DEBUGGABLE && mHasImeComponent) {
|
||||
@@ -7676,14 +7497,15 @@ public final class ActivityThread extends ClientTransactionHandler {
|
||||
if (mResourcesManager.applyConfigurationToResourcesLocked(globalConfig,
|
||||
null /* compat */,
|
||||
mInitialApplication.getResources().getDisplayAdjustments())) {
|
||||
updateLocaleListFromAppContext(mInitialApplication.getApplicationContext(),
|
||||
mResourcesManager.getConfiguration().getLocales());
|
||||
mConfigurationController.updateLocaleListFromAppContext(
|
||||
mInitialApplication.getApplicationContext());
|
||||
|
||||
// This actually changed the resources! Tell everyone about it.
|
||||
if (mPendingConfiguration == null
|
||||
|| mPendingConfiguration.isOtherSeqNewer(globalConfig)) {
|
||||
mPendingConfiguration = globalConfig;
|
||||
final Configuration updatedConfig =
|
||||
mConfigurationController.updatePendingConfiguration(globalConfig);
|
||||
if (updatedConfig != null) {
|
||||
sendMessage(H.CONFIGURATION_CHANGED, globalConfig);
|
||||
mPendingConfiguration = updatedConfig;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8019,6 +7841,16 @@ public final class ActivityThread extends ClientTransactionHandler {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInDensityCompatMode() {
|
||||
return mDensityCompatMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasImeComponent() {
|
||||
return mHasImeComponent;
|
||||
}
|
||||
|
||||
// ------------------ Regular JNI ------------------------
|
||||
private native void nPurgePendingResources();
|
||||
private native void nDumpGraphicsInfo(FileDescriptor fd);
|
||||
|
||||
42
core/java/android/app/ActivityThreadInternal.java
Normal file
42
core/java/android/app/ActivityThreadInternal.java
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.app;
|
||||
|
||||
import android.content.ComponentCallbacks2;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* ActivityThread internal interface.
|
||||
* It is a subset of ActivityThread and used for communicating with
|
||||
* {@link ConfigurationController}.
|
||||
*/
|
||||
interface ActivityThreadInternal {
|
||||
ContextImpl getSystemContext();
|
||||
|
||||
ContextImpl getSystemUiContext();
|
||||
|
||||
boolean isInDensityCompatMode();
|
||||
|
||||
boolean hasImeComponent();
|
||||
|
||||
boolean isCachedProcessState();
|
||||
|
||||
Application getApplication();
|
||||
|
||||
ArrayList<ComponentCallbacks2> collectComponentCallbacks(boolean includeActivities);
|
||||
}
|
||||
343
core/java/android/app/ConfigurationController.java
Normal file
343
core/java/android/app/ConfigurationController.java
Normal file
@@ -0,0 +1,343 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.app;
|
||||
|
||||
import static android.app.ActivityThread.DEBUG_CONFIGURATION;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.ComponentCallbacks2;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.res.CompatibilityInfo;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.HardwareRenderer;
|
||||
import android.inputmethodservice.InputMethodService;
|
||||
import android.os.Build;
|
||||
import android.os.LocaleList;
|
||||
import android.os.Trace;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.util.Slog;
|
||||
import android.view.ContextThemeWrapper;
|
||||
import android.view.WindowManagerGlobal;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* A client side controller to handle process level configuration changes.
|
||||
* @hide
|
||||
*/
|
||||
class ConfigurationController {
|
||||
private static final String TAG = "ConfigurationController";
|
||||
|
||||
private final ActivityThreadInternal mActivityThread;
|
||||
|
||||
private final ResourcesManager mResourcesManager = ResourcesManager.getInstance();
|
||||
|
||||
@GuardedBy("mResourcesManager")
|
||||
private @Nullable Configuration mPendingConfiguration;
|
||||
private @Nullable Configuration mCompatConfiguration;
|
||||
private @Nullable Configuration mConfiguration;
|
||||
|
||||
ConfigurationController(@NonNull ActivityThreadInternal activityThread) {
|
||||
mActivityThread = activityThread;
|
||||
}
|
||||
|
||||
/** Update the pending configuration. */
|
||||
Configuration updatePendingConfiguration(@NonNull Configuration config) {
|
||||
synchronized (mResourcesManager) {
|
||||
if (mPendingConfiguration == null || mPendingConfiguration.isOtherSeqNewer(config)) {
|
||||
mPendingConfiguration = config;
|
||||
return mPendingConfiguration;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Get the pending configuration. */
|
||||
Configuration getPendingConfiguration(boolean clearPending) {
|
||||
Configuration outConfig = null;
|
||||
synchronized (mResourcesManager) {
|
||||
if (mPendingConfiguration != null) {
|
||||
outConfig = mPendingConfiguration;
|
||||
if (clearPending) {
|
||||
mPendingConfiguration = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
return outConfig;
|
||||
}
|
||||
|
||||
/** Set the compatibility configuration. */
|
||||
void setCompatConfiguration(@NonNull Configuration config) {
|
||||
mCompatConfiguration = new Configuration(config);
|
||||
}
|
||||
|
||||
/** Get the compatibility configuration. */
|
||||
Configuration getCompatConfiguration() {
|
||||
return mCompatConfiguration;
|
||||
}
|
||||
|
||||
/** Apply the global compatibility configuration. */
|
||||
final Configuration applyCompatConfiguration() {
|
||||
Configuration config = mConfiguration;
|
||||
final int displayDensity = config.densityDpi;
|
||||
if (mCompatConfiguration == null) {
|
||||
mCompatConfiguration = new Configuration();
|
||||
}
|
||||
mCompatConfiguration.setTo(mConfiguration);
|
||||
if (mResourcesManager.applyCompatConfigurationLocked(displayDensity,
|
||||
mCompatConfiguration)) {
|
||||
config = mCompatConfiguration;
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
/** Set the configuration. */
|
||||
void setConfiguration(@NonNull Configuration config) {
|
||||
mConfiguration = new Configuration(config);
|
||||
}
|
||||
|
||||
/** Get current configuration. */
|
||||
Configuration getConfiguration() {
|
||||
return mConfiguration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the configuration to latest.
|
||||
* @param config The new configuration.
|
||||
*/
|
||||
void handleConfigurationChanged(@NonNull Configuration config) {
|
||||
if (mActivityThread.isCachedProcessState()) {
|
||||
updatePendingConfiguration(config);
|
||||
// If the process is in a cached state, delay the handling until the process is no
|
||||
// longer cached.
|
||||
return;
|
||||
}
|
||||
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "configChanged");
|
||||
handleConfigurationChanged(config, null /* compat */);
|
||||
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the configuration to latest.
|
||||
* @param compat The new compatibility information.
|
||||
*/
|
||||
void handleConfigurationChanged(@NonNull CompatibilityInfo compat) {
|
||||
handleConfigurationChanged(mConfiguration, compat);
|
||||
WindowManagerGlobal.getInstance().reportNewConfiguration(mConfiguration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the configuration to latest.
|
||||
* @param config The new configuration.
|
||||
* @param compat The new compatibility information.
|
||||
*/
|
||||
void handleConfigurationChanged(@Nullable Configuration config,
|
||||
@Nullable CompatibilityInfo compat) {
|
||||
int configDiff;
|
||||
boolean equivalent;
|
||||
|
||||
final Resources.Theme systemTheme = mActivityThread.getSystemContext().getTheme();
|
||||
final Resources.Theme systemUiTheme = mActivityThread.getSystemUiContext().getTheme();
|
||||
|
||||
synchronized (mResourcesManager) {
|
||||
if (mPendingConfiguration != null) {
|
||||
if (!mPendingConfiguration.isOtherSeqNewer(config)) {
|
||||
config = mPendingConfiguration;
|
||||
updateDefaultDensity(config.densityDpi);
|
||||
}
|
||||
mPendingConfiguration = null;
|
||||
}
|
||||
|
||||
final boolean hasIme = mActivityThread.hasImeComponent();
|
||||
if (config == null) {
|
||||
// TODO (b/135719017): Temporary log for debugging IME service.
|
||||
if (Build.IS_DEBUGGABLE && hasIme) {
|
||||
Log.w(TAG, "handleConfigurationChanged for IME app but config is null");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// This flag tracks whether the new configuration is fundamentally equivalent to the
|
||||
// existing configuration. This is necessary to determine whether non-activity callbacks
|
||||
// should receive notice when the only changes are related to non-public fields.
|
||||
// We do not gate calling {@link #performActivityConfigurationChanged} based on this
|
||||
// flag as that method uses the same check on the activity config override as well.
|
||||
equivalent = mConfiguration != null && (0 == mConfiguration.diffPublicOnly(config));
|
||||
|
||||
if (DEBUG_CONFIGURATION) {
|
||||
Slog.v(TAG, "Handle configuration changed: " + config);
|
||||
}
|
||||
|
||||
final Application app = mActivityThread.getApplication();
|
||||
final Resources appResources = app.getResources();
|
||||
if (appResources.hasOverrideDisplayAdjustments()) {
|
||||
// The value of Display#getRealSize will be adjusted by FixedRotationAdjustments,
|
||||
// but Display#getSize refers to DisplayAdjustments#mConfiguration. So the rotated
|
||||
// configuration also needs to set to the adjustments for consistency.
|
||||
appResources.getDisplayAdjustments().getConfiguration().updateFrom(config);
|
||||
}
|
||||
mResourcesManager.applyConfigurationToResourcesLocked(config, compat,
|
||||
appResources.getDisplayAdjustments());
|
||||
updateLocaleListFromAppContext(app.getApplicationContext());
|
||||
|
||||
if (mConfiguration == null) {
|
||||
mConfiguration = new Configuration();
|
||||
}
|
||||
if (!mConfiguration.isOtherSeqNewer(config) && compat == null) {
|
||||
// TODO (b/135719017): Temporary log for debugging IME service.
|
||||
if (Build.IS_DEBUGGABLE && hasIme) {
|
||||
Log.w(TAG, "handleConfigurationChanged for IME app but config seq is obsolete "
|
||||
+ ", config=" + config
|
||||
+ ", mConfiguration=" + mConfiguration);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
configDiff = mConfiguration.updateFrom(config);
|
||||
config = applyCompatConfiguration();
|
||||
HardwareRenderer.sendDeviceConfigurationForDebugging(config);
|
||||
|
||||
if ((systemTheme.getChangingConfigurations() & configDiff) != 0) {
|
||||
systemTheme.rebase();
|
||||
}
|
||||
|
||||
if ((systemUiTheme.getChangingConfigurations() & configDiff) != 0) {
|
||||
systemUiTheme.rebase();
|
||||
}
|
||||
}
|
||||
|
||||
final ArrayList<ComponentCallbacks2> callbacks =
|
||||
mActivityThread.collectComponentCallbacks(false /* includeActivities */);
|
||||
|
||||
freeTextLayoutCachesIfNeeded(configDiff);
|
||||
|
||||
if (callbacks != null) {
|
||||
final int size = callbacks.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
ComponentCallbacks2 cb = callbacks.get(i);
|
||||
if (!equivalent) {
|
||||
performConfigurationChanged(cb, config);
|
||||
} else {
|
||||
// TODO (b/135719017): Temporary log for debugging IME service.
|
||||
if (Build.IS_DEBUGGABLE && cb instanceof InputMethodService) {
|
||||
Log.w(TAG, "performConfigurationChanged didn't callback to IME "
|
||||
+ ", configDiff=" + configDiff
|
||||
+ ", mConfiguration=" + mConfiguration);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decides whether to update a component's configuration and whether to inform it.
|
||||
* @param cb The component callback to notify of configuration change.
|
||||
* @param newConfig The new configuration.
|
||||
*/
|
||||
void performConfigurationChanged(@NonNull ComponentCallbacks2 cb,
|
||||
@NonNull Configuration newConfig) {
|
||||
// ContextThemeWrappers may override the configuration for that context. We must check and
|
||||
// apply any overrides defined.
|
||||
Configuration contextThemeWrapperOverrideConfig = null;
|
||||
if (cb instanceof ContextThemeWrapper) {
|
||||
final ContextThemeWrapper contextThemeWrapper = (ContextThemeWrapper) cb;
|
||||
contextThemeWrapperOverrideConfig = contextThemeWrapper.getOverrideConfiguration();
|
||||
}
|
||||
|
||||
// Apply the ContextThemeWrapper override if necessary.
|
||||
// NOTE: Make sure the configurations are not modified, as they are treated as immutable
|
||||
// in many places.
|
||||
final Configuration configToReport = createNewConfigAndUpdateIfNotNull(
|
||||
newConfig, contextThemeWrapperOverrideConfig);
|
||||
cb.onConfigurationChanged(configToReport);
|
||||
}
|
||||
|
||||
/** Update default density. */
|
||||
void updateDefaultDensity(int densityDpi) {
|
||||
if (!mActivityThread.isInDensityCompatMode()
|
||||
&& densityDpi != Configuration.DENSITY_DPI_UNDEFINED
|
||||
&& densityDpi != DisplayMetrics.DENSITY_DEVICE) {
|
||||
DisplayMetrics.DENSITY_DEVICE = densityDpi;
|
||||
Bitmap.setDefaultDensity(densityDpi);
|
||||
}
|
||||
}
|
||||
|
||||
/** Get current default display dpi. This is only done to maintain @UnsupportedAppUsage. */
|
||||
int getCurDefaultDisplayDpi() {
|
||||
return mConfiguration.densityDpi;
|
||||
}
|
||||
|
||||
/**
|
||||
* The LocaleList set for the app's resources may have been shuffled so that the preferred
|
||||
* Locale is at position 0. We must find the index of this preferred Locale in the
|
||||
* original LocaleList.
|
||||
*/
|
||||
void updateLocaleListFromAppContext(@NonNull Context context) {
|
||||
final Locale bestLocale = context.getResources().getConfiguration().getLocales().get(0);
|
||||
final LocaleList newLocaleList = mResourcesManager.getConfiguration().getLocales();
|
||||
final int newLocaleListSize = newLocaleList.size();
|
||||
for (int i = 0; i < newLocaleListSize; i++) {
|
||||
if (bestLocale.equals(newLocaleList.get(i))) {
|
||||
LocaleList.setDefault(newLocaleList, i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// The app may have overridden the LocaleList with its own Locale
|
||||
// (not present in the available list). Push the chosen Locale
|
||||
// to the front of the list.
|
||||
LocaleList.setDefault(new LocaleList(bestLocale, newLocaleList));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Configuration only if override would modify base. Otherwise returns base.
|
||||
* @param base The base configuration.
|
||||
* @param override The update to apply to the base configuration. Can be null.
|
||||
* @return A Configuration representing base with override applied.
|
||||
*/
|
||||
static Configuration createNewConfigAndUpdateIfNotNull(@NonNull Configuration base,
|
||||
@Nullable Configuration override) {
|
||||
if (override == null) {
|
||||
return base;
|
||||
}
|
||||
Configuration newConfig = new Configuration(base);
|
||||
newConfig.updateFrom(override);
|
||||
return newConfig;
|
||||
}
|
||||
|
||||
/** Ask test layout engine to free its caches if there is a locale change. */
|
||||
static void freeTextLayoutCachesIfNeeded(int configDiff) {
|
||||
if (configDiff != 0) {
|
||||
boolean hasLocaleConfigChange = ((configDiff & ActivityInfo.CONFIG_LOCALE) != 0);
|
||||
if (hasLocaleConfigChange) {
|
||||
Canvas.freeTextLayoutCaches();
|
||||
if (DEBUG_CONFIGURATION) {
|
||||
Slog.v(TAG, "Cleared TextLayout Caches");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user