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

Commit 63e732e8 authored by Jerry Chang's avatar Jerry Chang Committed by Android (Google) Code Review
Browse files

Merge changes from topic "staged_split_ime" into sc-dev

* changes:
  Adjust split layout with IME animation in split (5/N)
  Adjust split layout with IME animation in split (4/N)
  Adjust split layout with IME animation in split (3/N)
  Adjust split layout with IME animation in split (2/N)
  Adjust split layout with IME animation in split (1/N)
parents bc563d80 aebdc015
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -16,14 +16,14 @@

package com.android.wm.shell;

import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT;

import com.android.wm.shell.apppairs.AppPairsController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutoutController;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.splitscreen.SplitScreenController;

import java.io.PrintWriter;
@@ -145,7 +145,7 @@ public final class ShellCommandHandlerImpl {
        }
        final int taskId = new Integer(args[2]);
        final int sideStagePosition = args.length > 3
                ? new Integer(args[3]) : STAGE_POSITION_BOTTOM_OR_RIGHT;
                ? new Integer(args[3]) : SPLIT_POSITION_BOTTOM_OR_RIGHT;
        mSplitScreenOptional.ifPresent(split -> split.moveToSideStage(taskId, sideStagePosition));
        return true;
    }
+41 −33
Original line number Diff line number Diff line
@@ -20,11 +20,15 @@ import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;

import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_UNDEFINED;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG;

import android.app.ActivityManager;
import android.graphics.Rect;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;

@@ -35,6 +39,7 @@ import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.SurfaceUtils;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.split.SplitLayout;

@@ -45,7 +50,7 @@ import java.io.PrintWriter;
 * {@link #mTaskInfo1} and {@link #mTaskInfo2} in the pair.
 * Also includes all UI for managing the pair like the divider.
 */
class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.LayoutChangeListener {
class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.SplitLayoutHandler {
    private static final String TAG = AppPair.class.getSimpleName();

    private ActivityManager.RunningTaskInfo mRootTaskInfo;
@@ -54,6 +59,9 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.LayoutChan
    private SurfaceControl mTaskLeash1;
    private ActivityManager.RunningTaskInfo mTaskInfo2;
    private SurfaceControl mTaskLeash2;
    private SurfaceControl mDimLayer1;
    private SurfaceControl mDimLayer2;
    private final SurfaceSession mSurfaceSession = new SurfaceSession();

    private final AppPairsController mController;
    private final SyncTransactionQueue mSyncQueue;
@@ -101,7 +109,8 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.LayoutChan
        mSplitLayout = new SplitLayout(TAG + "SplitDivider",
                mDisplayController.getDisplayContext(mRootTaskInfo.displayId),
                mRootTaskInfo.configuration, this /* layoutChangeListener */,
                b -> b.setParent(mRootTaskLeash), mDisplayImeController);
                b -> b.setParent(mRootTaskLeash), mDisplayImeController,
                mController.getTaskOrganizer());

        final WindowContainerToken token1 = task1.token;
        final WindowContainerToken token2 = task2.token;
@@ -153,9 +162,13 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.LayoutChan
        } else if (taskInfo.taskId == getTaskId1()) {
            mTaskInfo1 = taskInfo;
            mTaskLeash1 = leash;
            mSyncQueue.runInSync(t -> mDimLayer1 =
                    SurfaceUtils.makeDimLayer(t, mTaskLeash1, "Dim layer", mSurfaceSession));
        } else if (taskInfo.taskId == getTaskId2()) {
            mTaskInfo2 = taskInfo;
            mTaskLeash2 = leash;
            mSyncQueue.runInSync(t -> mDimLayer2 =
                    SurfaceUtils.makeDimLayer(t, mTaskLeash2, "Dim layer", mSurfaceSession));
        } else {
            throw new IllegalStateException("Unknown task=" + taskInfo.taskId);
        }
@@ -212,13 +225,32 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.LayoutChan
        }
    }

    @Override
    public int getSplitItemPosition(WindowContainerToken token) {
        if (token == null) {
            return SPLIT_POSITION_UNDEFINED;
        }

        if (token.equals(mTaskInfo1.getToken())) {
            return SPLIT_POSITION_TOP_OR_LEFT;
        } else if (token.equals(mTaskInfo2.getToken())) {
            return SPLIT_POSITION_BOTTOM_OR_RIGHT;
        }

        return SPLIT_POSITION_UNDEFINED;
    }

    @Override
    public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
        if (taskInfo.taskId == getRootTaskId()) {
            // We don't want to release this object back to the pool since the root task went away.
            mController.unpair(mRootTaskInfo.taskId, false /* releaseToPool */);
        } else if (taskInfo.taskId == getTaskId1() || taskInfo.taskId == getTaskId2()) {
        } else if (taskInfo.taskId == getTaskId1()) {
            mController.unpair(mRootTaskInfo.taskId);
            mSyncQueue.runInSync(t -> t.remove(mDimLayer1));
        } else if (taskInfo.taskId == getTaskId2()) {
            mController.unpair(mRootTaskInfo.taskId);
            mSyncQueue.runInSync(t -> t.remove(mDimLayer2));
        }
    }

