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

Commit e2febf71 authored by Tony Huang's avatar Tony Huang Committed by Automerger Merge Worker
Browse files

Merge "Remove stagesplit backup package" into tm-qpr-dev am: 1e2ddcc2 am: 1f69d522

parents b52765f5 1f69d522
Loading
Loading
Loading
Loading
+0 −103
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 com.android.wm.shell.stagesplit;

import android.app.PendingIntent;
import android.content.Intent;
import android.os.Bundle;
import android.os.UserHandle;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget;
import android.window.RemoteTransition;

import com.android.wm.shell.stagesplit.ISplitScreenListener;

/**
 * Interface that is exposed to remote callers to manipulate the splitscreen feature.
 */
interface ISplitScreen {

    /**
     * Registers a split screen listener.
     */
    oneway void registerSplitScreenListener(in ISplitScreenListener listener) = 1;

    /**
     * Unregisters a split screen listener.
     */
    oneway void unregisterSplitScreenListener(in ISplitScreenListener listener) = 2;

    /**
     * Hides the side-stage if it is currently visible.
     */
    oneway void setSideStageVisibility(boolean visible) = 3;

    /**
     * Removes a task from the side stage.
     */
    oneway void removeFromSideStage(int taskId) = 4;

    /**
     * Removes the split-screen stages and leaving indicated task to top. Passing INVALID_TASK_ID
     * to indicate leaving no top task after leaving split-screen.
     */
    oneway void exitSplitScreen(int toTopTaskId) = 5;

    /**
     * @param exitSplitScreenOnHide if to exit split-screen if both stages are not visible.
     */
    oneway void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) = 6;

    /**
     * Starts a task in a stage.
     */
    oneway void startTask(int taskId, int stage, int position, in Bundle options) = 7;

    /**
     * Starts a shortcut in a stage.
     */
    oneway void startShortcut(String packageName, String shortcutId, int stage, int position,
            in Bundle options, in UserHandle user) = 8;

    /**
     * Starts an activity in a stage.
     */
    oneway void startIntent(in PendingIntent intent, in Intent fillInIntent, int stage,
            int position, in Bundle options) = 9;

    /**
     * Starts tasks simultaneously in one transition.
     */
    oneway void startTasks(int mainTaskId, in Bundle mainOptions, int sideTaskId,
            in Bundle sideOptions, int sidePosition, in RemoteTransition remoteTransition) = 10;

    /**
     * Version of startTasks using legacy transition system.
     */
     oneway void startTasksWithLegacyTransition(int mainTaskId, in Bundle mainOptions,
                            int sideTaskId, in Bundle sideOptions, int sidePosition,
                            in RemoteAnimationAdapter adapter) = 11;

    /**
     * Blocking call that notifies and gets additional split-screen targets when entering
     * recents (for example: the dividerBar).
     * @param cancel is true if leaving recents back to split (eg. the gesture was cancelled).
     * @param appTargets apps that will be re-parented to display area
     */
    RemoteAnimationTarget[] onGoingToRecentsLegacy(boolean cancel,
                                                   in RemoteAnimationTarget[] appTargets) = 12;
}
+0 −33
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 com.android.wm.shell.stagesplit;

/**
 * Listener interface that Launcher attaches to SystemUI to get split-screen callbacks.
 */
oneway interface ISplitScreenListener {

    /**
     * Called when the stage position changes.
     */
    void onStagePositionChanged(int stage, int position);

    /**
     * Called when a task changes stages.
     */
    void onTaskStageChanged(int taskId, int stage, boolean visible);
}
 No newline at end of file
+0 −104
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.stagesplit;

import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;

import android.annotation.Nullable;
import android.graphics.Rect;
import android.view.SurfaceSession;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;

import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.SyncTransactionQueue;

/**
 * Main stage for split-screen mode. When split-screen is active all standard activity types launch
 * on the main stage, except for task that are explicitly pinned to the {@link SideStage}.
 * @see StageCoordinator
 */
