Configuration overrideConfig;
// Used for consolidating configs before sending on to Activity.
private Configuration tmpConfig = new Configuration();
+ // Callback used for updating activity override config.
+ ViewRootImpl.ActivityConfigCallback configCallback;
ActivityClientRecord nextIdle;
ProfilerInfo profilerInfo;
stopped = false;
hideForNow = false;
nextIdle = null;
+ configCallback = (Configuration overrideConfig, int newDisplayId) -> {
+ if (activity == null) {
+ throw new IllegalStateException(
+ "Received config update for non-existing activity");
+ }
+ activity.mMainThread.handleActivityConfigurationChanged(
+ new ActivityConfigChangeData(token, overrideConfig), newDisplayId);
+ };
}
public boolean isPreHoneycomb() {
if (r.activity.mVisibleFromClient) {
r.activity.makeVisible();
}
+ final ViewRootImpl viewRoot = r.activity.mDecor.getViewRootImpl();
+ if (viewRoot != null) {
+ // TODO: Figure out the best place to set the callback.
+ // This looks like a place where decor view is already initialized.
+ viewRoot.setActivityConfigCallback(r.configCallback);
+ }
}
if (!r.onlyLocalRequest) {
* @param displayId Id of the display where activity was moved to, -1 if there was no move and
* value didn't change.
*/
- private void handleActivityConfigurationChanged(ActivityConfigChangeData data, int displayId) {
+ void handleActivityConfigurationChanged(ActivityConfigChangeData data, int displayId) {
ActivityClientRecord r = mActivities.get(data.activityToken);
// Check input params.
if (r == null || r.activity == null) {
// Perform updates.
r.overrideConfig = data.overrideConfig;
+ final ViewRootImpl viewRoot = r.activity.mDecor.getViewRootImpl();
if (movedToDifferentDisplay) {
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Handle activity moved to display, activity:"
+ r.activityInfo.name + ", displayId=" + displayId
performConfigurationChangedForActivity(r, mCompatConfiguration, displayId,
true /* movedToDifferentDisplay */);
- final ViewRootImpl viewRoot = r.activity.mDecor.getViewRootImpl();
viewRoot.onMovedToDisplay(displayId);
} else {
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Handle activity config changed: "
+ r.activityInfo.name + ", config=" + data.overrideConfig);
performConfigurationChangedForActivity(r, mCompatConfiguration);
}
+ // Notify the ViewRootImpl instance about configuration changes. It may have initiated this
+ // update to make sure that resources are updated before updating itself.
+ viewRoot.updateConfiguration();
mSomeActivitiesChanged = true;
}
// add dropbox logging to libcore
DropBox.setReporter(new DropBoxReporter());
- ViewRootImpl.addConfigCallback(new ComponentCallbacks2() {
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- synchronized (mResourcesManager) {
- // We need to apply this change to the resources
- // immediately, because upon returning the view
- // hierarchy will be informed about it.
- if (mResourcesManager.applyConfigurationToResourcesLocked(newConfig, null)) {
- updateLocaleListFromAppContext(mInitialApplication.getApplicationContext(),
- mResourcesManager.getConfiguration().getLocales());
-
- // This actually changed the resources! Tell
- // everyone about it.
- if (mPendingConfiguration == null ||
- mPendingConfiguration.isOtherSeqNewer(newConfig)) {
- mPendingConfiguration = newConfig;
-
- sendMessage(H.CONFIGURATION_CHANGED, newConfig);
- }
+ ViewRootImpl.ConfigChangedCallback configChangedCallback
+ = (Configuration globalConfig) -> {
+ synchronized (mResourcesManager) {
+ // We need to apply this change to the resources immediately, because upon returning
+ // the view hierarchy will be informed about it.
+ if (mResourcesManager.applyConfigurationToResourcesLocked(globalConfig,
+ null /* compat */)) {
+ updateLocaleListFromAppContext(mInitialApplication.getApplicationContext(),
+ mResourcesManager.getConfiguration().getLocales());
+
+ // This actually changed the resources! Tell everyone about it.
+ if (mPendingConfiguration == null
+ || mPendingConfiguration.isOtherSeqNewer(globalConfig)) {
+ mPendingConfiguration = globalConfig;
+ sendMessage(H.CONFIGURATION_CHANGED, globalConfig);
}
}
}
- @Override
- public void onLowMemory() {
- }
- @Override
- public void onTrimMemory(int level) {
- }
- });
+ };
+ ViewRootImpl.addConfigCallback(configChangedCallback);
}
public static ActivityThread systemMain() {
import android.content.res.TypedArray;
import android.graphics.Canvas;
+import android.util.MergedConfiguration;
import android.view.WindowInsets;
import com.android.internal.R;
import com.android.internal.os.HandlerCaller;
-import com.android.internal.util.ScreenShapeHelper;
import com.android.internal.view.BaseIWindow;
import com.android.internal.view.BaseSurfaceHolder;
import android.app.WallpaperManager;
import android.content.Context;
import android.content.Intent;
-import android.content.res.Configuration;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
-import android.view.WindowManager.LayoutParams;
import android.view.WindowManagerGlobal;
import java.io.FileDescriptor;
final Rect mFinalSystemInsets = new Rect();
final Rect mFinalStableInsets = new Rect();
final Rect mBackdropFrame = new Rect();
- final Configuration mConfiguration = new Configuration();
+ final MergedConfiguration mMergedConfiguration = new MergedConfiguration();
final WindowManager.LayoutParams mLayout
= new WindowManager.LayoutParams();
@Override
public void resized(Rect frame, Rect overscanInsets, Rect contentInsets,
Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
- Configuration newConfig, Rect backDropRect, boolean forceLayout,
+ MergedConfiguration mergedConfiguration, Rect backDropRect, boolean forceLayout,
boolean alwaysConsumeNavBar, int displayId) {
Message msg = mCaller.obtainMessageIO(MSG_WINDOW_RESIZED,
reportDraw ? 1 : 0, outsets);
out.print(mVisibleInsets.toShortString());
out.print(" mWinFrame="); out.print(mWinFrame.toShortString());
out.print(" mContentInsets="); out.println(mContentInsets.toShortString());
- out.print(prefix); out.print("mConfiguration="); out.println(mConfiguration);
+ out.print(prefix); out.print("mConfiguration=");
+ out.println(mMergedConfiguration.getMergedConfiguration());
out.print(prefix); out.print("mLayout="); out.println(mLayout);
synchronized (mLock) {
out.print(prefix); out.print("mPendingXOffset="); out.print(mPendingXOffset);
mWindow, mWindow.mSeq, mLayout, mWidth, mHeight,
View.VISIBLE, 0, mWinFrame, mOverscanInsets, mContentInsets,
mVisibleInsets, mStableInsets, mOutsets, mBackdropFrame,
- mConfiguration, mSurfaceHolder.mSurface);
+ mMergedConfiguration, mSurfaceHolder.mSurface);
if (DEBUG) Log.v(TAG, "New surface: " + mSurfaceHolder.mSurface
+ ", frame=" + mWinFrame);
--- /dev/null
+/*
+ * Copyright (C) 2017 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.util;
+
+parcelable MergedConfiguration;
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 2017 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.util;
+
+import android.annotation.NonNull;
+import android.content.res.Configuration;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Container that holds global and override config and their merge product.
+ * Merged configuration updates automatically whenever global or override configs are updated via
+ * setters.
+ *
+ * {@hide}
+ */
+public class MergedConfiguration implements Parcelable {
+
+ private Configuration mGlobalConfig = new Configuration();
+ private Configuration mOverrideConfig = new Configuration();
+ private Configuration mMergedConfig = new Configuration();
+
+ public MergedConfiguration() {
+ }
+
+ public MergedConfiguration(Configuration globalConfig, Configuration overrideConfig) {
+ setConfiguration(globalConfig, overrideConfig);
+ }
+
+ public MergedConfiguration(MergedConfiguration mergedConfiguration) {
+ setConfiguration(mergedConfiguration.getGlobalConfiguration(),
+ mergedConfiguration.getOverrideConfiguration());
+ }
+
+ private MergedConfiguration(Parcel in) {
+ readFromParcel(in);
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeParcelable(mGlobalConfig, flags);
+ dest.writeParcelable(mOverrideConfig, flags);
+ dest.writeParcelable(mMergedConfig, flags);
+ }
+
+ public void readFromParcel(Parcel source) {
+ mGlobalConfig = source.readParcelable(Configuration.class.getClassLoader());
+ mOverrideConfig = source.readParcelable(Configuration.class.getClassLoader());
+ mMergedConfig = source.readParcelable(Configuration.class.getClassLoader());
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Creator<MergedConfiguration> CREATOR = new Creator<MergedConfiguration>() {
+ @Override
+ public MergedConfiguration createFromParcel(Parcel in) {
+ return new MergedConfiguration(in);
+ }
+
+ @Override
+ public MergedConfiguration[] newArray(int size) {
+ return new MergedConfiguration[size];
+ }
+ };
+
+ /**
+ * Update global and override configurations.
+ * Merged configuration will automatically be updated.
+ * @param globalConfig New global configuration.
+ * @param overrideConfig New override configuration.
+ */
+ public void setConfiguration(Configuration globalConfig, Configuration overrideConfig) {
+ mGlobalConfig.setTo(globalConfig);
+ mOverrideConfig.setTo(overrideConfig);
+ updateMergedConfig();
+ }
+
+ /**
+ * @return Stored global configuration value.
+ */
+ @NonNull
+ public Configuration getGlobalConfiguration() {
+ return mGlobalConfig;
+ }
+
+ /**
+ * @return Stored override configuration value.
+ */
+ public Configuration getOverrideConfiguration() {
+ return mOverrideConfig;
+ }
+
+ /**
+ * @return Stored merged configuration value.
+ */
+ public Configuration getMergedConfiguration() {
+ return mMergedConfig;
+ }
+
+ /** Update merged config when global or override config changes. */
+ private void updateMergedConfig() {
+ mMergedConfig.setTo(mGlobalConfig);
+ mMergedConfig.updateFrom(mOverrideConfig);
+ }
+}
package android.view;
-import android.content.ComponentCallbacks;
import android.content.res.Configuration;
import java.text.BreakIterator;
}
static class CharacterTextSegmentIterator extends AbstractTextSegmentIterator
- implements ComponentCallbacks {
+ implements ViewRootImpl.ConfigChangedCallback {
private static CharacterTextSegmentIterator sInstance;
private Locale mLocale;
}
@Override
- public void onConfigurationChanged(Configuration newConfig) {
- Locale locale = newConfig.locale;
+ public void onConfigurationChanged(Configuration globalConfig) {
+ final Locale locale = globalConfig.getLocales().get(0);
if (!mLocale.equals(locale)) {
mLocale = locale;
onLocaleChanged(locale);
}
}
- @Override
- public void onLowMemory() {
- /* ignore */
- }
-
protected void onLocaleChanged(Locale locale) {
mImpl = BreakIterator.getCharacterInstance(locale);
}
package android.view;
-import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.view.MotionEvent;
import com.android.internal.os.IResultReceiver;
+import android.util.MergedConfiguration;
/**
* API back to a client window that the Window Manager uses to inform it of
void resized(in Rect frame, in Rect overscanInsets, in Rect contentInsets,
in Rect visibleInsets, in Rect stableInsets, in Rect outsets, boolean reportDraw,
- in Configuration newConfig, in Rect backDropFrame, boolean forceLayout,
- boolean alwaysConsumeNavBar, int displayId);
+ in MergedConfiguration newMergedConfiguration, in Rect backDropFrame,
+ boolean forceLayout, boolean alwaysConsumeNavBar, int displayId);
void moved(int newX, int newY);
void dispatchAppVisibility(boolean visible);
void dispatchGetNewSurface();
** 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
+** See the License for the specific language governing permissions and
** limitations under the License.
*/
package android.view;
import android.content.ClipData;
-import android.content.res.Configuration;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.Bundle;
+import android.util.MergedConfiguration;
import android.view.InputChannel;
import android.view.IWindow;
import android.view.IWindowId;
* treat as real display. Example of such area is a chin in some models of wearable devices.
* @param outBackdropFrame Rect which is used draw the resizing background during a resize
* operation.
- * @param outConfiguration New configuration of window, if it is now
- * becoming visible and the global configuration has changed since it
- * was last displayed.
+ * @param outMergedConfiguration New config container that holds global, override and merged
+ * config for window, if it is now becoming visible and the merged configuration has changed
+ * since it was last displayed.
* @param outSurface Object in which is placed the new display surface.
*
* @return int Result flags: {@link WindowManagerGlobal#RELAYOUT_SHOW_FOCUS},
int requestedWidth, int requestedHeight, int viewVisibility,
int flags, out Rect outFrame, out Rect outOverscanInsets,
out Rect outContentInsets, out Rect outVisibleInsets, out Rect outStableInsets,
- out Rect outOutsets, out Rect outBackdropFrame, out Configuration outConfig,
- out Surface outSurface);
+ out Rect outOutsets, out Rect outBackdropFrame,
+ out MergedConfiguration outMergedConfiguration, out Surface outSurface);
/*
* Notify the window manager that an application is relaunching and
package android.view;
+import static android.view.Display.INVALID_DISPLAY;
import static android.view.View.PFLAG_DRAW_ANIMATION;
import static android.view.WindowCallbacks.RESIZE_MODE_DOCKED_DIVIDER;
import static android.view.WindowCallbacks.RESIZE_MODE_FREEFORM;
import android.animation.LayoutTransition;
import android.annotation.NonNull;
import android.app.ActivityManager;
+import android.app.ActivityThread;
import android.app.ResourcesManager;
import android.content.ClipData;
import android.content.ClipDescription;
-import android.content.ComponentCallbacks;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.CompatibilityInfo;
import android.util.AndroidRuntimeException;
import android.util.DisplayMetrics;
import android.util.Log;
+import android.util.MergedConfiguration;
import android.util.Slog;
import android.util.TimeUtils;
import android.util.TypedValue;
static final ArrayList<Runnable> sFirstDrawHandlers = new ArrayList();
static boolean sFirstDrawComplete = false;
- static final ArrayList<ComponentCallbacks> sConfigCallbacks = new ArrayList();
+ /**
+ * Callback for notifying about global configuration changes.
+ */
+ public interface ConfigChangedCallback {
+
+ /** Notifies about global config change. */
+ void onConfigurationChanged(Configuration globalConfig);
+ }
+
+ private static final ArrayList<ConfigChangedCallback> sConfigCallbacks = new ArrayList<>();
+
+ /**
+ * Callback for notifying activities about override configuration changes.
+ */
+ public interface ActivityConfigCallback {
+
+ /**
+ * Notifies about override config change and/or move to different display.
+ * @param overrideConfig New override config to apply to activity.
+ * @param newDisplayId New display id, {@link Display#INVALID_DISPLAY} if not changed.
+ */
+ void onConfigurationChanged(Configuration overrideConfig, int newDisplayId);
+ }
+
+ /**
+ * Callback used to notify corresponding activity about override configuration change and make
+ * sure that all resources are set correctly before updating the ViewRootImpl's internal state.
+ */
+ private ActivityConfigCallback mActivityConfigCallback;
+
+ /**
+ * Used when configuration change first updates the config of corresponding activity.
+ * In that case we receive a call back from {@link ActivityThread} and this flag is used to
+ * preserve the initial value.
+ *
+ * @see #performConfigurationChange(Configuration, Configuration, boolean, int)
+ */
+ private boolean mForceNextConfigUpdate;
/**
* Signals that compatibility booleans have been initialized according to
private WindowInsets mLastWindowInsets;
- final Configuration mLastConfiguration = new Configuration();
- final Configuration mPendingConfiguration = new Configuration();
+ /** Last applied configuration obtained from resources. */
+ private final Configuration mLastConfigurationFromResources = new Configuration();
+ /** Last configuration reported from WM or via {@link #MSG_UPDATE_CONFIGURATION}. */
+ private final MergedConfiguration mLastReportedMergedConfiguration = new MergedConfiguration();
+ /** Configurations waiting to be applied. */
+ private final MergedConfiguration mPendingMergedConfiguration = new MergedConfiguration();
boolean mScrollMayChange;
@SoftInputModeFlags
}
}
- public static void addConfigCallback(ComponentCallbacks callback) {
+ /** Add static config callback to be notified about global config changes. */
+ public static void addConfigCallback(ConfigChangedCallback callback) {
synchronized (sConfigCallbacks) {
sConfigCallbacks.add(callback);
}
}
+ /** Add activity config callback to be notified about override config changes. */
+ public void setActivityConfigCallback(ActivityConfigCallback callback) {
+ mActivityConfigCallback = callback;
+ }
+
public void addWindowCallbacks(WindowCallbacks callback) {
if (USE_MT_RENDERER) {
synchronized (mWindowCallbacks) {
mFullRedrawNeeded = true;
mLayoutRequested = true;
+ final Configuration config = mContext.getResources().getConfiguration();
if (shouldUseDisplaySize(lp)) {
// NOTE -- system code, won't try to do compat mode.
Point size = new Point();
desiredWindowWidth = size.x;
desiredWindowHeight = size.y;
} else {
- Configuration config = mContext.getResources().getConfiguration();
desiredWindowWidth = dipToPx(config.screenWidthDp);
desiredWindowHeight = dipToPx(config.screenHeightDp);
}
mAttachInfo.mHasWindowFocus = false;
mAttachInfo.mWindowVisibility = viewVisibility;
mAttachInfo.mRecomputeGlobalAttributes = false;
- mLastConfiguration.setTo(host.getResources().getConfiguration());
+ mLastConfigurationFromResources.setTo(config);
mLastSystemUiVisibility = mAttachInfo.mSystemUiVisibility;
// Set the layout direction if it has not been set before (inherit is the default)
if (mViewLayoutDirectionInitial == View.LAYOUT_DIRECTION_INHERIT) {
- host.setLayoutDirection(mLastConfiguration.getLayoutDirection());
+ host.setLayoutDirection(config.getLayoutDirection());
}
host.dispatchAttachedToWindow(mAttachInfo, 0);
mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(true);
+ " outsets=" + mPendingOutsets.toShortString()
+ " surface=" + mSurface);
- if (mPendingConfiguration.seq != 0) {
+ final Configuration pendingMergedConfig =
+ mPendingMergedConfiguration.getMergedConfiguration();
+ if (pendingMergedConfig.seq != 0) {
if (DEBUG_CONFIGURATION) Log.v(mTag, "Visible with new config: "
- + mPendingConfiguration);
- updateConfiguration(new Configuration(mPendingConfiguration), !mFirst);
- mPendingConfiguration.seq = 0;
+ + pendingMergedConfig);
+ performConfigurationChange(mPendingMergedConfiguration, !mFirst,
+ INVALID_DISPLAY /* same display */);
+ pendingMergedConfig.seq = 0;
updatedConfiguration = true;
}
unscheduleTraversals();
}
- void updateConfiguration(Configuration config, boolean force) {
+ /**
+ * Notifies all callbacks that configuration and/or display has changed and updates internal
+ * state.
+ * @param mergedConfiguration New global and override config in {@link MergedConfiguration}
+ * container.
+ * @param force Flag indicating if we should force apply the config.
+ * @param newDisplayId Id of new display if moved, {@link Display#INVALID_DISPLAY} if not
+ * changed.
+ */
+ private void performConfigurationChange(MergedConfiguration mergedConfiguration, boolean force,
+ int newDisplayId) {
+ if (mergedConfiguration == null) {
+ throw new IllegalArgumentException("No merged config provided.");
+ }
+
+ Configuration globalConfig = mergedConfiguration.getGlobalConfiguration();
+ final Configuration overrideConfig = mergedConfiguration.getOverrideConfiguration();
if (DEBUG_CONFIGURATION) Log.v(mTag,
- "Applying new config to window "
- + mWindowAttributes.getTitle()
- + ": " + config);
+ "Applying new config to window " + mWindowAttributes.getTitle()
+ + ", globalConfig: " + globalConfig
+ + ", overrideConfig: " + overrideConfig);
- CompatibilityInfo ci = mDisplay.getDisplayAdjustments().getCompatibilityInfo();
+ final CompatibilityInfo ci = mDisplay.getDisplayAdjustments().getCompatibilityInfo();
if (!ci.equals(CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO)) {
- config = new Configuration(config);
- ci.applyToConfiguration(mNoncompatDensity, config);
+ globalConfig = new Configuration(globalConfig);
+ ci.applyToConfiguration(mNoncompatDensity, globalConfig);
}
synchronized (sConfigCallbacks) {
for (int i=sConfigCallbacks.size()-1; i>=0; i--) {
- sConfigCallbacks.get(i).onConfigurationChanged(config);
+ sConfigCallbacks.get(i).onConfigurationChanged(globalConfig);
}
}
- if (mView != null) {
- // At this point the resources have been updated to
- // have the most recent config, whatever that is. Use
- // the one in them which may be newer.
- final Resources localResources = mView.getResources();
- config = localResources.getConfiguration();
- if (force || mLastConfiguration.diff(config) != 0) {
- // Update the display with new DisplayAdjustments.
- mDisplay = ResourcesManager.getInstance().getAdjustedDisplay(
- mDisplay.getDisplayId(), localResources);
- final int lastLayoutDirection = mLastConfiguration.getLayoutDirection();
- final int currentLayoutDirection = config.getLayoutDirection();
- mLastConfiguration.setTo(config);
- if (lastLayoutDirection != currentLayoutDirection &&
- mViewLayoutDirectionInitial == View.LAYOUT_DIRECTION_INHERIT) {
- mView.setLayoutDirection(currentLayoutDirection);
- }
- mView.dispatchConfigurationChanged(config);
+ mLastReportedMergedConfiguration.setConfiguration(globalConfig, overrideConfig);
+
+ mForceNextConfigUpdate = force;
+ if (mActivityConfigCallback != null) {
+ // An activity callback is set - notify it about override configuration update.
+ // This basically initiates a round trip to ActivityThread and back, which will ensure
+ // that corresponding activity and resources are updated before updating inner state of
+ // ViewRootImpl. Eventually it will call #updateConfiguration().
+ mActivityConfigCallback.onConfigurationChanged(overrideConfig, newDisplayId);
+ } else {
+ // There is no activity callback - update the configuration right away.
+ updateConfiguration();
+ }
+ mForceNextConfigUpdate = false;
+ }
+
+ /**
+ * Update display and views if last applied merged configuration changed.
+ */
+ public void updateConfiguration() {
+ if (mView == null) {
+ return;
+ }
+
+ // At this point the resources have been updated to
+ // have the most recent config, whatever that is. Use
+ // the one in them which may be newer.
+ final Resources localResources = mView.getResources();
+ final Configuration config = localResources.getConfiguration();
+ if (mForceNextConfigUpdate || mLastConfigurationFromResources.diff(config) != 0) {
+ // Update the display with new DisplayAdjustments.
+ mDisplay = ResourcesManager.getInstance().getAdjustedDisplay(
+ mDisplay.getDisplayId(), localResources);
+
+ final int lastLayoutDirection = mLastConfigurationFromResources.getLayoutDirection();
+ final int currentLayoutDirection = config.getLayoutDirection();
+ mLastConfigurationFromResources.setTo(config);
+ if (lastLayoutDirection != currentLayoutDirection
+ && mViewLayoutDirectionInitial == View.LAYOUT_DIRECTION_INHERIT) {
+ mView.setLayoutDirection(currentLayoutDirection);
}
+ mView.dispatchConfigurationChanged(config);
}
}
if (mAdded) {
SomeArgs args = (SomeArgs) msg.obj;
- if (mDisplay.getDisplayId() != args.argi3) {
- onMovedToDisplay(args.argi3);
+ final int displayId = args.argi3;
+ final boolean displayChanged = mDisplay.getDisplayId() != displayId;
+ if (displayChanged) {
+ onMovedToDisplay(displayId);
}
- Configuration config = (Configuration) args.arg4;
- if (config != null) {
- updateConfiguration(config, false);
+ final MergedConfiguration mergedConfiguration = (MergedConfiguration) args.arg4;
+ if (mergedConfiguration != null) {
+ performConfigurationChange(mergedConfiguration, false /* force */,
+ displayChanged ? displayId : INVALID_DISPLAY /* same display */);
}
final boolean framesChanged = !mWinFrame.equals(args.arg1)
handleDispatchSystemUiVisibilityChanged((SystemUiVisibilityInfo) msg.obj);
} break;
case MSG_UPDATE_CONFIGURATION: {
- Configuration config = (Configuration)msg.obj;
- if (config.isOtherSeqNewer(mLastConfiguration)) {
- config = mLastConfiguration;
+ Configuration config = (Configuration) msg.obj;
+ if (config.isOtherSeqNewer(
+ mLastReportedMergedConfiguration.getMergedConfiguration())) {
+ // If we already have a newer merged config applied - use its global part.
+ config = mLastReportedMergedConfiguration.getGlobalConfiguration();
}
- updateConfiguration(config, false);
+
+ // Use the newer global config and last reported override config.
+ mPendingMergedConfiguration.setConfiguration(config,
+ mLastReportedMergedConfiguration.getOverrideConfiguration());
+
+ performConfigurationChange(mPendingMergedConfiguration, false /* force */,
+ INVALID_DISPLAY /* same display */);
} break;
case MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST: {
setAccessibilityFocus(null, null);
if (params != null) {
if (DBG) Log.d(mTag, "WindowLayout in layoutWindow:" + params);
}
- mPendingConfiguration.seq = 0;
+ mPendingMergedConfiguration.getMergedConfiguration().seq = 0;
//Log.d(mTag, ">>>>>> CALLING relayout");
if (params != null && mOrigWindowType != params.type) {
// For compatibility with old apps, don't crash here.
(int) (mView.getMeasuredHeight() * appScale + 0.5f),
viewVisibility, insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,
mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
- mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingConfiguration,
- mSurface);
+ mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame,
+ mPendingMergedConfiguration, mSurface);
mPendingAlwaysConsumeNavBar =
(relayoutResult & WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_NAV_BAR) != 0;
}
}
- public void dispatchResized(Rect frame, Rect overscanInsets, Rect contentInsets,
+ private void dispatchResized(Rect frame, Rect overscanInsets, Rect contentInsets,
Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
- Configuration newConfig, Rect backDropFrame, boolean forceLayout,
+ MergedConfiguration mergedConfiguration, Rect backDropFrame, boolean forceLayout,
boolean alwaysConsumeNavBar, int displayId) {
if (DEBUG_LAYOUT) Log.v(mTag, "Resizing " + this + ": frame=" + frame.toShortString()
+ " contentInsets=" + contentInsets.toShortString()
args.arg1 = sameProcessCall ? new Rect(frame) : frame;
args.arg2 = sameProcessCall ? new Rect(contentInsets) : contentInsets;
args.arg3 = sameProcessCall ? new Rect(visibleInsets) : visibleInsets;
- args.arg4 = sameProcessCall && newConfig != null ? new Configuration(newConfig) : newConfig;
+ args.arg4 = sameProcessCall && mergedConfiguration != null
+ ? new MergedConfiguration(mergedConfiguration) : null;
args.arg5 = sameProcessCall ? new Rect(overscanInsets) : overscanInsets;
args.arg6 = sameProcessCall ? new Rect(stableInsets) : stableInsets;
args.arg7 = sameProcessCall ? new Rect(outsets) : outsets;
@Override
public void resized(Rect frame, Rect overscanInsets, Rect contentInsets,
Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
- Configuration newConfig, Rect backDropFrame, boolean forceLayout,
+ MergedConfiguration mergedConfiguration, Rect backDropFrame, boolean forceLayout,
boolean alwaysConsumeNavBar, int displayId) {
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
viewAncestor.dispatchResized(frame, overscanInsets, contentInsets,
- visibleInsets, stableInsets, outsets, reportDraw, newConfig, backDropFrame,
- forceLayout, alwaysConsumeNavBar, displayId);
+ visibleInsets, stableInsets, outsets, reportDraw, mergedConfiguration,
+ backDropFrame, forceLayout, alwaysConsumeNavBar, displayId);
}
}
package com.android.internal.view;
-import android.content.res.Configuration;
import android.graphics.Rect;
import android.hardware.input.InputManager;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
+import android.util.MergedConfiguration;
import android.view.DragEvent;
import android.view.IWindow;
import android.view.IWindowSession;
@Override
public void resized(Rect frame, Rect overscanInsets, Rect contentInsets, Rect visibleInsets,
- Rect stableInsets, Rect outsets, boolean reportDraw, Configuration newConfig,
- Rect backDropFrame, boolean forceLayout, boolean alwaysConsumeNavBar, int displayId) {
+ Rect stableInsets, Rect outsets, boolean reportDraw,
+ MergedConfiguration mergedConfiguration, Rect backDropFrame, boolean forceLayout,
+ boolean alwaysConsumeNavBar, int displayId) {
if (reportDraw) {
try {
mSession.finishDrawing(this);
import android.content.ClipData;
import android.content.Context;
-import android.content.res.Configuration;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.Binder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
+import android.util.MergedConfiguration;
import android.util.Slog;
import android.view.Display;
import android.view.IWindow;
int requestedWidth, int requestedHeight, int viewFlags,
int flags, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
Rect outVisibleInsets, Rect outStableInsets, Rect outsets, Rect outBackdropFrame,
- Configuration outConfig, Surface outSurface) {
+ MergedConfiguration mergedConfiguration, Surface outSurface) {
if (false) Slog.d(TAG_WM, ">>>>>> ENTERED relayout from "
+ Binder.getCallingPid());
int res = mService.relayoutWindow(this, window, seq, attrs,
requestedWidth, requestedHeight, viewFlags, flags,
outFrame, outOverscanInsets, outContentInsets, outVisibleInsets,
- outStableInsets, outsets, outBackdropFrame, outConfig, outSurface);
+ outStableInsets, outsets, outBackdropFrame, mergedConfiguration, outSurface);
if (false) Slog.d(TAG_WM, "<<<<<< EXITING relayout to "
+ Binder.getCallingPid());
return res;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.app.ActivityManager.TaskDescription;
-import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
+import android.util.MergedConfiguration;
import android.util.Slog;
import android.view.IWindowSession;
import android.view.Surface;
final Surface surface = new Surface();
final Rect tmpRect = new Rect();
final Rect tmpFrame = new Rect();
- final Configuration tmpConfiguration = new Configuration();
+ final MergedConfiguration tmpMergedConfiguration = new MergedConfiguration();
int fillBackgroundColor = Color.WHITE;
synchronized (service.mWindowMap) {
layoutParams.type = TYPE_APPLICATION_STARTING;
window.setOuter(snapshotSurface);
try {
session.relayout(window, window.mSeq, layoutParams, -1, -1, View.VISIBLE, 0, tmpFrame,
- tmpRect, tmpRect, tmpRect, tmpRect, tmpRect, tmpRect, tmpConfiguration,
+ tmpRect, tmpRect, tmpRect, tmpRect, tmpRect, tmpRect, tmpMergedConfiguration,
surface);
} catch (RemoteException e) {
// Local call.
@Override
public void resized(Rect frame, Rect overscanInsets, Rect contentInsets, Rect visibleInsets,
- Rect stableInsets, Rect outsets, boolean reportDraw, Configuration newConfig,
- Rect backDropFrame, boolean forceLayout, boolean alwaysConsumeNavBar,
- int displayId) {
+ Rect stableInsets, Rect outsets, boolean reportDraw,
+ MergedConfiguration mergedConfiguration, Rect backDropFrame, boolean forceLayout,
+ boolean alwaysConsumeNavBar, int displayId) {
if (reportDraw) {
sHandler.obtainMessage(MSG_REPORT_DRAW, mOuter).sendToTarget();
}
import android.os.WorkSource;
import android.provider.Settings;
import android.util.ArraySet;
+import android.util.MergedConfiguration;
import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.Log;
int requestedHeight, int viewVisibility, int flags,
Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
Rect outVisibleInsets, Rect outStableInsets, Rect outOutsets, Rect outBackdropFrame,
- Configuration outConfig, Surface outSurface) {
+ MergedConfiguration mergedConfiguration, Surface outSurface) {
int result = 0;
boolean configChanged;
boolean hasStatusBarPermission =
if (viewVisibility == View.VISIBLE &&
(win.mAppToken == null || win.mAttrs.type == TYPE_APPLICATION_STARTING
|| !win.mAppToken.clientHidden)) {
- result = win.relayoutVisibleWindow(outConfig, result, attrChanges,
+ result = win.relayoutVisibleWindow(mergedConfiguration, result, attrChanges,
oldVisibility);
try {
result = createSurfaceControl(outSurface, result, win, winAnimator);
import android.os.Trace;
import android.os.UserHandle;
import android.os.WorkSource;
+import android.util.MergedConfiguration;
import android.util.DisplayMetrics;
import android.util.Slog;
import android.util.TimeUtils;
}
}
- void prepareWindowToDisplayDuringRelayout(Configuration outConfig) {
+ void prepareWindowToDisplayDuringRelayout(MergedConfiguration mergedConfiguration) {
if ((mAttrs.softInputMode & SOFT_INPUT_MASK_ADJUST)
== SOFT_INPUT_ADJUST_RESIZE) {
mLayoutNeeded = true;
mTurnOnScreen = true;
}
if (isConfigChanged()) {
- outConfig.setTo(getConfiguration());
- if (DEBUG_CONFIGURATION) Slog.i(TAG, "Window " + this + " visible with new config: "
- + outConfig);
- mLastReportedConfiguration.setTo(outConfig);
+ final Configuration globalConfig = mService.mRoot.getConfiguration();
+ final Configuration overrideConfig = getMergedOverrideConfiguration();
+ mergedConfiguration.setConfiguration(globalConfig, overrideConfig);
+ if (DEBUG_CONFIGURATION) Slog.i(TAG, "Window " + this
+ + " visible with new global config: " + globalConfig
+ + " merged override config: " + overrideConfig);
+ mLastReportedConfiguration.setTo(getConfiguration());
}
}
try {
if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG, "Reporting new frame to " + this
+ ": " + mCompatFrame);
- final Configuration newConfig;
+ final MergedConfiguration mergedConfiguration;
if (isConfigChanged()) {
- newConfig = new Configuration(getConfiguration());
- mLastReportedConfiguration.setTo(newConfig);
+ mergedConfiguration = new MergedConfiguration(mService.mRoot.getConfiguration(),
+ getMergedOverrideConfiguration());
+ mLastReportedConfiguration.setTo(mergedConfiguration.getMergedConfiguration());
} else {
- newConfig = null;
+ mergedConfiguration = null;
}
if (DEBUG_ORIENTATION && mWinAnimator.mDrawState == DRAW_PENDING)
Slog.i(TAG, "Resizing " + this + " WITH DRAW PENDING");
public void run() {
try {
dispatchResized(frame, overscanInsets, contentInsets, visibleInsets,
- stableInsets, outsets, reportDraw, newConfig,
+ stableInsets, outsets, reportDraw, mergedConfiguration,
reportOrientation, displayId);
} catch (RemoteException e) {
// Not a remote call, RemoteException won't be raised.
});
} else {
dispatchResized(frame, overscanInsets, contentInsets, visibleInsets, stableInsets,
- outsets, reportDraw, newConfig, reportOrientation, displayId);
+ outsets, reportDraw, mergedConfiguration, reportOrientation, displayId);
}
//TODO (multidisplay): Accessibility supported only for the default display.
private void dispatchResized(Rect frame, Rect overscanInsets, Rect contentInsets,
Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
- Configuration newConfig, boolean reportOrientation, int displayId)
+ MergedConfiguration mergedConfiguration, boolean reportOrientation, int displayId)
throws RemoteException {
final boolean forceRelayout = isDragResizeChanged() || mResizedWhileNotDragResizing
|| reportOrientation;
mClient.resized(frame, overscanInsets, contentInsets, visibleInsets, stableInsets, outsets,
- reportDraw, newConfig, getBackdropFrame(frame),
- forceRelayout, mPolicy.isNavBarForcedShownLw(this), displayId);
+ reportDraw, mergedConfiguration, getBackdropFrame(frame), forceRelayout,
+ mPolicy.isNavBarForcedShownLw(this), displayId);
mDragResizingChangeReported = true;
}
return !mLastSurfaceInsets.equals(mAttrs.surfaceInsets);
}
- int relayoutVisibleWindow(Configuration outConfig, int result,
- int attrChanges, int oldVisibility) {
+ int relayoutVisibleWindow(MergedConfiguration mergedConfiguration, int result, int attrChanges,
+ int oldVisibility) {
result |= !isVisibleLw() ? RELAYOUT_RES_FIRST_TIME : 0;
if (mAnimatingExit) {
Slog.d(TAG, "relayoutVisibleWindow: " + this + " mAnimatingExit=true, mRemoveOnExit="
mWinAnimator.mEnteringAnimation = true;
if ((result & RELAYOUT_RES_FIRST_TIME) != 0) {
- prepareWindowToDisplayDuringRelayout(outConfig);
+ prepareWindowToDisplayDuringRelayout(mergedConfiguration);
}
if ((attrChanges & FORMAT_CHANGED) != 0) {
// If the format can't be changed in place, preserve the old surface until the app draws
import com.android.internal.os.IResultReceiver;
-import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
+import android.util.MergedConfiguration;
import android.view.DragEvent;
import android.view.IWindow;
@Override
public void resized(Rect frame, Rect overscanInsets, Rect contentInsets, Rect visibleInsets,
- Rect stableInsets, Rect outsets, boolean reportDraw, Configuration newConfig,
+ Rect stableInsets, Rect outsets, boolean reportDraw, MergedConfiguration mergedConfig,
Rect backDropFrame, boolean forceLayout, boolean alwaysConsumeNavBar, int displayId)
throws RemoteException {
import com.android.internal.os.IResultReceiver;
-import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
+import android.util.MergedConfiguration;
import android.view.DragEvent;
import android.view.IWindow;
@Override
public void resized(Rect rect, Rect rect2, Rect rect3, Rect rect4, Rect rect5, Rect rect6,
- boolean b, Configuration configuration, Rect rect7, boolean b2, boolean b3, int i0)
+ boolean b, MergedConfiguration mergedConfig, Rect rect7, boolean b2, boolean b3, int i0)
throws RemoteException {
// pass for now.
}
package com.android.layoutlib.bridge.android;
import android.content.ClipData;
-import android.content.res.Configuration;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
+import android.util.MergedConfiguration;
import android.view.IWindow;
import android.view.IWindowId;
import android.view.IWindowSession;
@Override
public int relayout(IWindow iWindow, int i, LayoutParams layoutParams, int i2,
int i3, int i4, int i5, Rect rect, Rect rect2, Rect rect3, Rect rect4, Rect rect5,
- Rect rect6, Rect rect7, Configuration configuration, Surface surface)
+ Rect rect6, Rect rect7, MergedConfiguration mergedConfig, Surface surface)
throws RemoteException {
// pass for now.
return 0;