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

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

Merge "Add task organizer based task embedder" into rvc-dev

parents 63ff5bb6 a1f869d1
Loading
Loading
Loading
Loading
+48 −8
Original line number Diff line number Diff line
@@ -69,6 +69,7 @@ public class ActivityView extends ViewGroup implements TaskEmbedder.Host {
    // For Host
    private final Point mWindowPosition = new Point();
    private final int[] mTmpArray = new int[2];
    private final Rect mTmpRect = new Rect();
    private final Matrix mScreenSurfaceMatrix = new Matrix();
    private final Region mTapExcludeRegion = new Region();

@@ -84,10 +85,14 @@ public class ActivityView extends ViewGroup implements TaskEmbedder.Host {
        this(context, attrs, defStyle, false /*singleTaskInstance*/);
    }

    public ActivityView(
            Context context, AttributeSet attrs, int defStyle, boolean singleTaskInstance) {
    public ActivityView(Context context, AttributeSet attrs, int defStyle,
            boolean singleTaskInstance) {
        super(context, attrs, defStyle);
        mTaskEmbedder = new TaskEmbedder(getContext(), this, singleTaskInstance);
        if (useTaskOrganizer()) {
            mTaskEmbedder = new TaskOrganizerTaskEmbedder(context, this);
        } else {
            mTaskEmbedder = new VirtualDisplayTaskEmbedder(context, this, singleTaskInstance);
        }
        mSurfaceView = new SurfaceView(context);
        // Since ActivityView#getAlpha has been overridden, we should use parent class's alpha
        // as master to synchronize surface view's alpha value.
@@ -128,6 +133,12 @@ public class ActivityView extends ViewGroup implements TaskEmbedder.Host {
         */
        public void onTaskCreated(int taskId, ComponentName componentName) { }

        /**
         * Called when a task visibility changes.
         * @hide
         */
        public void onTaskVisibilityChanged(int taskId, boolean visible) { }

        /**
         * Called when a task is moved to the front of the stack inside the container.
         * This is a filtered version of {@link TaskStackListener}
@@ -139,6 +150,12 @@ public class ActivityView extends ViewGroup implements TaskEmbedder.Host {
         * This is a filtered version of {@link TaskStackListener}
         */
        public void onTaskRemovalStarted(int taskId) { }

        /**
         * Called when back is pressed on the root activity of the task.
         * @hide
         */
        public void onBackPressedOnTaskRoot(int taskId) { }
    }

    /**
@@ -370,10 +387,8 @@ public class ActivityView extends ViewGroup implements TaskEmbedder.Host {

    @Override
    public boolean gatherTransparentRegion(Region region) {
        // The tap exclude region may be affected by any view on top of it, so we detect the
        // possible change by monitoring this function.
        mTaskEmbedder.notifyBoundsChanged();
        return super.gatherTransparentRegion(region);
        return mTaskEmbedder.gatherTransparentRegion(region)
                || super.gatherTransparentRegion(region);
    }

    private class SurfaceCallback implements SurfaceHolder.Callback {
@@ -432,7 +447,6 @@ public class ActivityView extends ViewGroup implements TaskEmbedder.Host {
            Log.e(TAG, "Failed to initialize ActivityView");
            return false;
        }
        mTmpTransaction.show(mTaskEmbedder.getSurfaceControl()).apply();
        return true;
    }

@@ -518,6 +532,13 @@ public class ActivityView extends ViewGroup implements TaskEmbedder.Host {
        return mWindowPosition;
    }

    /** @hide */
    @Override
    public Rect getScreenBounds() {
        getBoundsOnScreen(mTmpRect);
        return mTmpRect;
    }

    /** @hide */
    @Override
    public IWindow getWindow() {
@@ -530,6 +551,15 @@ public class ActivityView extends ViewGroup implements TaskEmbedder.Host {
        return super.canReceivePointerEvents();
    }

    /**
     * Overridden by instances that require the use of the task organizer implementation instead of
     * the virtual display implementation.  Not for general use.
     * @hide
     */
    protected boolean useTaskOrganizer() {
        return false;
    }

    private final class StateCallbackAdapter implements TaskEmbedder.Listener {
        private final StateCallback mCallback;

@@ -552,6 +582,11 @@ public class ActivityView extends ViewGroup implements TaskEmbedder.Host {
            mCallback.onTaskCreated(taskId, name);
        }

        @Override
        public void onTaskVisibilityChanged(int taskId, boolean visible) {
            mCallback.onTaskVisibilityChanged(taskId, visible);
        }

        @Override
        public void onTaskMovedToFront(int taskId) {
            mCallback.onTaskMovedToFront(taskId);
@@ -561,5 +596,10 @@ public class ActivityView extends ViewGroup implements TaskEmbedder.Host {
        public void onTaskRemovalStarted(int taskId) {
            mCallback.onTaskRemovalStarted(taskId);
        }

        @Override
        public void onBackPressedOnTaskRoot(int taskId) {
            mCallback.onBackPressedOnTaskRoot(taskId);
        }
    }
}
+122 −368

File changed.

Preview size limit exceeded, changes collapsed.

+325 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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.app;

import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;

import android.content.Context;
import android.graphics.Rect;
import android.os.RemoteException;
import android.util.Log;
import android.view.KeyEvent;
import android.view.SurfaceControl;
import android.window.ITaskOrganizer;
import android.window.IWindowContainer;
import android.window.WindowContainerTransaction;
import android.window.WindowOrganizer;
import android.window.WindowOrganizer.TaskOrganizer;

/**
 * A component which handles embedded display of tasks within another window. The embedded task can
 * be presented using the SurfaceControl provided from {@link #getSurfaceControl()}.
 *
 * @hide
 */
public class TaskOrganizerTaskEmbedder extends TaskEmbedder {
    private static final String TAG = "TaskOrgTaskEmbedder";
    private static final boolean DEBUG = false;

    private ITaskOrganizer.Stub mTaskOrganizer;
    private ActivityManager.RunningTaskInfo mTaskInfo;
    private IWindowContainer mTaskToken;
    private SurfaceControl mTaskLeash;
    private boolean mPendingNotifyBoundsChanged;

    /**
     * Constructs a new TaskEmbedder.
     *
     * @param context the context
     * @param host the host for this embedded task
     */
    public TaskOrganizerTaskEmbedder(Context context, TaskOrganizerTaskEmbedder.Host host) {
        super(context, host);
    }

    @Override
    public TaskStackListener createTaskStackListener() {
        return new TaskStackListenerImpl();
    }

    /**
     * Whether this container has been initialized.
     *
     * @return true if initialized
     */
    @Override
    public boolean isInitialized() {
        return mTaskOrganizer != null;
    }

    @Override
    public boolean onInitialize() {
        if (DEBUG) {
            log("onInitialize");
        }
        // Register the task organizer
        mTaskOrganizer =  new TaskOrganizerImpl();
        try {
            // TODO(wm-shell): This currently prevents other organizers from controlling MULT_WINDOW
            // windowing mode tasks. Plan is to migrate this to a wm-shell front-end when that
            // infrastructure is ready.
            TaskOrganizer.registerOrganizer(mTaskOrganizer, WINDOWING_MODE_MULTI_WINDOW);
            TaskOrganizer.setInterceptBackPressedOnTaskRoot(mTaskOrganizer, true);
        } catch (RemoteException e) {
            Log.e(TAG, "Failed to initialize TaskEmbedder", e);
            return false;
        }
        return true;
    }

    @Override
    protected boolean onRelease() {
        if (DEBUG) {
            log("onRelease");
        }
        if (!isInitialized()) {
            return false;
        }
        try {
            TaskOrganizer.unregisterOrganizer(mTaskOrganizer);
        } catch (RemoteException e) {
            Log.e(TAG, "Failed to remove task");
        }
        resetTaskInfo();
        return true;
    }

    /**
     * Starts presentation of tasks in this container.
     */
    @Override
    public void start() {
        if (DEBUG) {
            log("start");
        }
        if (!isInitialized()) {
            return;
        }
        if (mTaskToken == null) {
            return;
        }
        WindowContainerTransaction wct = new WindowContainerTransaction();
        wct.setHidden(mTaskToken, false /* hidden */);
        try {
            WindowOrganizer.applyTransaction(wct);
        } catch (RemoteException e) {
            Log.e(TAG, "Failed to unset hidden in transaction");
        }
        // TODO(b/151449487): Only call callback once we enable synchronization
        if (mListener != null) {
            mListener.onTaskVisibilityChanged(getTaskId(), true);
        }
    }

    /**
     * Stops presentation of tasks in this container.
     */
    @Override
    public void stop() {
        if (DEBUG) {
            log("stop");
        }
        if (!isInitialized()) {
            return;
        }
        if (mTaskToken == null) {
            return;
        }
        WindowContainerTransaction wct = new WindowContainerTransaction();
        wct.setHidden(mTaskToken, true /* hidden */);
        try {
            WindowOrganizer.applyTransaction(wct);
        } catch (RemoteException e) {
            Log.e(TAG, "Failed to set hidden in transaction");
        }
        // TODO(b/151449487): Only call callback once we enable synchronization
        if (mListener != null) {
            mListener.onTaskVisibilityChanged(getTaskId(), false);
        }
    }

    /**
     * This should be called whenever the position or size of the surface changes
     * or if touchable areas above the surface are added or removed.
     */
    @Override
    public void notifyBoundsChanged() {
        if (DEBUG) {
            log("notifyBoundsChanged: screenBounds=" + mHost.getScreenBounds());
        }
        if (mTaskToken == null) {
            mPendingNotifyBoundsChanged = true;
            return;
        }
        mPendingNotifyBoundsChanged = false;

        // Update based on the screen bounds
        Rect screenBounds = mHost.getScreenBounds();
        if (screenBounds.left < 0 || screenBounds.top < 0) {
            screenBounds.offsetTo(0, 0);
        }

        WindowContainerTransaction wct = new WindowContainerTransaction();
        wct.setBounds(mTaskToken, screenBounds);
        try {
            // TODO(b/151449487): Enable synchronization
            WindowOrganizer.applyTransaction(wct);
        } catch (RemoteException e) {
            Log.e(TAG, "Failed to set bounds in transaction");
        }
    }

    /**
     * Injects a pair of down/up key events with keycode {@link KeyEvent#KEYCODE_BACK} to the
     * virtual display.
     */
    @Override
    public void performBackPress() {
        // Do nothing, the task org task should already have focus if the caller is not focused
        return;
    }

    /** An opaque unique identifier for this task surface among others being managed by the app. */
    @Override
    public int getId() {
        return getTaskId();
    }

    /**
     * Check if container is ready to launch and create {@link ActivityOptions} to target the
     * virtual display.
     * @param options The existing options to amend, or null if the caller wants new options to be
     *                created
     */
    @Override
    protected ActivityOptions prepareActivityOptions(ActivityOptions options) {
        options = super.prepareActivityOptions(options);
        options.setLaunchWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
        return options;
    }

    private int getTaskId() {
        return mTaskInfo != null
                ? mTaskInfo.taskId
                : INVALID_TASK_ID;
    }

    private void resetTaskInfo() {
        if (DEBUG) {
            log("resetTaskInfo");
        }
        mTaskInfo = null;
        mTaskToken = null;
        mTaskLeash = null;
    }

    private void log(String msg) {
        Log.d(TAG, "[" + System.identityHashCode(this) + "] " + msg);
    }

    /**
     * A task change listener that detects background color change of the topmost stack on our
     * virtual display and updates the background of the surface view. This background will be shown
     * when surface view is resized, but the app hasn't drawn its content in new size yet.
     * It also calls StateCallback.onTaskMovedToFront to notify interested parties that the stack
     * associated with the {@link ActivityView} has had a Task moved to the front. This is useful
     * when needing to also bring the host Activity to the foreground at the same time.
     */
    private class TaskStackListenerImpl extends TaskStackListener {

        @Override
        public void onTaskDescriptionChanged(ActivityManager.RunningTaskInfo taskInfo)
                throws RemoteException {
            if (!isInitialized()) {
                return;
            }
            if (taskInfo.taskId == mTaskInfo.taskId) {
                mTaskInfo.taskDescription = taskInfo.taskDescription;
                mHost.onTaskBackgroundColorChanged(TaskOrganizerTaskEmbedder.this,
                        taskInfo.taskDescription.getBackgroundColor());
            }
        }
    }

    private class TaskOrganizerImpl extends ITaskOrganizer.Stub {
        @Override
        public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo)
                throws RemoteException {
            if (DEBUG) {
                log("taskAppeared: " + taskInfo.taskId);
            }

            // TODO: Ensure visibility/alpha of the leash in its initial state?
            mTaskInfo = taskInfo;
            mTaskToken = taskInfo.token;
            mTaskLeash = mTaskToken.getLeash();
            mTransaction.reparent(mTaskLeash, mSurfaceControl)
                    .show(mSurfaceControl).apply();
            if (mPendingNotifyBoundsChanged) {
                // TODO: Either defer show or hide and synchronize show with the resize
                notifyBoundsChanged();
            }
            mHost.post(() -> mHost.onTaskBackgroundColorChanged(TaskOrganizerTaskEmbedder.this,
                    taskInfo.taskDescription.getBackgroundColor()));

            if (mListener != null) {
                mListener.onTaskCreated(taskInfo.taskId, taskInfo.baseActivity);
            }
        }

        @Override
        public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo)
                throws RemoteException {
            if (DEBUG) {
                log("taskVanished: " + taskInfo.taskId);
            }

            if (mTaskToken != null && (taskInfo == null
                    || mTaskToken.asBinder().equals(taskInfo.token.asBinder()))) {
                if (mListener != null) {
                    mListener.onTaskRemovalStarted(taskInfo.taskId);
                }
                resetTaskInfo();
            }
        }

        @Override
        public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo)
                throws RemoteException {
            // Do nothing
        }

        @Override
        public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo taskInfo)
                throws RemoteException {
            if (mListener != null) {
                mListener.onBackPressedOnTaskRoot(taskInfo.taskId);
            }
        }
    }
}
+451 −0

File added.

Preview size limit exceeded, changes collapsed.

+8 −1
Original line number Diff line number Diff line
@@ -41,5 +41,12 @@ oneway interface ITaskOrganizer {
     * has children. The Divider impl looks at the info and can see that the secondary root task
     * has adopted an ActivityType of HOME and proceeds to show the minimized dock UX.
     */
    void onTaskInfoChanged(in ActivityManager.RunningTaskInfo info);
    void onTaskInfoChanged(in ActivityManager.RunningTaskInfo taskInfo);

    /**
     * Called when the task organizer has requested
     * {@link ITaskOrganizerController.setInterceptBackPressedOnTaskRoot} to get notified when the
     * user has pressed back on the root activity of a task controlled by the task organizer.
     */
    void onBackPressedOnTaskRoot(in ActivityManager.RunningTaskInfo taskInfo);
}
Loading