Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b661dcbd authored by Charles Chen's avatar Charles Chen Committed by Android (Google) Code Review
Browse files

Merge "Enable to listen to WindowContext's config changes" into sc-dev

parents fe7b3926 07838114
Loading
Loading
Loading
Loading
+9 −40
Original line number Diff line number Diff line
@@ -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;
@@ -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;
@@ -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) {
@@ -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;
+21 −0
Original line number Diff line number Diff line
@@ -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;
@@ -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
@@ -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);
    }
}
+3 −1
Original line number Diff line number Diff line
@@ -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;
        }
@@ -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);
+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);
        }
    }
}
+20 −1
Original line number Diff line number Diff line
@@ -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);
@@ -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