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

Commit b64fc6c2 authored by Sunny Goyal's avatar Sunny Goyal
Browse files

Fixing jump when swiping up in landscape and in waterfall cutout

> Adding tests for TaskViewSimulator to ensure proper calculations
> Disabling orientation listener while user is interacting with quickstep

Bug: 158781568
Bug: 156891776
Change-Id: I299c3b1243ac0dbf28faee1b8566c77ea3954e33
parent 714659f6
Loading
Loading
Loading
Loading
+21 −14
Original line number Diff line number Diff line
@@ -121,6 +121,10 @@ public abstract class BaseSwipeUpHandler<T extends StatefulActivity<?>, Q extend
        });

        mRecentsView.setOnScrollChangeListener((v, scrollX, scrollY, oldScrollX, oldScrollY) -> {
            // Wait until the first scroll event before applying scroll to taskViewSimulator.
            // Since, by default the current/running task already centered, this ensures that we
            // do not move the running task, in case RecentsView has not yet laid out completely.
            mRecentsViewScrollLinked = true;
            if (moveWindowWithRecentsScroll()) {
                updateFinalShift();
            }
@@ -128,7 +132,6 @@ public abstract class BaseSwipeUpHandler<T extends StatefulActivity<?>, Q extend
        runOnRecentsAnimationStart(() ->
                mRecentsView.setRecentsAnimationTargets(mRecentsAnimationController,
                        mRecentsAnimationTargets));
        mRecentsViewScrollLinked = true;
    }

    protected void startNewTask(Consumer<Boolean> resultCallback) {
@@ -205,10 +208,17 @@ public abstract class BaseSwipeUpHandler<T extends StatefulActivity<?>, Q extend
        mRecentsAnimationController = recentsAnimationController;
        mRecentsAnimationTargets = targets;
        mTransformParams.setTargetSet(mRecentsAnimationTargets);
        DeviceProfile dp = mTaskViewSimulator.getOrientationState().getLauncherDeviceProfile();
        RemoteAnimationTargetCompat runningTaskTarget = targets.findTask(
                mGestureState.getRunningTaskId());

        if (runningTaskTarget != null) {
            mTaskViewSimulator.setPreview(runningTaskTarget);
        }

        // Only initialize the device profile, if it has not been initialized before, as in some
        // configurations targets.homeContentInsets may not be correct.
        if (mActivity == null) {
            DeviceProfile dp = mTaskViewSimulator.getOrientationState().getLauncherDeviceProfile();
            if (targets.minimizedHomeBounds != null && runningTaskTarget != null) {
                Rect overviewStackBounds = mActivityInterface
                        .getOverviewWindowBounds(targets.minimizedHomeBounds, runningTaskTarget);
@@ -220,11 +230,8 @@ public abstract class BaseSwipeUpHandler<T extends StatefulActivity<?>, Q extend
            }
            dp.updateInsets(targets.homeContentInsets);
            dp.updateIsSeascape(mContext);
        if (runningTaskTarget != null) {
            mTaskViewSimulator.setPreview(runningTaskTarget);
        }

            initTransitionEndpoints(dp);
        }

        // Notify when the animation starts
        if (!mRecentsAnimationStartCallbacks.isEmpty()) {
+1 −1
Original line number Diff line number Diff line
@@ -255,8 +255,8 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy {
        float taskHeight = mTaskRect.height();

        mMatrix.set(mPositionHelper.getMatrix());
        mMatrix.postScale(scale, scale);
        mMatrix.postTranslate(insets.left, insets.top);
        mMatrix.postScale(scale, scale);

        // Apply TaskView matrix: translate, scale, scroll
        mMatrix.postTranslate(mTaskRect.left, mTaskRect.top);
+4 −0
Original line number Diff line number Diff line
@@ -998,6 +998,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
        mDwbToastShown = false;
        mActivity.getSystemUiController().updateUiState(UI_STATE_OVERVIEW, 0);
        LayoutUtils.setViewEnabled(mActionsView, true);
        mOrientationState.setGestureActive(false);
    }

    public @Nullable TaskView getRunningTaskView() {
@@ -1035,6 +1036,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
     */
    public void onGestureAnimationStart(int runningTaskId) {
        // This needs to be called before the other states are set since it can create the task view
        mOrientationState.setGestureActive(true);
        showCurrentTask(runningTaskId);
        setEnableFreeScroll(false);
        setEnableDrawingLiveTile(false);
@@ -1097,6 +1099,8 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
     * Called when a gesture from an app has finished.
     */
    public void onGestureAnimationEnd() {
        mOrientationState.setGestureActive(false);

        setOnScrollChangeListener(null);
        setEnableFreeScroll(true);
        setEnableDrawingLiveTile(true);
+203 −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.quickstep.util;

import static android.view.Display.DEFAULT_DISPLAY;

import android.content.Context;
import android.graphics.Rect;
import android.graphics.RectF;
import android.hardware.display.DisplayManager;
import android.view.Surface;
import android.view.SurfaceControl;

import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.shadows.LShadowDisplay;
import com.android.launcher3.util.DefaultDisplay;
import com.android.quickstep.LauncherActivityInterface;
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;

import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.LooperMode;
import org.robolectric.annotation.LooperMode.Mode;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowDisplayManager;

@RunWith(RobolectricTestRunner.class)
@LooperMode(Mode.PAUSED)
public class TaskViewSimulatorTest {

    @Test
    public void taskProperlyScaled_portrait_noRotation_sameInsets1() {
        new TaskMatrixVerifier()
                .withLauncherSize(1200, 2450)
                .withInsets(new Rect(0, 80, 0, 120))
                .verifyNoTransforms();
    }

    @Test
    public void taskProperlyScaled_portrait_noRotation_sameInsets2() {
        new TaskMatrixVerifier()
                .withLauncherSize(1200, 2450)
                .withInsets(new Rect(55, 80, 55, 120))
                .verifyNoTransforms();
    }

    @Test
    public void taskProperlyScaled_landscape_noRotation_sameInsets1() {
        new TaskMatrixVerifier()
                .withLauncherSize(2450, 1250)
                .withInsets(new Rect(0, 80, 0, 40))
                .verifyNoTransforms();
    }

    @Test
    public void taskProperlyScaled_landscape_noRotation_sameInsets2() {
        new TaskMatrixVerifier()
                .withLauncherSize(2450, 1250)
                .withInsets(new Rect(0, 80, 120, 0))
                .verifyNoTransforms();
    }

    @Test
    public void taskProperlyScaled_landscape_noRotation_sameInsets3() {
        new TaskMatrixVerifier()
                .withLauncherSize(2450, 1250)
                .withInsets(new Rect(55, 80, 55, 120))
                .verifyNoTransforms();
    }

    @Test
    public void taskProperlyScaled_landscape_rotated() {
        new TaskMatrixVerifier()
                .withLauncherSize(1200, 2450)
                .withInsets(new Rect(0, 80, 0, 120))
                .withAppBounds(
                        new Rect(0, 0, 2450, 1200),
                        new Rect(0, 80, 0, 120),
                        Surface.ROTATION_90)
                .verifyNoTransforms();
    }

    private static class TaskMatrixVerifier extends TransformParams {

        private final Context mContext = RuntimeEnvironment.application;

        private Rect mAppBounds = new Rect();
        private Rect mLauncherInsets = new Rect();

        private Rect mAppInsets;

        private int mAppRotation = -1;
        private DeviceProfile mDeviceProfile;

        TaskMatrixVerifier withLauncherSize(int width, int height) {
            ShadowDisplayManager.changeDisplay(DEFAULT_DISPLAY,
                    String.format("w%sdp-h%sdp-mdpi", width, height));
            if (mAppBounds.isEmpty()) {
                mAppBounds.set(0, 0, width, height);
            }
            return this;
        }

        TaskMatrixVerifier withInsets(Rect insets) {
            LShadowDisplay shadowDisplay = Shadow.extract(
                    mContext.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY));
            shadowDisplay.setInsets(insets);
            mLauncherInsets.set(insets);
            return this;
        }

        TaskMatrixVerifier withAppBounds(Rect bounds, Rect insets, int appRotation) {
            mAppBounds.set(bounds);
            mAppInsets = insets;
            mAppRotation = appRotation;
            return this;
        }

        void verifyNoTransforms() {
            mDeviceProfile = InvariantDeviceProfile.INSTANCE.get(mContext)
                    .getDeviceProfile(mContext);
            mDeviceProfile.updateInsets(mLauncherInsets);

            TaskViewSimulator tvs = new TaskViewSimulator(mContext,
                    LauncherActivityInterface.INSTANCE);
            tvs.setDp(mDeviceProfile);

            int launcherRotation = DefaultDisplay.INSTANCE.get(mContext).getInfo().rotation;
            if (mAppRotation < 0) {
                mAppRotation = launcherRotation;
            }
            tvs.setLayoutRotation(launcherRotation, mAppRotation);
            if (mAppInsets == null) {
                mAppInsets = new Rect(mLauncherInsets);
            }
            tvs.setPreviewBounds(mAppBounds, mAppInsets);

            tvs.fullScreenProgress.value = 1;
            tvs.recentsViewScale.value = tvs.getFullScreenScale();
            tvs.apply(this);
        }

        @Override
        public SurfaceParams[] createSurfaceParams(BuilderProxy proxy) {
            SurfaceParams.Builder builder = new SurfaceParams.Builder((SurfaceControl) null);
            proxy.onBuildTargetParams(builder, null, this);
            return new SurfaceParams[] {builder.build()};
        }

        @Override
        public void applySurfaceParams(SurfaceParams[] params) {
            // Verify that the task position remains the same
            RectF newAppBounds = new RectF(mAppBounds);
            params[0].matrix.mapRect(newAppBounds);
            Assert.assertThat(newAppBounds, new AlmostSame(mAppBounds));

            System.err.println("Bounds mapped: " + mAppBounds + " => " + newAppBounds);
        }
    }

    private static class AlmostSame extends TypeSafeMatcher<RectF>  {

        // Allow 1px error margin to account for float to int conversions
        private final float mError = 1f;
        private final Rect mExpected;

        AlmostSame(Rect expected) {
            mExpected = expected;
        }

        @Override
        protected boolean matchesSafely(RectF item) {
            return Math.abs(item.left - mExpected.left) < mError
                    && Math.abs(item.top - mExpected.top) < mError
                    && Math.abs(item.right - mExpected.right) < mError
                    && Math.abs(item.bottom - mExpected.bottom) < mError;
        }

        @Override
        public void describeTo(Description description) {
            description.appendValue(mExpected);
        }
    }
}
+13 −1
Original line number Diff line number Diff line
@@ -105,6 +105,9 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre
    private static final int FLAG_ROTATION_WATCHER_ENABLED = 1 << 6;
    // Enable home rotation for UI tests, ignoring home rotation value from prefs
    private static final int FLAG_HOME_ROTATION_FORCE_ENABLED_FOR_TESTING = 1 << 7;
    // Whether the swipe gesture is running, so the recents would stay locked in the
    // current orientation
    private static final int FLAG_SWIPE_UP_NOT_RUNNING = 1 << 8;

    private static final int MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE =
            FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_ACTIVITY
@@ -114,7 +117,8 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre
    // multi-window is enabled as in that case, activity itself rotates.
    private static final int VALUE_ROTATION_WATCHER_ENABLED =
            MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE | FLAG_SYSTEM_ROTATION_ALLOWED
                    | FLAG_ROTATION_WATCHER_SUPPORTED | FLAG_ROTATION_WATCHER_ENABLED;
                    | FLAG_ROTATION_WATCHER_SUPPORTED | FLAG_ROTATION_WATCHER_ENABLED
                    | FLAG_SWIPE_UP_NOT_RUNNING;

    private final Context mContext;
    private final ContentResolver mContentResolver;
@@ -156,6 +160,7 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre
        if (originalSmallestWidth < 600) {
            mFlags |= FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_DENSITY;
        }
        mFlags |= FLAG_SWIPE_UP_NOT_RUNNING;
        initFlags();
    }

@@ -166,6 +171,13 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre
        setFlag(FLAG_MULTIWINDOW_ROTATION_ALLOWED, isMultiWindow);
    }

    /**
     * Sets if the swipe up gesture is currently running or not
     */
    public void setGestureActive(boolean isGestureActive) {
        setFlag(FLAG_SWIPE_UP_NOT_RUNNING, !isGestureActive);
    }

    /**
     * Sets the appropriate {@link PagedOrientationHandler} for {@link #mOrientationHandler}
     * @param touchRotation The rotation the nav bar region that is touched is in
Loading