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

Commit 89bd9fed authored by Riddle Hsu's avatar Riddle Hsu Committed by Android (Google) Code Review
Browse files

Merge "Add support to listen the proposed rotation from window manager"

parents cf576eab 183f5d82
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -53734,11 +53734,13 @@ package android.view {
  public interface WindowManager extends android.view.ViewManager {
    method public default void addCrossWindowBlurEnabledListener(@NonNull java.util.function.Consumer<java.lang.Boolean>);
    method public default void addCrossWindowBlurEnabledListener(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
    method public default void addProposedRotationListener(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.IntConsumer);
    method @NonNull public default android.view.WindowMetrics getCurrentWindowMetrics();
    method @Deprecated public android.view.Display getDefaultDisplay();
    method @NonNull public default android.view.WindowMetrics getMaximumWindowMetrics();
    method public default boolean isCrossWindowBlurEnabled();
    method public default void removeCrossWindowBlurEnabledListener(@NonNull java.util.function.Consumer<java.lang.Boolean>);
    method public default void removeProposedRotationListener(@NonNull java.util.function.IntConsumer);
    method public void removeViewImmediate(android.view.View);
    field public static final String PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE = "android.window.PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE";
    field public static final String PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED = "android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED";
+3 −0
Original line number Diff line number Diff line
@@ -284,6 +284,9 @@ interface IWindowManager
    @UnsupportedAppUsage
    void removeRotationWatcher(IRotationWatcher watcher);

    /** Registers the listener to the context token and returns the current proposed rotation. */
    int registerProposedRotationListener(IBinder contextToken, IRotationWatcher listener);

    /**
     * Determine the preferred edge of the screen to pin the compact options menu against.
     *
+33 −0
Original line number Diff line number Diff line
@@ -129,6 +129,7 @@ import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
import java.util.function.IntConsumer;

/**
 * The interface that apps use to talk to the window manager.
@@ -1357,6 +1358,38 @@ public interface WindowManager extends ViewManager {
    default void removeCrossWindowBlurEnabledListener(@NonNull Consumer<Boolean> listener) {
    }

    /**
     * Adds a listener to start monitoring the proposed rotation of the current associated context.
     * It reports the current recommendation for the rotation that takes various factors (e.g.
     * sensor, context, device state, etc) into account. The proposed rotation might not be applied
     * by the system automatically due to the application's active preference to lock the
     * orientation (e.g. with {@link android.app.Activity#setRequestedOrientation(int)}). This
     * listener gives application an opportunity to selectively react to device orientation changes.
     * The newly added listener will be called with current proposed rotation. Note that the context
     * of this window manager instance must be a {@link android.annotation.UiContext}.
     *
     * @param executor The executor on which callback method will be invoked.
     * @param listener Called when the proposed rotation for the context is being delivered.
     *                 The reported rotation can be {@link Surface#ROTATION_0},
     *                 {@link Surface#ROTATION_90}, {@link Surface#ROTATION_180} and
     *                 {@link Surface#ROTATION_270}.
     * @throws UnsupportedOperationException if this method is called on an instance that is not
     *         associated with a {@link android.annotation.UiContext}.
     */
    default void addProposedRotationListener(@NonNull @CallbackExecutor Executor executor,
            @NonNull IntConsumer listener) {
    }

    /**
     * Removes a listener, previously added with {@link #addProposedRotationListener}. It is
     * recommended to call when the associated context no longer has visible components. No-op if
     * the provided listener is not registered.
     *
     * @param listener The listener to be removed.
     */
    default void removeProposedRotationListener(@NonNull IntConsumer listener) {
    }

    /**
     * @hide
     */
+127 −0
Original line number Diff line number Diff line
@@ -38,7 +38,11 @@ import com.android.internal.util.FastPrintWriter;
import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.WeakHashMap;
import java.util.concurrent.Executor;
import java.util.function.IntConsumer;

/**
 * Provides low-level communication with the system window manager for
@@ -136,6 +140,9 @@ public final class WindowManagerGlobal {

    private final ArrayList<ViewRootImpl> mWindowlessRoots = new ArrayList<ViewRootImpl>();

    /** A context token only has one remote registration to system. */
    private WeakHashMap<IBinder, ProposedRotationListenerDelegate> mProposedRotationListenerMap;

    private Runnable mSystemPropertyUpdater;

    private WindowManagerGlobal() {
@@ -666,6 +673,126 @@ public final class WindowManagerGlobal {
        }
    }

    /** Registers the listener to the context token and returns the current proposed rotation. */
    public void registerProposedRotationListener(IBinder contextToken, Executor executor,
            IntConsumer listener) {
        ProposedRotationListenerDelegate delegate;
        synchronized (mLock) {
            if (mProposedRotationListenerMap == null) {
                mProposedRotationListenerMap = new WeakHashMap<>(1);
            }
            delegate = mProposedRotationListenerMap.get(contextToken);
            final ProposedRotationListenerDelegate existingDelegate = delegate;
            if (delegate == null) {
                mProposedRotationListenerMap.put(contextToken,
                        delegate = new ProposedRotationListenerDelegate());
            }
            if (!delegate.add(executor, listener)) {
                // Duplicated listener.
                return;
            }
            if (existingDelegate != null) {
                executor.execute(() -> listener.accept(existingDelegate.mLastRotation));
                return;
            }
        }
        try {
            final int currentRotation = getWindowManagerService().registerProposedRotationListener(
                    contextToken, delegate);
            delegate.onRotationChanged(currentRotation);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /** Unregisters the proposed rotation listener of the given token. */
    public void unregisterProposedRotationListener(IBinder contextToken, IntConsumer listener) {
        final ProposedRotationListenerDelegate delegate;
        synchronized (mLock) {
            if (mProposedRotationListenerMap == null) {
                return;
            }
            delegate = mProposedRotationListenerMap.get(contextToken);
            if (delegate == null) {
                return;
            }
            if (delegate.remove(listener)) {
                // The delegate becomes empty.
                mProposedRotationListenerMap.remove(contextToken);
            } else {
                // The delegate still contains other listeners.
                return;
            }
        }
        try {
            getWindowManagerService().removeRotationWatcher(delegate);
        } catch (RemoteException e) {
            e.rethrowFromSystemServer();
        }
    }

    private static class ProposedRotationListenerDelegate extends IRotationWatcher.Stub {
        static class ListenerWrapper {
            final Executor mExecutor;
            final WeakReference<IntConsumer> mListener;

            ListenerWrapper(Executor executor, IntConsumer listener) {
                mExecutor = executor;
                mListener = new WeakReference<>(listener);
            }
        }

        /** The registered listeners. */
        private final ArrayList<ListenerWrapper> mListeners = new ArrayList<>(1);
        /** A thread-safe copy of registered listeners for dispatching events. */
        private volatile ListenerWrapper[] mListenerArray;
        int mLastRotation;

        boolean add(Executor executor, IntConsumer listener) {
            for (int i = mListeners.size() - 1; i >= 0; i--) {
                if (mListeners.get(i).mListener.get() == listener) {
                    // Ignore adding duplicated listener.
                    return false;
                }
            }
            mListeners.add(new ListenerWrapper(executor, listener));
            mListenerArray = mListeners.toArray(new ListenerWrapper[0]);
            return true;
        }

        boolean remove(IntConsumer listener) {
            for (int i = mListeners.size() - 1; i >= 0; i--) {
                if (mListeners.get(i).mListener.get() == listener) {
                    mListeners.remove(i);
                    mListenerArray = mListeners.toArray(new ListenerWrapper[0]);
                    return mListeners.isEmpty();
                }
            }
            return false;
        }

        @Override
        public void onRotationChanged(int rotation) {
            mLastRotation = rotation;
            boolean alive = false;
            for (ListenerWrapper listenerWrapper : mListenerArray) {
                final IntConsumer listener = listenerWrapper.mListener.get();
                if (listener != null) {
                    listenerWrapper.mExecutor.execute(() -> listener.accept(rotation));
                    alive = true;
                }
            }
            if (!alive) {
                // Unregister if there is no strong reference.
                try {
                    getWindowManagerService().removeRotationWatcher(this);
                } catch (RemoteException e) {
                    e.rethrowFromSystemServer();
                }
            }
        }
    }

    /** @hide */
    public void addWindowlessRoot(ViewRootImpl impl) {
        synchronized (mLock) {
+21 −0
Original line number Diff line number Diff line
@@ -47,9 +47,11 @@ import com.android.internal.os.IResultReceiver;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
import java.util.function.IntConsumer;

/**
 * Provides low-level communication with the system window manager for
@@ -337,6 +339,25 @@ public final class WindowManagerImpl implements WindowManager {
        CrossWindowBlurListeners.getInstance().removeListener(listener);
    }

    @Override
    public void addProposedRotationListener(@NonNull @CallbackExecutor Executor executor,
            @NonNull IntConsumer listener) {
        Objects.requireNonNull(executor, "executor must not be null");
        Objects.requireNonNull(listener, "listener must not be null");
        final IBinder contextToken = Context.getToken(mContext);
        if (contextToken == null) {
            throw new UnsupportedOperationException("The context of this window manager instance "
                    + "must be a UI context, e.g. an Activity or a Context created by "
                    + "Context#createWindowContext()");
        }
        mGlobal.registerProposedRotationListener(contextToken, executor, listener);
    }

    @Override
    public void removeProposedRotationListener(@NonNull IntConsumer listener) {
        mGlobal.unregisterProposedRotationListener(Context.getToken(mContext), listener);
    }

    @Override
    public boolean isTaskSnapshotSupported() {
        try {
Loading