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

Commit b73cc69f authored by Winson Chung's avatar Winson Chung Committed by Android (Google) Code Review
Browse files

Merge changes from topic "shell_task_stack_listener"

* changes:
  6/ Fix some flakey issues with dragging to split
  Clean up task stack listeners
parents be00e1b8 6dd73e98
Loading
Loading
Loading
Loading
+1 −7
Original line number Diff line number Diff line
@@ -346,15 +346,9 @@ public class ShellTaskOrganizer extends TaskOrganizer {
        return mTaskListeners.get(taskListenerType);
    }

    @WindowingMode
    public static int getWindowingMode(RunningTaskInfo taskInfo) {
        return taskInfo.configuration.windowConfiguration.getWindowingMode();
    }

    @VisibleForTesting
    static @TaskListenerType int taskInfoToTaskListenerType(RunningTaskInfo runningTaskInfo) {
        final int windowingMode = getWindowingMode(runningTaskInfo);
        switch (windowingMode) {
        switch (runningTaskInfo.getWindowingMode()) {
            case WINDOWING_MODE_FULLSCREEN:
                return runningTaskInfo.letterboxActivityBounds != null
                        ? TASK_LISTENER_TYPE_LETTERBOX
+182 −0
Original line number 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 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.

+41 −18
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.wm.shell.draganddrop;

import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.ClipDescription.EXTRA_ACTIVITY_OPTIONS;
@@ -67,6 +68,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

/**
 * The policy for handling drag and drop operations to shell.
@@ -133,20 +135,20 @@ public class DragAndDropPolicy {
                // TODO(b/169894807): For now, only allow splitting to the right/bottom until we
                //                    have split pairs
                mTargets.add(new Target(TYPE_FULLSCREEN,
                        new Rect(0, 0, w, h / 2),
                        new Rect(l, t, l + iw, t + ih / 2),
                        new Rect(l, t, l + iw, t + ih),
                        new Rect(0, 0, w, h)));
                mTargets.add(new Target(TYPE_SPLIT_BOTTOM,
                        new Rect(0, h / 2, w, h),
                        new Rect(l, t + ih / 2, l + iw, t + ih),
                        new Rect(l, t + ih / 2, l + iw, t + ih),
                        new Rect(0, h / 2, w, h)));
            } else {
                mTargets.add(new Target(TYPE_FULLSCREEN,
                        new Rect(0, 0, w / 2, h),
                        new Rect(l, t, l + iw / 2, t + ih),
                        new Rect(l, t, l + iw, t + ih),
                        new Rect(0, 0, w, h)));
                mTargets.add(new Target(TYPE_SPLIT_RIGHT,
                        new Rect(w / 2, 0, w, h),
                        new Rect(l + iw / 2, t, l + iw, t + ih),
                        new Rect(l + iw / 2, t, l + iw, t + ih),
                        new Rect(w / 2, 0, w, h)));
            }
@@ -162,12 +164,12 @@ public class DragAndDropPolicy {
            secondarySplitBounds.intersect(new Rect(l, t, l + iw, t + ih));
            if (isVerticalSplit) {
                mTargets.add(new Target(TYPE_FULLSCREEN,
                        new Rect(0, 0, w, secondarySplitRawBounds.top),
                        new Rect(l, t, l + iw, secondarySplitRawBounds.top),
                        new Rect(l, t, l + iw, t + ih),
                        new Rect(0, 0, w, secondarySplitRawBounds.top)));
            } else {
                mTargets.add(new Target(TYPE_FULLSCREEN,
                        new Rect(0, 0, secondarySplitRawBounds.left, h),
                        new Rect(l, t, secondarySplitRawBounds.left, t + ih),
                        new Rect(l, t, l + iw, t + ih),
                        new Rect(0, 0, w, h)));
            }
@@ -178,7 +180,7 @@ public class DragAndDropPolicy {
        } else {
            // Otherwise only show the fullscreen target
            mTargets.add(new Target(TYPE_FULLSCREEN,
                    new Rect(0, 0, w, h),
                    new Rect(l, t, l + iw, t + ih),
                    new Rect(l, t, l + iw, t + ih),
                    new Rect(0, 0, w, h)));
        }
@@ -210,6 +212,7 @@ public class DragAndDropPolicy {
        final boolean isShortcut = description.hasMimeType(MIMETYPE_APPLICATION_SHORTCUT);
        final Intent dragData = mSession.dragData;

        boolean deferAppLaunchUntilSplit = false;
        if (target.type == TYPE_FULLSCREEN) {
            if (mSplitScreen != null && mSplitScreen.isDividerVisible()) {
                // If in split, remove split and launch fullscreen
@@ -226,9 +229,11 @@ public class DragAndDropPolicy {
                // Not in split, enter split now
                mStarter.enterSplitScreen(mSession.runningTaskId,
                        target.type == TYPE_SPLIT_LEFT || target.type == TYPE_SPLIT_TOP);
                deferAppLaunchUntilSplit = true;
            }
        }

        final Runnable startAppRunnable = () -> {
            Bundle opts = dragData.hasExtra(EXTRA_ACTIVITY_OPTIONS)
                    ? dragData.getBundleExtra(EXTRA_ACTIVITY_OPTIONS)
                    : null;
@@ -241,6 +246,24 @@ public class DragAndDropPolicy {
            } else {
                mStarter.startIntent(dragData.getParcelableExtra(EXTRA_PENDING_INTENT), opts);
            }
        };
        if (deferAppLaunchUntilSplit) {
            // TODO(b/169894807): The enterSplitScreen() call above will trigger the current task
            // into split, and we should wait for home and other tasks to be moved to
            // split-secondary before trying to launch the new secondary task.  This can be removed
            // once we have app-pairs.
            mSplitScreen.registerInSplitScreenListener(new Consumer<Boolean>() {
                @Override
                public void accept(Boolean inSplit) {
                    if (inSplit) {
                        startAppRunnable.run();
                        mSplitScreen.unregisterInSplitScreenListener(this);
                    }
                }
            });
        } else {
            startAppRunnable.run();
        }
    }

    /**
@@ -274,7 +297,7 @@ public class DragAndDropPolicy {
         * Updates the session data based on the current state of the system.
         */
        void update() {
            final ClipDescription description = mInitialDragData.getDescription();

            try {
                List<ActivityManager.RunningTaskInfo> tasks =
                        mIActivityTaskManager.getFilteredTasks(1,
Loading