Loading core/java/android/window/ConfigurationDispatcher.java 0 → 100644 +44 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 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.window; import android.annotation.NonNull; import android.content.ComponentCallbacks; import android.content.res.Configuration; /** * Indicates a {@link android.content.Context} could propagate the * {@link android.content.res.Configuration} from the server side and users may listen to the * updates through {@link android.content.Context#registerComponentCallbacks(ComponentCallbacks)}. * * @hide */ public interface ConfigurationDispatcher { /** * Called when there's configuration update from the server side. */ void dispatchConfigurationChanged(@NonNull Configuration configuration); /** * Indicates that if this dispatcher should report the change even if it's not * {@link Configuration#diffPublicOnly}. */ default boolean shouldReportPrivateChanges() { return false; } } core/java/android/window/WindowContext.java +9 −4 Original line number Diff line number Diff line Loading @@ -17,8 +17,6 @@ package android.window; import static android.view.WindowManagerImpl.createWindowContextWindowManager; import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UiContext; Loading Loading @@ -46,7 +44,8 @@ import java.lang.ref.Reference; * @hide */ @UiContext public class WindowContext extends ContextWrapper implements WindowProvider { public class WindowContext extends ContextWrapper implements WindowProvider, ConfigurationDispatcher { private final WindowManager mWindowManager; @WindowManager.LayoutParams.WindowType private final int mType; Loading Loading @@ -155,7 +154,7 @@ public class WindowContext extends ContextWrapper implements WindowProvider { } /** Dispatch {@link Configuration} to each {@link ComponentCallbacks}. */ @VisibleForTesting(visibility = PACKAGE) @Override public void dispatchConfigurationChanged(@NonNull Configuration newConfig) { mCallbacksController.dispatchConfigurationChanged(newConfig); } Loading @@ -170,4 +169,10 @@ public class WindowContext extends ContextWrapper implements WindowProvider { public Bundle getWindowContextOptions() { return mOptions; } @Override public boolean shouldReportPrivateChanges() { // Always dispatch config changes to WindowContext. return true; } } core/java/android/window/WindowContextController.java +0 −1 Original line number Diff line number Diff line Loading @@ -86,7 +86,6 @@ public class WindowContextController { * @param token The token used to attach to a window manager node. It is usually from * {@link Context#getWindowContextToken()}. */ @VisibleForTesting public WindowContextController(@NonNull WindowTokenClient token) { mToken = token; } Loading core/java/android/window/WindowProviderService.java +13 −1 Original line number Diff line number Diff line Loading @@ -49,9 +49,11 @@ import android.view.WindowManagerImpl; * * @hide */ @SuppressWarnings("HiddenSuperclass") @TestApi @UiContext public abstract class WindowProviderService extends Service implements WindowProvider { public abstract class WindowProviderService extends Service implements WindowProvider, ConfigurationDispatcher { private static final String TAG = WindowProviderService.class.getSimpleName(); Loading Loading @@ -240,4 +242,14 @@ public abstract class WindowProviderService extends Service implements WindowPro mController.detachIfNeeded(); mCallbacksController.clearCallbacks(); } /** * {@inheritDoc} * * @hide */ @Override public void dispatchConfigurationChanged(@NonNull Configuration configuration) { onConfigurationChanged(configuration); } } core/java/android/window/WindowTokenClient.java +10 −14 Original line number Diff line number Diff line Loading @@ -121,8 +121,6 @@ public class WindowTokenClient extends Binder { newDisplayId, true /* shouldReportConfigChange */).recycleOnUse()); } // TODO(b/192048581): rewrite this method based on WindowContext and WindowProviderService // are inherited from WindowProvider. /** * Called when {@link Configuration} updates from the server side receive. * Loading Loading @@ -169,7 +167,7 @@ public class WindowTokenClient extends Binder { CompatibilityInfo.applyOverrideIfNeeded(newConfig); final boolean displayChanged; final boolean shouldUpdateResources; final int diff; final int publicDiff; final Configuration currentConfig; synchronized (mConfiguration) { Loading @@ -177,7 +175,7 @@ public class WindowTokenClient extends Binder { shouldUpdateResources = shouldUpdateResources(this, mConfiguration, newConfig, newConfig /* overrideConfig */, displayChanged, null /* configChanged */); diff = mConfiguration.diffPublicOnly(newConfig); publicDiff = mConfiguration.diffPublicOnly(newConfig); currentConfig = mShouldDumpConfigForIme ? new Configuration(mConfiguration) : null; if (shouldUpdateResources) { mConfiguration.setTo(newConfig); Loading @@ -200,17 +198,15 @@ public class WindowTokenClient extends Binder { // TODO(ag/9789103): update resource manager logic to track non-activity tokens mResourcesManager.updateResourcesForActivity(this, newConfig, newDisplayId); if (shouldReportConfigChange && context instanceof WindowContext) { final WindowContext windowContext = (WindowContext) context; windowContext.dispatchConfigurationChanged(newConfig); if (shouldReportConfigChange && context instanceof ConfigurationDispatcher dispatcher) { // Updating resources implies some fields of configuration are updated despite they // are public or not. if (dispatcher.shouldReportPrivateChanges() || publicDiff != 0) { dispatcher.dispatchConfigurationChanged(newConfig); } if (shouldReportConfigChange && diff != 0 && context instanceof WindowProviderService) { final WindowProviderService windowProviderService = (WindowProviderService) context; windowProviderService.onConfigurationChanged(newConfig); } freeTextLayoutCachesIfNeeded(diff); freeTextLayoutCachesIfNeeded(publicDiff); if (mShouldDumpConfigForIme) { if (!shouldReportConfigChange) { Log.d(TAG, "Only apply configuration update to Resources because " Loading @@ -219,7 +215,7 @@ public class WindowTokenClient extends Binder { + ", config=" + context.getResources().getConfiguration() + ", display ID=" + context.getDisplayId() + "\n" + Debug.getCallers(5)); } else if (diff == 0) { } else if (publicDiff == 0) { Log.d(TAG, "Configuration not dispatch to IME because configuration has no " + " public difference with updated config. " + " Current config=" + context.getResources().getConfiguration() Loading Loading
core/java/android/window/ConfigurationDispatcher.java 0 → 100644 +44 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 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.window; import android.annotation.NonNull; import android.content.ComponentCallbacks; import android.content.res.Configuration; /** * Indicates a {@link android.content.Context} could propagate the * {@link android.content.res.Configuration} from the server side and users may listen to the * updates through {@link android.content.Context#registerComponentCallbacks(ComponentCallbacks)}. * * @hide */ public interface ConfigurationDispatcher { /** * Called when there's configuration update from the server side. */ void dispatchConfigurationChanged(@NonNull Configuration configuration); /** * Indicates that if this dispatcher should report the change even if it's not * {@link Configuration#diffPublicOnly}. */ default boolean shouldReportPrivateChanges() { return false; } }
core/java/android/window/WindowContext.java +9 −4 Original line number Diff line number Diff line Loading @@ -17,8 +17,6 @@ package android.window; import static android.view.WindowManagerImpl.createWindowContextWindowManager; import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UiContext; Loading Loading @@ -46,7 +44,8 @@ import java.lang.ref.Reference; * @hide */ @UiContext public class WindowContext extends ContextWrapper implements WindowProvider { public class WindowContext extends ContextWrapper implements WindowProvider, ConfigurationDispatcher { private final WindowManager mWindowManager; @WindowManager.LayoutParams.WindowType private final int mType; Loading Loading @@ -155,7 +154,7 @@ public class WindowContext extends ContextWrapper implements WindowProvider { } /** Dispatch {@link Configuration} to each {@link ComponentCallbacks}. */ @VisibleForTesting(visibility = PACKAGE) @Override public void dispatchConfigurationChanged(@NonNull Configuration newConfig) { mCallbacksController.dispatchConfigurationChanged(newConfig); } Loading @@ -170,4 +169,10 @@ public class WindowContext extends ContextWrapper implements WindowProvider { public Bundle getWindowContextOptions() { return mOptions; } @Override public boolean shouldReportPrivateChanges() { // Always dispatch config changes to WindowContext. return true; } }
core/java/android/window/WindowContextController.java +0 −1 Original line number Diff line number Diff line Loading @@ -86,7 +86,6 @@ public class WindowContextController { * @param token The token used to attach to a window manager node. It is usually from * {@link Context#getWindowContextToken()}. */ @VisibleForTesting public WindowContextController(@NonNull WindowTokenClient token) { mToken = token; } Loading
core/java/android/window/WindowProviderService.java +13 −1 Original line number Diff line number Diff line Loading @@ -49,9 +49,11 @@ import android.view.WindowManagerImpl; * * @hide */ @SuppressWarnings("HiddenSuperclass") @TestApi @UiContext public abstract class WindowProviderService extends Service implements WindowProvider { public abstract class WindowProviderService extends Service implements WindowProvider, ConfigurationDispatcher { private static final String TAG = WindowProviderService.class.getSimpleName(); Loading Loading @@ -240,4 +242,14 @@ public abstract class WindowProviderService extends Service implements WindowPro mController.detachIfNeeded(); mCallbacksController.clearCallbacks(); } /** * {@inheritDoc} * * @hide */ @Override public void dispatchConfigurationChanged(@NonNull Configuration configuration) { onConfigurationChanged(configuration); } }
core/java/android/window/WindowTokenClient.java +10 −14 Original line number Diff line number Diff line Loading @@ -121,8 +121,6 @@ public class WindowTokenClient extends Binder { newDisplayId, true /* shouldReportConfigChange */).recycleOnUse()); } // TODO(b/192048581): rewrite this method based on WindowContext and WindowProviderService // are inherited from WindowProvider. /** * Called when {@link Configuration} updates from the server side receive. * Loading Loading @@ -169,7 +167,7 @@ public class WindowTokenClient extends Binder { CompatibilityInfo.applyOverrideIfNeeded(newConfig); final boolean displayChanged; final boolean shouldUpdateResources; final int diff; final int publicDiff; final Configuration currentConfig; synchronized (mConfiguration) { Loading @@ -177,7 +175,7 @@ public class WindowTokenClient extends Binder { shouldUpdateResources = shouldUpdateResources(this, mConfiguration, newConfig, newConfig /* overrideConfig */, displayChanged, null /* configChanged */); diff = mConfiguration.diffPublicOnly(newConfig); publicDiff = mConfiguration.diffPublicOnly(newConfig); currentConfig = mShouldDumpConfigForIme ? new Configuration(mConfiguration) : null; if (shouldUpdateResources) { mConfiguration.setTo(newConfig); Loading @@ -200,17 +198,15 @@ public class WindowTokenClient extends Binder { // TODO(ag/9789103): update resource manager logic to track non-activity tokens mResourcesManager.updateResourcesForActivity(this, newConfig, newDisplayId); if (shouldReportConfigChange && context instanceof WindowContext) { final WindowContext windowContext = (WindowContext) context; windowContext.dispatchConfigurationChanged(newConfig); if (shouldReportConfigChange && context instanceof ConfigurationDispatcher dispatcher) { // Updating resources implies some fields of configuration are updated despite they // are public or not. if (dispatcher.shouldReportPrivateChanges() || publicDiff != 0) { dispatcher.dispatchConfigurationChanged(newConfig); } if (shouldReportConfigChange && diff != 0 && context instanceof WindowProviderService) { final WindowProviderService windowProviderService = (WindowProviderService) context; windowProviderService.onConfigurationChanged(newConfig); } freeTextLayoutCachesIfNeeded(diff); freeTextLayoutCachesIfNeeded(publicDiff); if (mShouldDumpConfigForIme) { if (!shouldReportConfigChange) { Log.d(TAG, "Only apply configuration update to Resources because " Loading @@ -219,7 +215,7 @@ public class WindowTokenClient extends Binder { + ", config=" + context.getResources().getConfiguration() + ", display ID=" + context.getDisplayId() + "\n" + Debug.getCallers(5)); } else if (diff == 0) { } else if (publicDiff == 0) { Log.d(TAG, "Configuration not dispatch to IME because configuration has no " + " public difference with updated config. " + " Current config=" + context.getResources().getConfiguration() Loading