@@ -264,40 +296,16 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.LayoutChan

    @Override
    public void onBoundsChanging(SplitLayout layout) {
        final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash();
        if (dividerLeash == null) return;
        final Rect dividerBounds = layout.getDividerBounds();
        final Rect bounds1 = layout.getBounds1();
        final Rect bounds2 = layout.getBounds2();
        mSyncQueue.runInSync(t -> t
                .setPosition(dividerLeash, dividerBounds.left, dividerBounds.top)
                .setPosition(mTaskLeash1, bounds1.left, bounds1.top)
                .setPosition(mTaskLeash2, bounds2.left, bounds2.top)
                // Sets crop to prevent visible region of tasks overlap with each other when
                // re-positioning surfaces while resizing.
                .setWindowCrop(mTaskLeash1, bounds1.width(), bounds1.height())
                .setWindowCrop(mTaskLeash2, bounds2.width(), bounds2.height()));
        mSyncQueue.runInSync(t ->
                layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2));
    }

    @Override
    public void onBoundsChanged(SplitLayout layout) {
        final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash();
        if (dividerLeash == null) return;
        final Rect dividerBounds = layout.getDividerBounds();
        final Rect bounds1 = layout.getBounds1();
        final Rect bounds2 = layout.getBounds2();
        final WindowContainerTransaction wct = new WindowContainerTransaction();
        wct.setBounds(mTaskInfo1.token, bounds1)
                .setBounds(mTaskInfo2.token, bounds2);
        mController.getTaskOrganizer().applyTransaction(wct);
        mSyncQueue.runInSync(t -> t
                // Resets layer of divider bar to make sure it is always on top.
                .setLayer(dividerLeash, Integer.MAX_VALUE)
                .setPosition(dividerLeash, dividerBounds.left, dividerBounds.top)
                .setPosition(mTaskLeash1, bounds1.left, bounds1.top)
                .setPosition(mTaskLeash2, bounds2.left, bounds2.top)
                // Resets crop to apply new surface bounds directly.
                .setWindowCrop(mTaskLeash1, null)
                .setWindowCrop(mTaskLeash2, null));
        layout.applyTaskChanges(wct, mTaskInfo1, mTaskInfo2);
        mSyncQueue.queue(wct);
        mSyncQueue.runInSync(t ->
                layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2));
    }
}
+38 −0
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.common;

import android.view.SurfaceControl;
import android.view.SurfaceSession;

/**
 * Helpers for handling surface.
 */