class MainStage extends StageTaskListener {
    private static final String TAG = MainStage.class.getSimpleName();

    private boolean mIsActive = false;

    MainStage(ShellTaskOrganizer taskOrganizer, int displayId,
            StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
            SurfaceSession surfaceSession,
            @Nullable StageTaskUnfoldController stageTaskUnfoldController) {
        super(taskOrganizer, displayId, callbacks, syncQueue, surfaceSession,
                stageTaskUnfoldController);
    }

    boolean isActive() {
        return mIsActive;
    }

    void activate(Rect rootBounds, WindowContainerTransaction wct) {
        if (mIsActive) return;

        final WindowContainerToken rootToken = mRootTaskInfo.token;
        wct.setBounds(rootToken, rootBounds)
                .setWindowingMode(rootToken, WINDOWING_MODE_MULTI_WINDOW)
                .setLaunchRoot(
                        rootToken,
                        CONTROLLED_WINDOWING_MODES,
                        CONTROLLED_ACTIVITY_TYPES)
                .reparentTasks(
                        null /* currentParent */,
                        rootToken,
                        CONTROLLED_WINDOWING_MODES,
                        CONTROLLED_ACTIVITY_TYPES,
                        true /* onTop */)
                // Moving the root task to top after the child tasks were re-parented , or the root
                // task cannot be visible and focused.
                .reorder(rootToken, true /* onTop */);

        mIsActive = true;
    }

    void deactivate(WindowContainerTransaction wct) {
        deactivate(wct, false /* toTop */);
    }

    void deactivate(WindowContainerTransaction wct, boolean toTop) {
        if (!mIsActive) return;
        mIsActive = false;

        if (mRootTaskInfo == null) return;
        final WindowContainerToken rootToken = mRootTaskInfo.token;
        wct.setLaunchRoot(
                        rootToken,
                        null,
                        null)
                .reparentTasks(
                        rootToken,
                        null /* newParent */,
                        CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE,
                        CONTROLLED_ACTIVITY_TYPES,
                        toTop)
                // We want this re-order to the bottom regardless since we are re-parenting
                // all its tasks.
                .reorder(rootToken, false /* onTop */);
    }

    void updateConfiguration(int windowingMode, Rect bounds, WindowContainerTransaction wct) {
        wct.setBounds(mRootTaskInfo.token, bounds)
                .setWindowingMode(mRootTaskInfo.token, windowingMode);
    }
}
+0 −2
Original line number Diff line number Diff line
# WM shell sub-modules stagesplit owner
chenghsiuchang@google.com
+0 −181
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 com.android.wm.shell.stagesplit;

import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;

import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.Binder;
import android.view.IWindow;
import android.view.InsetsSource;
import android.view.InsetsState;
import android.view.LayoutInflater;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.WindowlessWindowManager;
import android.widget.FrameLayout;

import com.android.wm.shell.R;

/**
 * Handles drawing outline of the bounds of provided root surface. The outline will be drown with
 * the consideration of display insets like status bar, navigation bar and display cutout.
 */
class OutlineManager extends WindowlessWindowManager {
    private static final String WINDOW_NAME = "SplitOutlineLayer";
    private final Context mContext;
    private final Rect mRootBounds = new Rect();
    private final Rect mTempRect = new Rect();
    private final Rect mLastOutlineBounds = new Rect();
    private final InsetsState mInsetsState = new InsetsState();
    private final int mExpandedTaskBarHeight;
    private OutlineView mOutlineView;
    private SurfaceControlViewHost mViewHost;
    private SurfaceControl mHostLeash;
    private SurfaceControl mLeash;

    OutlineManager(Context context, Configuration configuration) {
        super(configuration, null /* rootSurface */, null /* hostInputToken */);
        mContext = context.createWindowContext(context.getDisplay(), TYPE_APPLICATION_OVERLAY,
                null /* options */);
        mExpandedTaskBarHeight = mContext.getResources().getDimensionPixelSize(
                com.android.internal.R.dimen.taskbar_frame_height);
    }

