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

Commit e3d75ac2 authored by Winson Chung's avatar Winson Chung
Browse files

Clean up task stack listeners

- Have shell provided a task stack listener that various controllers can
  add to, and remove registration from outside the controllers (this
  also removes methods on the interface exposed only for use from the
  task stack listeners)
- Move copy of InputConsumerController into shell (shared lib version
  can probably be refactored into use just by launcher for gesture nav)
- Hide remaining wm components that are exposed now that all the
  features are in the Shell

Bug: 169894807
Test: atest WMShellUnitTests WMShellTest
Test: Verify Pip/Split/One handed still behaves as expected
Change-Id: If0f50f6b701ac4b013a9e41fe6f88857ea35bf5b
parent 327ff53a
Loading
Loading
Loading
Loading
+182 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2020 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 com.android.wm.shell.common;

import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.INPUT_CONSUMER_PIP;
import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;

import android.os.Binder;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.util.Log;
import android.view.BatchedInputEventReceiver;
import android.view.Choreographer;
import android.view.IWindowManager;
import android.view.InputChannel;
import android.view.InputEvent;
import android.view.WindowManagerGlobal;

import java.io.PrintWriter;

/**
 * Manages the input consumer that allows the Shell to directly receive input.
 */
public class PipInputConsumer {

    private static final String TAG = PipInputConsumer.class.getSimpleName();

    /**
     * Listener interface for callers to subscribe to input events.
     */
    public interface InputListener {
        /** Handles any input event. */
        boolean onInputEvent(InputEvent ev);
    }

    /**
     * Listener interface for callers to learn when this class is registered or unregistered with
     * window manager
     */
    public interface RegistrationListener {
        void onRegistrationChanged(boolean isRegistered);
    }

    /**
     * Input handler used for the input consumer. Input events are batched and consumed with the
     * SurfaceFlinger vsync.
     */
    private final class InputEventReceiver extends BatchedInputEventReceiver {

        InputEventReceiver(InputChannel inputChannel, Looper looper,
                Choreographer choreographer) {
            super(inputChannel, looper, choreographer);
        }

        @Override
        public void onInputEvent(InputEvent event) {
            boolean handled = true;
            try {
                if (mListener != null) {
                    handled = mListener.onInputEvent(event);
                }
            } finally {
                finishInputEvent(event, handled);
            }
        }
    }

    private final IWindowManager mWindowManager;
    private final IBinder mToken;
    private final String mName;

    private InputEventReceiver mInputEventReceiver;
    private InputListener mListener;
    private RegistrationListener mRegistrationListener;

    /**
     * @param name the name corresponding to the input consumer that is defined in the system.
     */
    public PipInputConsumer(IWindowManager windowManager, String name) {
        mWindowManager = windowManager;
        mToken = new Binder();
        mName = name;
    }

    /**
     * Sets the input listener.
     */
    public void setInputListener(InputListener listener) {
        mListener = listener;
    }

    /**
     * Sets the registration listener.
     */
    public void setRegistrationListener(RegistrationListener listener) {
        mRegistrationListener = listener;
        if (mRegistrationListener != null) {
            mRegistrationListener.onRegistrationChanged(mInputEventReceiver != null);
        }
    }

    /**
     * Check if the InputConsumer is currently registered with WindowManager
     *
     * @return {@code true} if registered, {@code false} if not.
     */
    public boolean isRegistered() {
        return mInputEventReceiver != null;
    }

    /**
     * Registers the input consumer.
     */
    public void registerInputConsumer() {
        registerInputConsumer(false);
    }

    /**
     * Registers the input consumer.
     * @param withSfVsync the flag set using sf vsync signal or no
     */
    public void registerInputConsumer(boolean withSfVsync) {
        if (mInputEventReceiver != null) {
            return;
        }
        final InputChannel inputChannel = new InputChannel();
        try {
            // TODO(b/113087003): Support Picture-in-picture in multi-display.
            mWindowManager.destroyInputConsumer(mName, DEFAULT_DISPLAY);
            mWindowManager.createInputConsumer(mToken, mName, DEFAULT_DISPLAY, inputChannel);
        } catch (RemoteException e) {
            Log.e(TAG, "Failed to create input consumer", e);
        }
        mInputEventReceiver = new InputEventReceiver(inputChannel, Looper.myLooper(),
                withSfVsync ? Choreographer.getSfInstance() : Choreographer.getInstance());
        if (mRegistrationListener != null) {
            mRegistrationListener.onRegistrationChanged(true /* isRegistered */);
        }
    }