public class SurfaceUtils {
    /** Creates a dim layer above indicated host surface. */
    public static SurfaceControl makeDimLayer(SurfaceControl.Transaction t, SurfaceControl host,
            String name, SurfaceSession surfaceSession) {
        SurfaceControl dimLayer = new SurfaceControl.Builder(surfaceSession)
                .setParent(host)
                .setColorLayer()
                .setName(name)
                .setCallsite("SurfaceUtils.makeDimLayer")
                .build();
        t.setLayer(dimLayer, Integer.MAX_VALUE).setColor(dimLayer, new float[]{0f, 0f, 0f});
        return dimLayer;
    }
}
+2 −10
Original line number Diff line number Diff line
@@ -36,13 +36,11 @@ import androidx.annotation.Nullable;
import com.android.internal.policy.DividerSnapAlgorithm;
import com.android.wm.shell.R;
import com.android.wm.shell.animation.Interpolators;
import com.android.wm.shell.common.DisplayImeController;

/**
 * Divider for multi window splits.
 */
public class DividerView extends FrameLayout implements View.OnTouchListener,
        DisplayImeController.ImePositionProcessor {
public class DividerView extends FrameLayout implements View.OnTouchListener {
    public static final long TOUCH_ANIMATION_DURATION = 150;
    public static final long TOUCH_RELEASE_ANIMATION_DURATION = 200;

@@ -98,12 +96,6 @@ public class DividerView extends FrameLayout implements View.OnTouchListener,
        setOnTouchListener(this);
    }

    @Override
    public void onImeVisibilityChanged(int displayId, boolean isShowing) {
        if (displayId != getDisplay().getDisplayId()) return;
        setInteractive(!isShowing);
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        if (mSplitLayout == null || !mInteractive) {
@@ -217,7 +209,7 @@ public class DividerView extends FrameLayout implements View.OnTouchListener,
        mViewHost.relayout(lp);
    }

    private void setInteractive(boolean interactive) {
    void setInteractive(boolean interactive) {
        if (interactive == mInteractive) return;
        mInteractive = interactive;
        releaseTouching();
+217 −19
Original line number Diff line number Diff line
@@ -25,16 +25,22 @@ import static com.android.internal.policy.DividerSnapAlgorithm.SnapTarget.FLAG_D
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
import android.app.ActivityManager;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Rect;
import android.view.SurfaceControl;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;

import androidx.annotation.Nullable;

import com.android.internal.policy.DividerSnapAlgorithm;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.animation.Interpolators;
import com.android.wm.shell.common.DisplayImeController;

@@ -42,7 +48,32 @@ import com.android.wm.shell.common.DisplayImeController;
 * Records and handles layout of splits. Helps to calculate proper bounds when configuration or
 * divide position changes.
 */
public class SplitLayout {
public final class SplitLayout {
    /**
     * Split position isn't specified normally meaning to use what ever it is currently set to.
     */
    public static final int SPLIT_POSITION_UNDEFINED = -1;

    /**
     * Specifies that a split is positioned at the top half of the screen if
     * in portrait mode or at the left half of the screen if in landscape mode.
     */
    public static final int SPLIT_POSITION_TOP_OR_LEFT = 0;

    /**
     * Specifies that a split is positioned at the bottom half of the screen if
     * in portrait mode or at the right half of the screen if in landscape mode.
     */
    public static final int SPLIT_POSITION_BOTTOM_OR_RIGHT = 1;

    @IntDef(prefix = {"SPLIT_POSITION_"}, value = {
            SPLIT_POSITION_UNDEFINED,
            SPLIT_POSITION_TOP_OR_LEFT,
            SPLIT_POSITION_BOTTOM_OR_RIGHT
    })
    public @interface SplitPosition {
    }

    private final int mDividerWindowWidth;
    private final int mDividerInsets;
    private final int mDividerSize;
@@ -51,8 +82,11 @@ public class SplitLayout {
    private final Rect mDividerBounds = new Rect();
    private final Rect mBounds1 = new Rect();
    private final Rect mBounds2 = new Rect();
    private final LayoutChangeListener mLayoutChangeListener;
    private final SplitLayoutHandler mSplitLayoutHandler;
    private final SplitWindowManager mSplitWindowManager;
    private final DisplayImeController mDisplayImeController;
    private final ImePositionProcessor mImePositionProcessor;
    private final ShellTaskOrganizer mTaskOrganizer;

    private Context mContext;
    private DividerSnapAlgorithm mDividerSnapAlgorithm;
@@ -60,18 +94,21 @@ public class SplitLayout {
    private boolean mInitialized = false;

    public SplitLayout(String windowName, Context context, Configuration configuration,
            LayoutChangeListener layoutChangeListener,
            SplitLayoutHandler splitLayoutHandler,
            SplitWindowManager.ParentContainerCallbacks parentContainerCallbacks,
            DisplayImeController displayImeController) {
            DisplayImeController displayImeController, ShellTaskOrganizer taskOrganizer) {
        mContext = context.createConfigurationContext(configuration);
        mLayoutChangeListener = layoutChangeListener;
        mSplitLayoutHandler = splitLayoutHandler;
        mDisplayImeController = displayImeController;
        mSplitWindowManager = new SplitWindowManager(
                windowName, mContext, configuration, parentContainerCallbacks,
                displayImeController);
                windowName, mContext, configuration, parentContainerCallbacks);
        mTaskOrganizer = taskOrganizer;
        mImePositionProcessor = new ImePositionProcessor(mContext.getDisplayId());

        mDividerWindowWidth = context.getResources().getDimensionPixelSize(
        final Resources resources = context.getResources();
        mDividerWindowWidth = resources.getDimensionPixelSize(
                com.android.internal.R.dimen.docked_stack_divider_thickness);
        mDividerInsets = context.getResources().getDimensionPixelSize(
        mDividerInsets = resources.getDimensionPixelSize(
                com.android.internal.R.dimen.docked_stack_divider_insets);
        mDividerSize = mDividerWindowWidth - mDividerInsets * 2;

@@ -82,17 +119,17 @@ public class SplitLayout {

    /** Gets bounds of the primary split. */
    public Rect getBounds1() {
        return mBounds1;
        return new Rect(mBounds1);
    }

    /** Gets bounds of the secondary split. */
    public Rect getBounds2() {
        return mBounds2;
        return new Rect(mBounds2);
    }

    /** Gets bounds of divider window. */
    public Rect getDividerBounds() {
        return mDividerBounds;
        return new Rect(mDividerBounds);
    }

    /** Returns leash of the current divider bar. */
@@ -153,6 +190,7 @@ public class SplitLayout {
        if (mInitialized) return;
        mInitialized = true;
        mSplitWindowManager.init(this);
        mDisplayImeController.addPositionProcessor(mImePositionProcessor);
    }

    /** Releases the surface holding the current {@link DividerView}. */
@@ -160,6 +198,8 @@ public class SplitLayout {
        if (!mInitialized) return;
        mInitialized = false;
        mSplitWindowManager.release();
        mDisplayImeController.removePositionProcessor(mImePositionProcessor);
        mImePositionProcessor.reset();
    }

    /**
@@ -168,14 +208,14 @@ public class SplitLayout {
     */
    void updateDivideBounds(int position) {
        updateBounds(position);
        mLayoutChangeListener.onBoundsChanging(this);
        mSplitWindowManager.setResizingSplits(true);
        mSplitLayoutHandler.onBoundsChanging(this);
    }

    void setDividePosition(int position) {
        mDividePosition = position;
        updateBounds(mDividePosition);
        mLayoutChangeListener.onBoundsChanged(this);
        mSplitLayoutHandler.onBoundsChanged(this);
        mSplitWindowManager.setResizingSplits(false);
    }

@@ -192,11 +232,11 @@ public class SplitLayout {
    public void snapToTarget(int currentPosition, DividerSnapAlgorithm.SnapTarget snapTarget) {
        switch (snapTarget.flag) {
            case FLAG_DISMISS_START:
                mLayoutChangeListener.onSnappedToDismiss(false /* bottomOrRight */);
                mSplitLayoutHandler.onSnappedToDismiss(false /* bottomOrRight */);
                mSplitWindowManager.setResizingSplits(false);
                break;
            case FLAG_DISMISS_END:
                mLayoutChangeListener.onSnappedToDismiss(true /* bottomOrRight */);
                mSplitLayoutHandler.onSnappedToDismiss(true /* bottomOrRight */);
                mSplitWindowManager.setResizingSplits(false);
                break;
            default:
@@ -206,7 +246,7 @@ public class SplitLayout {
    }

    void onDoubleTappedDivider() {
        mLayoutChangeListener.onDoubleTappedDivider();
        mSplitLayoutHandler.onDoubleTappedDivider();
    }

    /**
@@ -265,8 +305,38 @@ public class SplitLayout {
        return bounds.width() > bounds.height();
    }

    /** Listens layout change event. */
    public interface LayoutChangeListener {
    /** Apply recorded surface layout to the {@link SurfaceControl.Transaction}. */
    public void applySurfaceChanges(SurfaceControl.Transaction t, SurfaceControl leash1,
            SurfaceControl leash2, SurfaceControl dimLayer1, SurfaceControl dimLayer2) {
        final Rect dividerBounds = mImePositionProcessor.adjustForIme(mDividerBounds);
        final Rect bounds1 = mImePositionProcessor.adjustForIme(mBounds1);
        final Rect bounds2 = mImePositionProcessor.adjustForIme(mBounds2);
        final SurfaceControl dividerLeash = getDividerLeash();
        if (dividerLeash != null) {
            t.setPosition(dividerLeash, dividerBounds.left, dividerBounds.top)
                    // Resets layer of divider bar to make sure it is always on top.
                    .setLayer(dividerLeash, Integer.MAX_VALUE);
        }

        t.setPosition(leash1, bounds1.left, bounds1.top)
                .setWindowCrop(leash1, bounds1.width(), bounds1.height());

        t.setPosition(leash2, bounds2.left, bounds2.top)
                .setWindowCrop(leash2, bounds2.width(), bounds2.height());

        mImePositionProcessor.applySurfaceDimValues(t, dimLayer1, dimLayer2);
    }

    /** Apply recorded task layout to the {@link WindowContainerTransaction}. */
    public void applyTaskChanges(WindowContainerTransaction wct,
            ActivityManager.RunningTaskInfo task1, ActivityManager.RunningTaskInfo task2) {
        wct.setBounds(task1.token, mImePositionProcessor.adjustForIme(mBounds1))
                .setBounds(task2.token, mImePositionProcessor.adjustForIme(mBounds2));
    }

    /** Handles layout change event. */
    public interface SplitLayoutHandler {

        /** Calls when dismissing split. */
        void onSnappedToDismiss(boolean snappedToEnd);

@@ -279,5 +349,133 @@ public class SplitLayout {
        /** Calls when user double tapped on the divider bar. */
        default void onDoubleTappedDivider() {
        }

        /** Returns split position of the token. */
        @SplitPosition
        int getSplitItemPosition(WindowContainerToken token);
    }

    /** Records IME top offset changes and updates SplitLayout correspondingly. */
    private class ImePositionProcessor implements DisplayImeController.ImePositionProcessor {
        /**
         * Maximum size of an adjusted split bounds relative to original stack bounds. Used to
         * restrict IME adjustment so that a min portion of top split remains visible.
         */
        private static final float ADJUSTED_SPLIT_FRACTION_MAX = 0.7f;
        private static final float ADJUSTED_NONFOCUS_DIM = 0.3f;

        private final int mDisplayId;

        private int mYOffsetForIme;
        private float mDimValue1;
        private float mDimValue2;

        private int mStartImeTop;
        private int mEndImeTop;

        private int mTargetYOffset;
        private int mLastYOffset;
        private float mTargetDim1;
        private float mTargetDim2;
        private float mLastDim1;
        private float mLastDim2;

        private ImePositionProcessor(int displayId) {
            mDisplayId = displayId;
        }

        @Override
        public int onImeStartPositioning(int displayId, int hiddenTop, int shownTop,
                boolean showing, boolean isFloating, SurfaceControl.Transaction t) {
            if (displayId != mDisplayId) return 0;
            final int imeTargetPosition = getImeTargetPosition();
            if (!mInitialized || imeTargetPosition == SPLIT_POSITION_UNDEFINED) return 0;
            mStartImeTop = showing ? hiddenTop : shownTop;
            mEndImeTop = showing ? shownTop : hiddenTop;

            // Update target dim values
            mLastDim1 = mDimValue1;
            mTargetDim1 = imeTargetPosition == SPLIT_POSITION_BOTTOM_OR_RIGHT && showing
                    ? ADJUSTED_NONFOCUS_DIM : 0.0f;
            mLastDim2 = mDimValue2;
            mTargetDim2 = imeTargetPosition == SPLIT_POSITION_TOP_OR_LEFT && showing
                    ? ADJUSTED_NONFOCUS_DIM : 0.0f;

            // Calculate target bounds offset for IME
            mLastYOffset = mYOffsetForIme;
            final boolean needOffset = imeTargetPosition == SPLIT_POSITION_BOTTOM_OR_RIGHT
                    && !isFloating && !isLandscape(mRootBounds) && showing;
            mTargetYOffset = needOffset ? getTargetYOffset() : 0;

            // Make {@link DividerView} non-interactive while IME showing in split mode. Listen to
            // ImePositionProcessor#onImeVisibilityChanged directly in DividerView is not enough
            // because DividerView won't receive onImeVisibilityChanged callback after it being
            // re-inflated.
            mSplitWindowManager.setInteractive(
                    !showing || imeTargetPosition == SPLIT_POSITION_UNDEFINED);

            return 0;
        }

        @Override
        public void onImePositionChanged(int displayId, int imeTop, SurfaceControl.Transaction t) {
            if (displayId != mDisplayId) return;
            onProgress(getProgress(imeTop));
            mSplitLayoutHandler.onBoundsChanging(SplitLayout.this);
        }

        @Override
        public void onImeEndPositioning(int displayId, boolean cancel,
                SurfaceControl.Transaction t) {
            if (displayId != mDisplayId || cancel) return;
            onProgress(1.0f);
            mSplitLayoutHandler.onBoundsChanging(SplitLayout.this);
        }

        private int getTargetYOffset() {
            final int desireOffset = Math.abs(mEndImeTop - mStartImeTop);
            // Make sure to keep at least 30% visible for the top split.
            final int maxOffset = (int) (mBounds1.height() * ADJUSTED_SPLIT_FRACTION_MAX);
            return -Math.min(desireOffset, maxOffset);
        }

        @SplitPosition
        private int getImeTargetPosition() {
            final WindowContainerToken token = mTaskOrganizer.getImeTarget(mDisplayId);
            return mSplitLayoutHandler.getSplitItemPosition(token);
        }

        private float getProgress(int currImeTop) {
            return ((float) currImeTop - mStartImeTop) / (mEndImeTop - mStartImeTop);
        }

        private void onProgress(float progress) {
            mDimValue1 = getProgressValue(mLastDim1, mTargetDim1, progress);
            mDimValue2 = getProgressValue(mLastDim2, mTargetDim2, progress);
            mYOffsetForIme =
                    (int) getProgressValue((float) mLastYOffset, (float) mTargetYOffset, progress);
        }

        private float getProgressValue(float start, float end, float progress) {
            return start + (end - start) * progress;
        }

        private void reset() {
            mYOffsetForIme = 0;
            mDimValue1 = mDimValue2 = 0.0f;
        }

        /* Adjust bounds with IME offset. */
        private Rect adjustForIme(Rect bounds) {
            final Rect temp = new Rect(bounds);
            if (mYOffsetForIme != 0) temp.offset(0, mYOffsetForIme);
            return temp;
        }

        private void applySurfaceDimValues(SurfaceControl.Transaction t, SurfaceControl dimLayer1,
                SurfaceControl dimLayer2) {
            t.setAlpha(dimLayer1, mDimValue1).setVisibility(dimLayer1, mDimValue1 > 0.001f);
            t.setAlpha(dimLayer2, mDimValue2).setVisibility(dimLayer2, mDimValue2 > 0.001f);
        }
    }
}
Loading