    @Override
    protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) {
        b.setParent(mHostLeash);
    }

    void inflate(SurfaceControl rootLeash, Rect rootBounds) {
        if (mLeash != null || mViewHost != null) return;

        mHostLeash = rootLeash;
        mRootBounds.set(rootBounds);
        mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this);

        final FrameLayout rootLayout = (FrameLayout) LayoutInflater.from(mContext)
                .inflate(R.layout.split_outline, null);
        mOutlineView = rootLayout.findViewById(R.id.split_outline);

        final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
                0 /* width */, 0 /* height */, TYPE_APPLICATION_OVERLAY,
                FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCHABLE, PixelFormat.TRANSLUCENT);
        lp.width = mRootBounds.width();
        lp.height = mRootBounds.height();
        lp.token = new Binder();
        lp.setTitle(WINDOW_NAME);
        lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
        // TODO(b/189839391): Set INPUT_FEATURE_NO_INPUT_CHANNEL after WM supports
        //  TRUSTED_OVERLAY for windowless window without input channel.
        mViewHost.setView(rootLayout, lp);
        mLeash = getSurfaceControl(mViewHost.getWindowToken());

        drawOutline();
    }

    void release() {
        if (mViewHost != null) {
            mViewHost.release();
            mViewHost = null;
        }
        mRootBounds.setEmpty();
        mLastOutlineBounds.setEmpty();
        mOutlineView = null;
        mHostLeash = null;
        mLeash = null;
    }

    @Nullable
    SurfaceControl getOutlineLeash() {
        return mLeash;
    }

    void setVisibility(boolean visible) {
        if (mOutlineView != null) {
            mOutlineView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
        }
    }

    void setRootBounds(Rect rootBounds) {
        if (mViewHost == null || mViewHost.getView() == null) {
            return;
        }

        if (!mRootBounds.equals(rootBounds)) {
            WindowManager.LayoutParams lp =
                    (WindowManager.LayoutParams) mViewHost.getView().getLayoutParams();
            lp.width = rootBounds.width();
            lp.height = rootBounds.height();
            mViewHost.relayout(lp);
            mRootBounds.set(rootBounds);
            drawOutline();
        }
    }

    void onInsetsChanged(InsetsState insetsState) {
        if (!mInsetsState.equals(insetsState)) {
            mInsetsState.set(insetsState);
            drawOutline();
        }
    }

    private void computeOutlineBounds(Rect rootBounds, InsetsState insetsState, Rect outBounds) {
        outBounds.set(rootBounds);
        final InsetsSource taskBarInsetsSource =
                insetsState.getSource(InsetsState.ITYPE_EXTRA_NAVIGATION_BAR);
        // Only insets the divider bar with task bar when it's expanded so that the rounded corners
        // will be drawn against task bar.
        if (taskBarInsetsSource.getFrame().height() >= mExpandedTaskBarHeight) {
            outBounds.inset(taskBarInsetsSource.calculateVisibleInsets(outBounds));
        }

        // Offset the coordinate from screen based to surface based.
        outBounds.offset(-rootBounds.left, -rootBounds.top);
    }

    void drawOutline() {
        if (mOutlineView == null) {
            return;
        }

        computeOutlineBounds(mRootBounds, mInsetsState, mTempRect);
        if (mTempRect.equals(mLastOutlineBounds)) {
            return;
        }

        ViewGroup.MarginLayoutParams lp =
                (ViewGroup.MarginLayoutParams) mOutlineView.getLayoutParams();
        lp.leftMargin = mTempRect.left;
        lp.topMargin = mTempRect.top;
        lp.width = mTempRect.width();
        lp.height = mTempRect.height();
        mOutlineView.setLayoutParams(lp);
        mLastOutlineBounds.set(mTempRect);
    }
}
Loading