    /**
     * Unregisters the input consumer.
     */
    public void unregisterInputConsumer() {
        if (mInputEventReceiver == null) {
            return;
        }
        try {
            // TODO(b/113087003): Support Picture-in-picture in multi-display.
            mWindowManager.destroyInputConsumer(mName, DEFAULT_DISPLAY);
        } catch (RemoteException e) {
            Log.e(TAG, "Failed to destroy input consumer", e);
        }
        mInputEventReceiver.dispose();
        mInputEventReceiver = null;
        if (mRegistrationListener != null) {
            mRegistrationListener.onRegistrationChanged(false /* isRegistered */);
        }
    }

    public void dump(PrintWriter pw, String prefix) {
        final String innerPrefix = prefix + "  ";
        pw.println(prefix + TAG);
        pw.println(innerPrefix + "registered=" + (mInputEventReceiver != null));
    }
}
+116 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2020 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 com.android.wm.shell.common;

import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ITaskStackListener;
import android.app.TaskInfo;
import android.content.ComponentName;
import android.os.IBinder;

import androidx.annotation.BinderThread;
import androidx.annotation.MainThread;

/**
 * An interface to track task stack changes. Classes should implement this instead of
 * {@link ITaskStackListener} to reduce IPC calls from system services.
 */
public interface TaskStackListenerCallback {

    @MainThread
    default void onRecentTaskListUpdated() { }

    @MainThread
    default void onRecentTaskListFrozenChanged(boolean frozen) { }

    @BinderThread
    default void onTaskStackChangedBackground() { }

    @MainThread
    default void onTaskStackChanged() { }

    @MainThread
    default void onTaskProfileLocked(int taskId, int userId) { }

    @MainThread
    default void onTaskDisplayChanged(int taskId, int newDisplayId) { }

    @MainThread
    default void onTaskCreated(int taskId, ComponentName componentName) { }

    @MainThread
    default void onTaskRemoved(int taskId) { }

    @MainThread
    default void onTaskMovedToFront(int taskId) { }

    @MainThread
    default void onTaskMovedToFront(RunningTaskInfo taskInfo) {
        onTaskMovedToFront(taskInfo.taskId);
    }

    @MainThread
    default void onTaskDescriptionChanged(RunningTaskInfo taskInfo) { }

    @MainThread
    default void onTaskSnapshotChanged(int taskId, ActivityManager.TaskSnapshot snapshot) { }

    @MainThread
    default void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) { }

    @MainThread
    default void onActivityRestartAttempt(RunningTaskInfo task, boolean homeTaskVisible,
            boolean clearedTask, boolean wasVisible) { }

    @MainThread
    default void onActivityPinned(String packageName, int userId, int taskId, int stackId) { }

    @MainThread
    default void onActivityUnpinned() { }

    @MainThread
    default void onActivityForcedResizable(String packageName, int taskId, int reason) { }

    @MainThread
    default void onActivityDismissingDockedStack() { }

    @MainThread
    default void onActivityLaunchOnSecondaryDisplayFailed() { }

    @MainThread
    default void onActivityLaunchOnSecondaryDisplayFailed(RunningTaskInfo taskInfo) {
        onActivityLaunchOnSecondaryDisplayFailed();
    }

    @MainThread
    default void onActivityLaunchOnSecondaryDisplayRerouted() { }

    @MainThread
    default void onActivityLaunchOnSecondaryDisplayRerouted(RunningTaskInfo taskInfo) {
        onActivityLaunchOnSecondaryDisplayRerouted();
    }

    @MainThread
    default void onActivityRequestedOrientationChanged(int taskId, int requestedOrientation) { }

    @MainThread
    default void onActivityRotation(int displayId) { }

    @MainThread
    default void onSizeCompatModeActivityChanged(int displayId, IBinder activityToken) { }
}
+437 −0

File added.

Preview size limit exceeded, changes collapsed.

+21 −3
Original line number Original line Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.wm.shell.onehanded;
import static android.os.UserHandle.USER_CURRENT;
import static android.os.UserHandle.USER_CURRENT;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.DEFAULT_DISPLAY;


import android.content.ComponentName;
import android.content.Context;
import android.content.Context;
import android.content.om.IOverlayManager;
import android.content.om.IOverlayManager;
import android.content.om.OverlayInfo;
import android.content.om.OverlayInfo;
@@ -38,6 +39,8 @@ import androidx.annotation.VisibleForTesting;


