Loading core/java/android/app/Application.java +9 −40 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentCallbacks; import android.content.ComponentCallbacks2; import android.content.ComponentCallbacksController; import android.content.Context; import android.content.ContextWrapper; import android.content.Intent; Loading Loading @@ -53,14 +54,14 @@ import java.util.ArrayList; public class Application extends ContextWrapper implements ComponentCallbacks2 { private static final String TAG = "Application"; @UnsupportedAppUsage private ArrayList<ComponentCallbacks> mComponentCallbacks = new ArrayList<ComponentCallbacks>(); @UnsupportedAppUsage private ArrayList<ActivityLifecycleCallbacks> mActivityLifecycleCallbacks = new ArrayList<ActivityLifecycleCallbacks>(); @UnsupportedAppUsage private ArrayList<OnProvideAssistDataListener> mAssistCallbacks = null; private final ComponentCallbacksController mCallbacksController = new ComponentCallbacksController(); /** @hide */ @UnsupportedAppUsage public LoadedApk mLoadedApk; Loading Loading @@ -260,47 +261,25 @@ public class Application extends ContextWrapper implements ComponentCallbacks2 { @CallSuper public void onConfigurationChanged(@NonNull Configuration newConfig) { Object[] callbacks = collectComponentCallbacks(); if (callbacks != null) { for (int i=0; i<callbacks.length; i++) { ((ComponentCallbacks)callbacks[i]).onConfigurationChanged(newConfig); } } mCallbacksController.dispatchConfigurationChanged(newConfig); } @CallSuper public void onLowMemory() { Object[] callbacks = collectComponentCallbacks(); if (callbacks != null) { for (int i=0; i<callbacks.length; i++) { ((ComponentCallbacks)callbacks[i]).onLowMemory(); } } mCallbacksController.dispatchLowMemory(); } @CallSuper public void onTrimMemory(int level) { Object[] callbacks = collectComponentCallbacks(); if (callbacks != null) { for (int i=0; i<callbacks.length; i++) { Object c = callbacks[i]; if (c instanceof ComponentCallbacks2) { ((ComponentCallbacks2)c).onTrimMemory(level); } } } mCallbacksController.dispatchTrimMemory(level); } public void registerComponentCallbacks(ComponentCallbacks callback) { synchronized (mComponentCallbacks) { mComponentCallbacks.add(callback); } mCallbacksController.registerCallbacks(callback); } public void unregisterComponentCallbacks(ComponentCallbacks callback) { synchronized (mComponentCallbacks) { mComponentCallbacks.remove(callback); } mCallbacksController.unregisterCallbacks(callback); } public void registerActivityLifecycleCallbacks(ActivityLifecycleCallbacks callback) { Loading Loading @@ -575,16 +554,6 @@ public class Application extends ContextWrapper implements ComponentCallbacks2 { } } private Object[] collectComponentCallbacks() { Object[] callbacks = null; synchronized (mComponentCallbacks) { if (mComponentCallbacks.size() > 0) { callbacks = mComponentCallbacks.toArray(); } } return callbacks; } @UnsupportedAppUsage private Object[] collectActivityLifecycleCallbacks() { Object[] callbacks = null; Loading core/java/android/app/WindowContext.java +21 −0 Original line number Diff line number Diff line Loading @@ -20,8 +20,11 @@ import static android.view.WindowManagerImpl.createWindowContextWindowManager; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UiContext; import android.content.ComponentCallbacks; import android.content.ComponentCallbacksController; import android.content.Context; import android.content.ContextWrapper; import android.content.res.Configuration; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; Loading Loading @@ -49,6 +52,8 @@ public class WindowContext extends ContextWrapper { private final IWindowManager mWms; private final WindowTokenClient mToken; private boolean mListenerRegistered; private final ComponentCallbacksController mCallbacksController = new ComponentCallbacksController(); /** * Default constructor. Will generate a {@link WindowTokenClient} and attach this context to Loading Loading @@ -131,8 +136,24 @@ public class WindowContext extends ContextWrapper { } void destroy() { mCallbacksController.clearCallbacks(); final ContextImpl impl = (ContextImpl) getBaseContext(); impl.scheduleFinalCleanup(getClass().getName(), "WindowContext"); Reference.reachabilityFence(this); } @Override public void registerComponentCallbacks(@NonNull ComponentCallbacks callback) { mCallbacksController.registerCallbacks(callback); } @Override public void unregisterComponentCallbacks(@NonNull ComponentCallbacks callback) { mCallbacksController.unregisterCallbacks(callback); } /** Dispatch {@link Configuration} to each {@link ComponentCallbacks}. */ void dispatchConfigurationChanged(@NonNull Configuration newConfig) { mCallbacksController.dispatchConfigurationChanged(newConfig); } } core/java/android/app/WindowTokenClient.java +3 −1 Original line number Diff line number Diff line Loading @@ -61,7 +61,7 @@ public class WindowTokenClient extends IWindowToken.Stub { @Override public void onConfigurationChanged(Configuration newConfig, int newDisplayId) { final Context context = mContextRef.get(); final WindowContext context = mContextRef.get(); if (context == null) { return; } Loading @@ -72,6 +72,8 @@ public class WindowTokenClient extends IWindowToken.Stub { if (displayChanged || configChanged) { // TODO(ag/9789103): update resource manager logic to track non-activity tokens mResourcesManager.updateResourcesForActivity(this, newConfig, newDisplayId); ActivityThread.currentActivityThread().getHandler().post( () -> context.dispatchConfigurationChanged(newConfig)); } if (displayChanged) { context.updateDisplay(newDisplayId); Loading core/java/android/content/ComponentCallbacksController.java 0 → 100644 +127 −0 Original line number Diff line number Diff line /* * 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.content; import android.annotation.NonNull; import android.content.res.Configuration; import com.android.internal.annotations.GuardedBy; import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; /** * A helper class to manage {@link ComponentCallbacks} and {@link ComponentCallbacks2}, such as * registering ,unregistering {@link ComponentCallbacks} and sending callbacks to all registered * {@link ComponentCallbacks}. * * @see Context#registerComponentCallbacks(ComponentCallbacks) * @see Context#unregisterComponentCallbacks(ComponentCallbacks) * @see ComponentCallbacks * @see ComponentCallbacks2 * * @hide */ public class ComponentCallbacksController { @GuardedBy("mLock") private List<ComponentCallbacks> mComponentCallbacks; private final Object mLock = new Object(); /** * Register the {@link ComponentCallbacks}. * * @see Context#registerComponentCallbacks(ComponentCallbacks) */ public void registerCallbacks(@NonNull ComponentCallbacks callbacks) { synchronized (mLock) { if (mComponentCallbacks == null) { mComponentCallbacks = new ArrayList<>(); } mComponentCallbacks.add(callbacks); } } /** * Unregister the {@link ComponentCallbacks}. * * @see Context#unregisterComponentCallbacks(ComponentCallbacks) */ public void unregisterCallbacks(@NonNull ComponentCallbacks callbacks) { synchronized (mLock) { if (mComponentCallbacks == null || mComponentCallbacks.isEmpty()) { return; } mComponentCallbacks.remove(callbacks); } } /** * Clear all registered {@link ComponentCallbacks}. * It is useful when the associated {@link Context} is going to be released. */ public void clearCallbacks() { synchronized (mLock) { if (mComponentCallbacks != null) { mComponentCallbacks.clear(); } } } /** * Sending {@link ComponentCallbacks#onConfigurationChanged(Configuration)} to all registered * {@link ComponentCallbacks}. */ public void dispatchConfigurationChanged(@NonNull Configuration newConfig) { forAllComponentCallbacks(callbacks -> callbacks.onConfigurationChanged(newConfig)); } /** * Sending {@link ComponentCallbacks#onLowMemory()} to all registered * {@link ComponentCallbacks}. */ public void dispatchLowMemory() { forAllComponentCallbacks(ComponentCallbacks::onLowMemory); } /** * Sending {@link ComponentCallbacks2#onTrimMemory(int)} to all registered * {@link ComponentCallbacks2}. */ public void dispatchTrimMemory(int level) { forAllComponentCallbacks(callbacks -> { if (callbacks instanceof ComponentCallbacks2) { ((ComponentCallbacks2) callbacks).onTrimMemory(level); } }); } private void forAllComponentCallbacks(Consumer<ComponentCallbacks> callbacksConsumer) { final ComponentCallbacks[] callbacksArray; synchronized (mLock) { if (mComponentCallbacks == null || mComponentCallbacks.isEmpty()) { return; } callbacksArray = new ComponentCallbacks[mComponentCallbacks.size()]; mComponentCallbacks.toArray(callbacksArray); } for (ComponentCallbacks callbacks : callbacksArray) { callbacksConsumer.accept(callbacks); } } } core/java/android/content/Context.java +20 −1 Original line number Diff line number Diff line Loading @@ -658,9 +658,18 @@ public abstract class Context { * methods of activities and other components are called. Note that you * <em>must</em> be sure to use {@link #unregisterComponentCallbacks} when * appropriate in the future; this will not be removed for you. * <p> * After {@link Build.VERSION_CODES#S}, Registering the ComponentCallbacks to Context created * via {@link #createWindowContext(int, Bundle)} or * {@link #createWindowContext(Display, int, Bundle)} will receive * {@link ComponentCallbacks#onConfigurationChanged(Configuration)} from Window Context rather * than its base application. It is helpful if you want to handle UI components that * associated with the Window Context when the Window Context has configuration changes.</p> * * @param callback The interface to call. This can be either a * {@link ComponentCallbacks} or {@link ComponentCallbacks2} interface. * * @see Context#createWindowContext(int, Bundle) */ public void registerComponentCallbacks(ComponentCallbacks callback) { getApplicationContext().registerComponentCallbacks(callback); Loading Loading @@ -6358,6 +6367,16 @@ public abstract class Context { * windowContext.getSystemService(WindowManager.class).addView(overlayView, mParams); * </pre> * <p> * After {@link Build.VERSION_CODES#S}, window context provides the capability to listen to its * {@link Configuration} changes by calling * {@link #registerComponentCallbacks(ComponentCallbacks)}, while other kinds of {@link Context} * will register the {@link ComponentCallbacks} to {@link #getApplicationContext() its * Application context}. Note that window context only propagate * {@link ComponentCallbacks#onConfigurationChanged(Configuration)} callback. * {@link ComponentCallbacks#onLowMemory()} or other callbacks in {@link ComponentCallbacks2} * won't be invoked. * </p> * <p> * Note that using {@link android.app.Application} or {@link android.app.Service} context for * UI-related queries may result in layout or continuity issues on devices with variable screen * sizes (e.g. foldables) or in multi-window modes, since these non-UI contexts may not reflect Loading Loading
core/java/android/app/Application.java +9 −40 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentCallbacks; import android.content.ComponentCallbacks2; import android.content.ComponentCallbacksController; import android.content.Context; import android.content.ContextWrapper; import android.content.Intent; Loading Loading @@ -53,14 +54,14 @@ import java.util.ArrayList; public class Application extends ContextWrapper implements ComponentCallbacks2 { private static final String TAG = "Application"; @UnsupportedAppUsage private ArrayList<ComponentCallbacks> mComponentCallbacks = new ArrayList<ComponentCallbacks>(); @UnsupportedAppUsage private ArrayList<ActivityLifecycleCallbacks> mActivityLifecycleCallbacks = new ArrayList<ActivityLifecycleCallbacks>(); @UnsupportedAppUsage private ArrayList<OnProvideAssistDataListener> mAssistCallbacks = null; private final ComponentCallbacksController mCallbacksController = new ComponentCallbacksController(); /** @hide */ @UnsupportedAppUsage public LoadedApk mLoadedApk; Loading Loading @@ -260,47 +261,25 @@ public class Application extends ContextWrapper implements ComponentCallbacks2 { @CallSuper public void onConfigurationChanged(@NonNull Configuration newConfig) { Object[] callbacks = collectComponentCallbacks(); if (callbacks != null) { for (int i=0; i<callbacks.length; i++) { ((ComponentCallbacks)callbacks[i]).onConfigurationChanged(newConfig); } } mCallbacksController.dispatchConfigurationChanged(newConfig); } @CallSuper public void onLowMemory() { Object[] callbacks = collectComponentCallbacks(); if (callbacks != null) { for (int i=0; i<callbacks.length; i++) { ((ComponentCallbacks)callbacks[i]).onLowMemory(); } } mCallbacksController.dispatchLowMemory(); } @CallSuper public void onTrimMemory(int level) { Object[] callbacks = collectComponentCallbacks(); if (callbacks != null) { for (int i=0; i<callbacks.length; i++) { Object c = callbacks[i]; if (c instanceof ComponentCallbacks2) { ((ComponentCallbacks2)c).onTrimMemory(level); } } } mCallbacksController.dispatchTrimMemory(level); } public void registerComponentCallbacks(ComponentCallbacks callback) { synchronized (mComponentCallbacks) { mComponentCallbacks.add(callback); } mCallbacksController.registerCallbacks(callback); } public void unregisterComponentCallbacks(ComponentCallbacks callback) { synchronized (mComponentCallbacks) { mComponentCallbacks.remove(callback); } mCallbacksController.unregisterCallbacks(callback); } public void registerActivityLifecycleCallbacks(ActivityLifecycleCallbacks callback) { Loading Loading @@ -575,16 +554,6 @@ public class Application extends ContextWrapper implements ComponentCallbacks2 { } } private Object[] collectComponentCallbacks() { Object[] callbacks = null; synchronized (mComponentCallbacks) { if (mComponentCallbacks.size() > 0) { callbacks = mComponentCallbacks.toArray(); } } return callbacks; } @UnsupportedAppUsage private Object[] collectActivityLifecycleCallbacks() { Object[] callbacks = null; Loading
core/java/android/app/WindowContext.java +21 −0 Original line number Diff line number Diff line Loading @@ -20,8 +20,11 @@ import static android.view.WindowManagerImpl.createWindowContextWindowManager; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UiContext; import android.content.ComponentCallbacks; import android.content.ComponentCallbacksController; import android.content.Context; import android.content.ContextWrapper; import android.content.res.Configuration; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; Loading Loading @@ -49,6 +52,8 @@ public class WindowContext extends ContextWrapper { private final IWindowManager mWms; private final WindowTokenClient mToken; private boolean mListenerRegistered; private final ComponentCallbacksController mCallbacksController = new ComponentCallbacksController(); /** * Default constructor. Will generate a {@link WindowTokenClient} and attach this context to Loading Loading @@ -131,8 +136,24 @@ public class WindowContext extends ContextWrapper { } void destroy() { mCallbacksController.clearCallbacks(); final ContextImpl impl = (ContextImpl) getBaseContext(); impl.scheduleFinalCleanup(getClass().getName(), "WindowContext"); Reference.reachabilityFence(this); } @Override public void registerComponentCallbacks(@NonNull ComponentCallbacks callback) { mCallbacksController.registerCallbacks(callback); } @Override public void unregisterComponentCallbacks(@NonNull ComponentCallbacks callback) { mCallbacksController.unregisterCallbacks(callback); } /** Dispatch {@link Configuration} to each {@link ComponentCallbacks}. */ void dispatchConfigurationChanged(@NonNull Configuration newConfig) { mCallbacksController.dispatchConfigurationChanged(newConfig); } }
core/java/android/app/WindowTokenClient.java +3 −1 Original line number Diff line number Diff line Loading @@ -61,7 +61,7 @@ public class WindowTokenClient extends IWindowToken.Stub { @Override public void onConfigurationChanged(Configuration newConfig, int newDisplayId) { final Context context = mContextRef.get(); final WindowContext context = mContextRef.get(); if (context == null) { return; } Loading @@ -72,6 +72,8 @@ public class WindowTokenClient extends IWindowToken.Stub { if (displayChanged || configChanged) { // TODO(ag/9789103): update resource manager logic to track non-activity tokens mResourcesManager.updateResourcesForActivity(this, newConfig, newDisplayId); ActivityThread.currentActivityThread().getHandler().post( () -> context.dispatchConfigurationChanged(newConfig)); } if (displayChanged) { context.updateDisplay(newDisplayId); Loading
core/java/android/content/ComponentCallbacksController.java 0 → 100644 +127 −0 Original line number Diff line number Diff line /* * 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.content; import android.annotation.NonNull; import android.content.res.Configuration; import com.android.internal.annotations.GuardedBy; import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; /** * A helper class to manage {@link ComponentCallbacks} and {@link ComponentCallbacks2}, such as * registering ,unregistering {@link ComponentCallbacks} and sending callbacks to all registered * {@link ComponentCallbacks}. * * @see Context#registerComponentCallbacks(ComponentCallbacks) * @see Context#unregisterComponentCallbacks(ComponentCallbacks) * @see ComponentCallbacks * @see ComponentCallbacks2 * * @hide */ public class ComponentCallbacksController { @GuardedBy("mLock") private List<ComponentCallbacks> mComponentCallbacks; private final Object mLock = new Object(); /** * Register the {@link ComponentCallbacks}. * * @see Context#registerComponentCallbacks(ComponentCallbacks) */ public void registerCallbacks(@NonNull ComponentCallbacks callbacks) { synchronized (mLock) { if (mComponentCallbacks == null) { mComponentCallbacks = new ArrayList<>(); } mComponentCallbacks.add(callbacks); } } /** * Unregister the {@link ComponentCallbacks}. * * @see Context#unregisterComponentCallbacks(ComponentCallbacks) */ public void unregisterCallbacks(@NonNull ComponentCallbacks callbacks) { synchronized (mLock) { if (mComponentCallbacks == null || mComponentCallbacks.isEmpty()) { return; } mComponentCallbacks.remove(callbacks); } } /** * Clear all registered {@link ComponentCallbacks}. * It is useful when the associated {@link Context} is going to be released. */ public void clearCallbacks() { synchronized (mLock) { if (mComponentCallbacks != null) { mComponentCallbacks.clear(); } } } /** * Sending {@link ComponentCallbacks#onConfigurationChanged(Configuration)} to all registered * {@link ComponentCallbacks}. */ public void dispatchConfigurationChanged(@NonNull Configuration newConfig) { forAllComponentCallbacks(callbacks -> callbacks.onConfigurationChanged(newConfig)); } /** * Sending {@link ComponentCallbacks#onLowMemory()} to all registered * {@link ComponentCallbacks}. */ public void dispatchLowMemory() { forAllComponentCallbacks(ComponentCallbacks::onLowMemory); } /** * Sending {@link ComponentCallbacks2#onTrimMemory(int)} to all registered * {@link ComponentCallbacks2}. */ public void dispatchTrimMemory(int level) { forAllComponentCallbacks(callbacks -> { if (callbacks instanceof ComponentCallbacks2) { ((ComponentCallbacks2) callbacks).onTrimMemory(level); } }); } private void forAllComponentCallbacks(Consumer<ComponentCallbacks> callbacksConsumer) { final ComponentCallbacks[] callbacksArray; synchronized (mLock) { if (mComponentCallbacks == null || mComponentCallbacks.isEmpty()) { return; } callbacksArray = new ComponentCallbacks[mComponentCallbacks.size()]; mComponentCallbacks.toArray(callbacksArray); } for (ComponentCallbacks callbacks : callbacksArray) { callbacksConsumer.accept(callbacks); } } }
core/java/android/content/Context.java +20 −1 Original line number Diff line number Diff line Loading @@ -658,9 +658,18 @@ public abstract class Context { * methods of activities and other components are called. Note that you * <em>must</em> be sure to use {@link #unregisterComponentCallbacks} when * appropriate in the future; this will not be removed for you. * <p> * After {@link Build.VERSION_CODES#S}, Registering the ComponentCallbacks to Context created * via {@link #createWindowContext(int, Bundle)} or * {@link #createWindowContext(Display, int, Bundle)} will receive * {@link ComponentCallbacks#onConfigurationChanged(Configuration)} from Window Context rather * than its base application. It is helpful if you want to handle UI components that * associated with the Window Context when the Window Context has configuration changes.</p> * * @param callback The interface to call. This can be either a * {@link ComponentCallbacks} or {@link ComponentCallbacks2} interface. * * @see Context#createWindowContext(int, Bundle) */ public void registerComponentCallbacks(ComponentCallbacks callback) { getApplicationContext().registerComponentCallbacks(callback); Loading Loading @@ -6358,6 +6367,16 @@ public abstract class Context { * windowContext.getSystemService(WindowManager.class).addView(overlayView, mParams); * </pre> * <p> * After {@link Build.VERSION_CODES#S}, window context provides the capability to listen to its * {@link Configuration} changes by calling * {@link #registerComponentCallbacks(ComponentCallbacks)}, while other kinds of {@link Context} * will register the {@link ComponentCallbacks} to {@link #getApplicationContext() its * Application context}. Note that window context only propagate * {@link ComponentCallbacks#onConfigurationChanged(Configuration)} callback. * {@link ComponentCallbacks#onLowMemory()} or other callbacks in {@link ComponentCallbacks2} * won't be invoked. * </p> * <p> * Note that using {@link android.app.Application} or {@link android.app.Service} context for * UI-related queries may result in layout or continuity issues on devices with variable screen * sizes (e.g. foldables) or in multi-window modes, since these non-UI contexts may not reflect Loading