import com.android.wm.shell.common.DisplayChangeController;
import com.android.wm.shell.common.DisplayChangeController;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.onehanded.OneHandedGestureHandler.OneHandedGestureEventCallback;
import com.android.wm.shell.onehanded.OneHandedGestureHandler.OneHandedGestureEventCallback;


import java.io.PrintWriter;
import java.io.PrintWriter;
@@ -164,7 +167,8 @@ public class OneHandedController implements OneHanded {
     */
     */
    @Nullable
    @Nullable
    public static OneHandedController create(
    public static OneHandedController create(
            Context context, DisplayController displayController) {
            Context context, DisplayController displayController,
            TaskStackListenerImpl taskStackListener) {
        if (!SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false)) {
        if (!SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false)) {
            Slog.w(TAG, "Device doesn't support OneHanded feature");
            Slog.w(TAG, "Device doesn't support OneHanded feature");
            return null;
            return null;
@@ -181,7 +185,7 @@ public class OneHandedController implements OneHanded {
        IOverlayManager overlayManager = IOverlayManager.Stub.asInterface(
        IOverlayManager overlayManager = IOverlayManager.Stub.asInterface(
                ServiceManager.getService(Context.OVERLAY_SERVICE));
                ServiceManager.getService(Context.OVERLAY_SERVICE));
        return new OneHandedController(context, displayController, organizer, touchHandler,
        return new OneHandedController(context, displayController, organizer, touchHandler,
                tutorialHandler, gestureHandler, overlayManager);
                tutorialHandler, gestureHandler, overlayManager, taskStackListener);
    }
    }


    @VisibleForTesting
    @VisibleForTesting
@@ -191,7 +195,8 @@ public class OneHandedController implements OneHanded {
            OneHandedTouchHandler touchHandler,
            OneHandedTouchHandler touchHandler,
            OneHandedTutorialHandler tutorialHandler,
            OneHandedTutorialHandler tutorialHandler,
            OneHandedGestureHandler gestureHandler,
            OneHandedGestureHandler gestureHandler,
            IOverlayManager overlayManager) {
            IOverlayManager overlayManager,
            TaskStackListenerImpl taskStackListener) {
        mContext = context;
        mContext = context;
        mDisplayAreaOrganizer = displayAreaOrganizer;
        mDisplayAreaOrganizer = displayAreaOrganizer;
        mDisplayController = displayController;
        mDisplayController = displayController;
@@ -216,6 +221,19 @@ public class OneHandedController implements OneHanded {
        setupTimeoutListener();
        setupTimeoutListener();
        setupGesturalOverlay();
        setupGesturalOverlay();
        updateSettings();
        updateSettings();

        taskStackListener.addListener(
                new TaskStackListenerCallback() {
                    @Override
                    public void onTaskCreated(int taskId, ComponentName componentName) {
                        stopOneHanded(OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_APP_TAPS_OUT);
                    }

                    @Override
                    public void onTaskMovedToFront(int taskId) {
                        stopOneHanded(OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_APP_TAPS_OUT);
                    }
                });
    }
    }


    /**
    /**
+0 −30
Original line number Original line Diff line number Diff line
@@ -80,30 +80,6 @@ public interface Pip {
    default void movePipToFullscreen() {
    default void movePipToFullscreen() {
    }
    }


    /**
     * Called whenever an Activity is moved to the pinned stack from another stack.
     */
    default void onActivityPinned(String packageName) {
    }

    /**
     * Called whenever an Activity is moved from the pinned stack to another stack
     */
    default void onActivityUnpinned(ComponentName topActivity) {
    }

    /**
     * Called whenever IActivityManager.startActivity is called on an activity that is already
     * running, but the task is either brought to the front or a new Intent is delivered to it.
     *
     * @param task        information about the task the activity was relaunched into
     * @param clearedTask whether or not the launch activity also cleared the task as a part of
     *                    starting
     */
    default void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
            boolean clearedTask) {
    }

    /**
    /**
     * Called when display size or font size of settings changed
     * Called when display size or font size of settings changed
     */
     */
@@ -131,12 +107,6 @@ public interface Pip {
    default void onSystemUiStateChanged(boolean isSysUiStateValid, int flag) {
    default void onSystemUiStateChanged(boolean isSysUiStateValid, int flag) {
    }
    }


    /**
     * Called when task stack changed.
     */
    default void onTaskStackChanged() {
    }

    /**
    /**
     * Resize the Pip to the appropriate size for the input state.
     * Resize the Pip to the appropriate size for the input state.
     *
     *
Loading