Merge "Merge "Skip duplicate setBounds requests for task fragments" into sc-v2-dev am: 7d1040f716" into sc-v2-dev-plus-aosp
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationCallback.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationCallback.java
index 24e5111..8969cc8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationCallback.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationCallback.java
@@ -46,7 +46,7 @@
     /**
      * Called when OneHanded animator is updating position
      */
-    default void onAnimationUpdate(float xPos, float yPos) {
+    default void onAnimationUpdate(SurfaceControl.Transaction tx, float xPos, float yPos) {
     }
 
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationController.java
index bfb2cc6..4b78328 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationController.java
@@ -182,10 +182,10 @@
         @Override
         public void onAnimationUpdate(ValueAnimator animation) {
             final SurfaceControl.Transaction tx = newSurfaceControlTransaction();
-            applySurfaceControlTransaction(mLeash, tx, animation.getAnimatedFraction());
             mOneHandedAnimationCallbacks.forEach(
-                    (callback) -> callback.onAnimationUpdate(0f, mCurrentValue)
+                    (callback) -> callback.onAnimationUpdate(tx, 0f, mCurrentValue)
             );
+            applySurfaceControlTransaction(mLeash, tx, animation.getAnimatedFraction());
         }
 
         void onStartTransaction(SurfaceControl leash, SurfaceControl.Transaction tx) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java
index 3ccb9e7..97461e6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java
@@ -16,12 +16,14 @@
 
 package com.android.wm.shell.onehanded;
 
+import android.animation.ValueAnimator;
 import android.content.Context;
 import android.graphics.Color;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
+import android.view.animation.LinearInterpolator;
 import android.window.DisplayAreaAppearedInfo;
 import android.window.DisplayAreaInfo;
 import android.window.DisplayAreaOrganizer;
@@ -29,8 +31,8 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
+import androidx.appcompat.view.ContextThemeWrapper;
 
-import com.android.internal.annotations.GuardedBy;
 import com.android.wm.shell.R;
 import com.android.wm.shell.common.DisplayLayout;
 
@@ -44,194 +46,212 @@
  * the screen has entered one handed mode.
  */
 public class OneHandedBackgroundPanelOrganizer extends DisplayAreaOrganizer
-        implements OneHandedTransitionCallback {
+        implements OneHandedAnimationCallback {
     private static final String TAG = "OneHandedBackgroundPanelOrganizer";
     private static final int THEME_COLOR_OFFSET = 10;
+    private static final int ALPHA_ANIMATION_DURATION = 200;
 
     private final Context mContext;
-    private final Object mLock = new Object();
     private final SurfaceSession mSurfaceSession = new SurfaceSession();
-    private final Executor mMainExecutor;
     private final OneHandedSurfaceTransactionHelper.SurfaceControlTransactionFactory
-            mSurfaceControlTransactionFactory;
+            mTransactionFactory;
 
-    private float[] mDefaultColor;
+    private ValueAnimator mAlphaAnimator;
+
+    private float mTranslationFraction;
+    private float[] mThemeColor;
 
     /**
      * The background to distinguish the boundary of translated windows and empty region when
      * one handed mode triggered.
      */
     private Rect mBkgBounds;
+    private Rect mStableInsets;
+
+    @Nullable
     @VisibleForTesting
-    @GuardedBy("mLock")
-    boolean mIsShowing;
+    SurfaceControl mBackgroundSurface;
     @Nullable
-    @GuardedBy("mLock")
-    private SurfaceControl mBackgroundSurface;
-    @Nullable
-    @GuardedBy("mLock")
     private SurfaceControl mParentLeash;
 
-    private final OneHandedAnimationCallback mOneHandedAnimationCallback =
-            new OneHandedAnimationCallback() {
-                @Override
-                public void onOneHandedAnimationStart(
-                        OneHandedAnimationController.OneHandedTransitionAnimator animator) {
-                    mMainExecutor.execute(() -> showBackgroundPanelLayer());
-                }
-            };
-
-    @Override
-    public void onStopFinished(Rect bounds) {
-        mMainExecutor.execute(() -> removeBackgroundPanelLayer());
-    }
-
     public OneHandedBackgroundPanelOrganizer(Context context, DisplayLayout displayLayout,
-            Executor executor) {
+            OneHandedSettingsUtil settingsUtil, Executor executor) {
         super(executor);
         mContext = context;
-        // Ensure the mBkgBounds is portrait, due to OHM only support on portrait
-        if (displayLayout.height() > displayLayout.width()) {
-            mBkgBounds = new Rect(0, 0, displayLayout.width(), displayLayout.height());
-        } else {
-            mBkgBounds = new Rect(0, 0, displayLayout.height(), displayLayout.width());
-        }
+        mTranslationFraction = settingsUtil.getTranslationFraction(context);
+        mTransactionFactory = SurfaceControl.Transaction::new;
         updateThemeColors();
-        mMainExecutor = executor;
-        mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
     }
 
     @Override
     public void onDisplayAreaAppeared(@NonNull DisplayAreaInfo displayAreaInfo,
             @NonNull SurfaceControl leash) {
-        synchronized (mLock) {
-            if (mParentLeash == null) {
-                mParentLeash = leash;
-            } else {
-                throw new RuntimeException("There should be only one DisplayArea for "
-                        + "the one-handed mode background panel");
-            }
-        }
-    }
-
-    OneHandedAnimationCallback getOneHandedAnimationCallback() {
-        return mOneHandedAnimationCallback;
+        mParentLeash = leash;
     }
 
     @Override
     public List<DisplayAreaAppearedInfo> registerOrganizer(int displayAreaFeature) {
-        synchronized (mLock) {
-            final List<DisplayAreaAppearedInfo> displayAreaInfos;
-            displayAreaInfos = super.registerOrganizer(displayAreaFeature);
-            for (int i = 0; i < displayAreaInfos.size(); i++) {
-                final DisplayAreaAppearedInfo info = displayAreaInfos.get(i);
-                onDisplayAreaAppeared(info.getDisplayAreaInfo(), info.getLeash());
-            }
-            return displayAreaInfos;
+        final List<DisplayAreaAppearedInfo> displayAreaInfos;
+        displayAreaInfos = super.registerOrganizer(displayAreaFeature);
+        for (int i = 0; i < displayAreaInfos.size(); i++) {
+            final DisplayAreaAppearedInfo info = displayAreaInfos.get(i);
+            onDisplayAreaAppeared(info.getDisplayAreaInfo(), info.getLeash());
         }
+        return displayAreaInfos;
     }
 
     @Override
     public void unregisterOrganizer() {
-        synchronized (mLock) {
-            super.unregisterOrganizer();
-            mParentLeash = null;
-        }
+        super.unregisterOrganizer();
+        removeBackgroundPanelLayer();
+        mParentLeash = null;
+    }
+
+    @Override
+    public void onAnimationUpdate(SurfaceControl.Transaction tx, float xPos, float yPos) {
+        final int yTopPos = (mStableInsets.top - mBkgBounds.height()) + Math.round(yPos);
+        tx.setPosition(mBackgroundSurface, 0, yTopPos);
     }
 
     @Nullable
     @VisibleForTesting
-    SurfaceControl getBackgroundSurface() {
-        synchronized (mLock) {
-            if (mParentLeash == null) {
-                return null;
-            }
+    boolean isRegistered() {
+        return mParentLeash != null;
+    }
 
-            if (mBackgroundSurface == null) {
-                mBackgroundSurface = new SurfaceControl.Builder(mSurfaceSession)
-                        .setParent(mParentLeash)
-                        .setBufferSize(mBkgBounds.width(), mBkgBounds.height())
-                        .setColorLayer()
-                        .setFormat(PixelFormat.RGB_888)
-                        .setOpaque(true)
-                        .setName("one-handed-background-panel")
-                        .setCallsite("OneHandedBackgroundPanelOrganizer")
-                        .build();
-            }
-            return mBackgroundSurface;
+    void createBackgroundSurface() {
+        mBackgroundSurface = new SurfaceControl.Builder(mSurfaceSession)
+                .setBufferSize(mBkgBounds.width(), mBkgBounds.height())
+                .setColorLayer()
+                .setFormat(PixelFormat.RGB_888)
+                .setOpaque(true)
+                .setName("one-handed-background-panel")
+                .setCallsite("OneHandedBackgroundPanelOrganizer")
+                .build();
+
+        // TODO(185890335) Avoid Dimming for mid-range luminance wallpapers flash.
+        mAlphaAnimator = ValueAnimator.ofFloat(1.0f, 0.0f);
+        mAlphaAnimator.setInterpolator(new LinearInterpolator());
+        mAlphaAnimator.setDuration(ALPHA_ANIMATION_DURATION);
+        mAlphaAnimator.addUpdateListener(
+                animator -> detachBackgroundFromParent(animator));
+    }
+
+    void detachBackgroundFromParent(ValueAnimator animator) {
+        if (mBackgroundSurface == null || mParentLeash == null) {
+            return;
         }
+        // TODO(185890335) Avoid Dimming for mid-range luminance wallpapers flash.
+        final float currentValue = (float) animator.getAnimatedValue();
+        final SurfaceControl.Transaction tx = mTransactionFactory.getTransaction();
+        if (currentValue == 0.0f) {
+            tx.reparent(mBackgroundSurface, null).apply();
+        } else {
+            tx.setAlpha(mBackgroundSurface, (float) animator.getAnimatedValue()).apply();
+        }
+    }
+
+    /**
+     * Called when onDisplayAdded() or onDisplayRemoved() callback.
+     *
+     * @param displayLayout The latest {@link DisplayLayout} representing current displayId
+     */
+    public void onDisplayChanged(DisplayLayout displayLayout) {
+        mStableInsets = displayLayout.stableInsets();
+        // Ensure the mBkgBounds is portrait, due to OHM only support on portrait
+        if (displayLayout.height() > displayLayout.width()) {
+            mBkgBounds = new Rect(0, 0, displayLayout.width(),
+                    Math.round(displayLayout.height() * mTranslationFraction) + mStableInsets.top);
+        } else {
+            mBkgBounds = new Rect(0, 0, displayLayout.height(),
+                    Math.round(displayLayout.width() * mTranslationFraction) + mStableInsets.top);
+        }
+    }
+
+    @VisibleForTesting
+    void onStart() {
+        if (mBackgroundSurface == null) {
+            createBackgroundSurface();
+        }
+        showBackgroundPanelLayer();
+    }
+
+    /**
+     * Called when transition finished.
+     */
+    public void onStopFinished() {
+        mAlphaAnimator.start();
     }
 
     @VisibleForTesting
     void showBackgroundPanelLayer() {
-        synchronized (mLock) {
-            if (mIsShowing) {
-                return;
-            }
-
-            if (getBackgroundSurface() == null) {
-                return;
-            }
-
-            SurfaceControl.Transaction transaction =
-                    mSurfaceControlTransactionFactory.getTransaction();
-            transaction.setLayer(mBackgroundSurface, -1 /* at bottom-most layer */)
-                    .setColor(mBackgroundSurface, mDefaultColor)
-                    .show(mBackgroundSurface)
-                    .apply();
-            transaction.close();
-            mIsShowing = true;
+        if (mParentLeash == null) {
+            return;
         }
+
+        if (mBackgroundSurface == null) {
+            createBackgroundSurface();
+        }
+
+        // TODO(185890335) Avoid Dimming for mid-range luminance wallpapers flash.
+        if (mAlphaAnimator.isRunning()) {
+            mAlphaAnimator.end();
+        }
+
+        mTransactionFactory.getTransaction()
+                .reparent(mBackgroundSurface, mParentLeash)
+                .setAlpha(mBackgroundSurface, 1.0f)
+                .setLayer(mBackgroundSurface, -1 /* at bottom-most layer */)
+                .setColor(mBackgroundSurface, mThemeColor)
+                .show(mBackgroundSurface)
+                .apply();
     }
 
     @VisibleForTesting
     void removeBackgroundPanelLayer() {
-        synchronized (mLock) {
-            if (mBackgroundSurface == null) {
-                return;
-            }
-
-            SurfaceControl.Transaction transaction =
-                    mSurfaceControlTransactionFactory.getTransaction();
-            transaction.remove(mBackgroundSurface).apply();
-            transaction.close();
-            mBackgroundSurface = null;
-            mIsShowing = false;
+        if (mBackgroundSurface == null) {
+            return;
         }
+
+        mTransactionFactory.getTransaction()
+                .remove(mBackgroundSurface)
+                .apply();
+        mBackgroundSurface = null;
     }
 
     /**
      * onConfigurationChanged events for updating tutorial text.
      */
     public void onConfigurationChanged() {
-        synchronized (mLock) {
-            if (mBackgroundSurface == null) {
-                getBackgroundSurface();
-            } else {
-                removeBackgroundPanelLayer();
-            }
-            updateThemeColors();
-            showBackgroundPanelLayer();
-        }
+        updateThemeColors();
+        showBackgroundPanelLayer();
     }
 
     private void updateThemeColors() {
-        synchronized (mLock) {
-            final int themeColor = mContext.getColor(R.color.one_handed_tutorial_background_color);
-            mDefaultColor = new float[]{(Color.red(themeColor) - THEME_COLOR_OFFSET) / 255.0f,
-                    (Color.green(themeColor) - THEME_COLOR_OFFSET) / 255.0f,
-                    (Color.blue(themeColor) - THEME_COLOR_OFFSET) / 255.0f};
-        }
+        final Context themedContext = new ContextThemeWrapper(mContext,
+                com.android.internal.R.style.Theme_DeviceDefault_DayNight);
+        final int themeColor = themedContext.getColor(
+                R.color.one_handed_tutorial_background_color);
+        mThemeColor = new float[]{
+                adjustColor(Color.red(themeColor)),
+                adjustColor(Color.green(themeColor)),
+                adjustColor(Color.blue(themeColor))};
+    }
+
+    private float adjustColor(int origColor) {
+        return Math.max(origColor - THEME_COLOR_OFFSET, 0) / 255.0f;
     }
 
     void dump(@NonNull PrintWriter pw) {
         final String innerPrefix = "  ";
         pw.println(TAG);
-        pw.print(innerPrefix + "mIsShowing=");
-        pw.println(mIsShowing);
+        pw.print(innerPrefix + "mBackgroundSurface=");
+        pw.println(mBackgroundSurface);
         pw.print(innerPrefix + "mBkgBounds=");
         pw.println(mBkgBounds);
-        pw.print(innerPrefix + "mDefaultColor=");
-        pw.println(mDefaultColor);
+        pw.print(innerPrefix + "mThemeColor=");
+        pw.println(mThemeColor);
+        pw.print(innerPrefix + "mTranslationFraction=");
+        pw.println(mTranslationFraction);
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
index 09cde38..b0fe856 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
@@ -180,6 +180,7 @@
                 public void onStopFinished(Rect bounds) {
                     mState.setState(STATE_NONE);
                     notifyShortcutStateChanged(STATE_NONE);
+                    mBackgroundPanelOrganizer.onStopFinished();
                 }
             };
 
@@ -223,13 +224,14 @@
         OneHandedTimeoutHandler timeoutHandler = new OneHandedTimeoutHandler(mainExecutor);
         OneHandedState transitionState = new OneHandedState();
         OneHandedTutorialHandler tutorialHandler = new OneHandedTutorialHandler(context,
-                windowManager);
+                settingsUtil, windowManager);
         OneHandedAnimationController animationController =
                 new OneHandedAnimationController(context);
         OneHandedTouchHandler touchHandler = new OneHandedTouchHandler(timeoutHandler,
                 mainExecutor);
         OneHandedBackgroundPanelOrganizer oneHandedBackgroundPanelOrganizer =
-                new OneHandedBackgroundPanelOrganizer(context, displayLayout, mainExecutor);
+                new OneHandedBackgroundPanelOrganizer(context, displayLayout, settingsUtil,
+                        mainExecutor);
         OneHandedDisplayAreaOrganizer organizer = new OneHandedDisplayAreaOrganizer(
                 context, displayLayout, settingsUtil, animationController, tutorialHandler,
                 oneHandedBackgroundPanelOrganizer, mainExecutor);
@@ -386,6 +388,7 @@
                 mDisplayAreaOrganizer.getDisplayLayout().height() * mOffSetFraction);
         mOneHandedAccessibilityUtil.announcementForScreenReader(
                 mOneHandedAccessibilityUtil.getOneHandedStartDescription());
+        mBackgroundPanelOrganizer.onStart();
         mDisplayAreaOrganizer.scheduleOffset(0, yOffSet);
         mTimeoutHandler.resetTimer();
         mOneHandedUiEventLogger.writeEvent(
@@ -423,7 +426,6 @@
                 stopOneHanded(OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_OVERSPACE_OUT));
         mDisplayAreaOrganizer.registerTransitionCallback(mTouchHandler);
         mDisplayAreaOrganizer.registerTransitionCallback(mTutorialHandler);
-        mDisplayAreaOrganizer.registerTransitionCallback(mBackgroundPanelOrganizer);
         mDisplayAreaOrganizer.registerTransitionCallback(mTransitionCallBack);
         if (mTaskChangeToExit) {
             mTaskStackListener.addListener(mTaskStackListenerCallback);
@@ -469,6 +471,7 @@
         final DisplayLayout newDisplayLayout = mDisplayController.getDisplayLayout(displayId);
         mDisplayAreaOrganizer.setDisplayLayout(newDisplayLayout);
         mTutorialHandler.onDisplayChanged(newDisplayLayout);
+        mBackgroundPanelOrganizer.onDisplayChanged(newDisplayLayout);
     }
 
     private ContentObserver getObserver(Runnable onChangeRunnable) {
@@ -606,7 +609,7 @@
                     OneHandedDisplayAreaOrganizer.FEATURE_ONE_HANDED);
         }
 
-        if (mBackgroundPanelOrganizer.getBackgroundSurface() == null) {
+        if (!mBackgroundPanelOrganizer.isRegistered()) {
             mBackgroundPanelOrganizer.registerOrganizer(
                     OneHandedBackgroundPanelOrganizer.FEATURE_ONE_HANDED_BACKGROUND_PANEL);
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
index 03a90c6..c2bbd9e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
@@ -135,8 +135,8 @@
                 SystemProperties.getInt(ONE_HANDED_MODE_TRANSLATE_ANIMATION_DURATION,
                         animationDurationConfig);
         mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
-        mTutorialHandler = tutorialHandler;
         mBackgroundPanelOrganizer = oneHandedBackgroundGradientOrganizer;
+        mTutorialHandler = tutorialHandler;
     }
 
     @Override
@@ -249,9 +249,8 @@
         if (animator != null) {
             animator.setTransitionDirection(direction)
                     .addOneHandedAnimationCallback(mOneHandedAnimationCallback)
-                    .addOneHandedAnimationCallback(mTutorialHandler.getAnimationCallback())
-                    .addOneHandedAnimationCallback(
-                            mBackgroundPanelOrganizer.getOneHandedAnimationCallback())
+                    .addOneHandedAnimationCallback(mTutorialHandler)
+                    .addOneHandedAnimationCallback(mBackgroundPanelOrganizer)
                     .setDuration(durationMs)
                     .start();
         }
@@ -267,7 +266,6 @@
         mLastVisualDisplayBounds.offsetTo(0, Math.round(mLastVisualOffset));
         for (int i = mTransitionCallbacks.size() - 1; i >= 0; i--) {
             final OneHandedTransitionCallback cb = mTransitionCallbacks.get(i);
-            cb.onStartTransition(false /* isTransitioning */);
             if (direction == TRANSITION_DIRECTION_TRIGGER) {
                 cb.onStartFinished(getLastVisualDisplayBounds());
             } else {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java
index aa6961a..21bc889 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java
@@ -20,6 +20,7 @@
 
 import android.annotation.IntDef;
 import android.content.ContentResolver;
+import android.content.Context;
 import android.database.ContentObserver;
 import android.net.Uri;
 import android.provider.Settings;
@@ -27,6 +28,8 @@
 
 import androidx.annotation.Nullable;
 
+import com.android.wm.shell.R;
+
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -36,7 +39,6 @@
  */
 public final class OneHandedSettingsUtil {
     private static final String TAG = "OneHandedSettingsUtil";
-
     private static final String ONE_HANDED_MODE_TARGET_NAME =
             ONE_HANDED_COMPONENT_NAME.getShortClassName();
 
@@ -217,6 +219,25 @@
                 Settings.Secure.ONE_HANDED_MODE_ACTIVATED, state, userId);
     }
 
+    /**
+     * Obtains one-handed mode transition duration from resource config.
+     *
+     * @return durationMs The duration in milli-seconds
+     */
+    public int getTransitionDuration(Context context) {
+        return context.getResources().getInteger(
+                R.integer.config_one_handed_translate_animation_duration);
+    }
+
+    /**
+     * Obtains one-handed mode offset fraction from resource config.
+     *
+     * @return fraction The fraction of offset of the whole screen.
+     */
+    public float getTranslationFraction(Context context) {
+        return context.getResources().getFraction(R.fraction.config_one_handed_offset, 1, 1);
+    }
+
     void dump(PrintWriter pw, String prefix, ContentResolver resolver,
             int userId) {
         final String innerPrefix = "  ";
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java
index 97e04b5..f58c6b1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java
@@ -32,9 +32,9 @@
 import android.content.res.TypedArray;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
-import android.os.SystemProperties;
 import android.view.Gravity;
 import android.view.LayoutInflater;
+import android.view.SurfaceControl;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowManager;
@@ -58,66 +58,54 @@
  * detach TargetViewContainer from window after exiting one handed mode.
  */
 public class OneHandedTutorialHandler implements OneHandedTransitionCallback,
-        OneHandedState.OnStateChangedListener {
+        OneHandedState.OnStateChangedListener, OneHandedAnimationCallback {
     private static final String TAG = "OneHandedTutorialHandler";
-    private static final String OFFSET_PERCENTAGE = "persist.debug.one_handed_offset_percentage";
-    private static final String TRANSLATE_ANIMATION_DURATION =
-            "persist.debug.one_handed_translate_animation_duration";
-    private static final float START_TRANSITION_FRACTION = 0.7f;
+    private static final float START_TRANSITION_FRACTION = 0.6f;
 
     private final float mTutorialHeightRatio;
     private final WindowManager mWindowManager;
-    private final OneHandedAnimationCallback mAnimationCallback;
 
     private @OneHandedState.State int mCurrentState;
     private int mTutorialAreaHeight;
 
     private Context mContext;
     private Rect mDisplayBounds;
+    private ValueAnimator mAlphaAnimator;
     private @Nullable View mTutorialView;
     private @Nullable ViewGroup mTargetViewContainer;
 
     private float mAlphaTransitionStart;
-    private ValueAnimator mAlphaAnimator;
     private int mAlphaAnimationDurationMs;
 
-    public OneHandedTutorialHandler(Context context, WindowManager windowManager) {
+    public OneHandedTutorialHandler(Context context, OneHandedSettingsUtil settingsUtil,
+            WindowManager windowManager) {
         mContext = context;
         mWindowManager = windowManager;
-        final float offsetPercentageConfig = context.getResources().getFraction(
-                R.fraction.config_one_handed_offset, 1, 1);
-        final int sysPropPercentageConfig = SystemProperties.getInt(
-                OFFSET_PERCENTAGE, Math.round(offsetPercentageConfig * 100.0f));
-        mTutorialHeightRatio = sysPropPercentageConfig / 100.0f;
-        final int animationDuration = context.getResources().getInteger(
-                R.integer.config_one_handed_translate_animation_duration);
-        mAlphaAnimationDurationMs = SystemProperties.getInt(TRANSLATE_ANIMATION_DURATION,
-                animationDuration);
-        mAnimationCallback = new OneHandedAnimationCallback() {
-            @Override
-            public void onOneHandedAnimationCancel(
-                    OneHandedAnimationController.OneHandedTransitionAnimator animator) {
-                if (mAlphaAnimator != null) {
-                    mAlphaAnimator.cancel();
-                }
-            }
+        mTutorialHeightRatio = settingsUtil.getTranslationFraction(context);
+        mAlphaAnimationDurationMs = settingsUtil.getTransitionDuration(context);
+    }
 
-            @Override
-            public void onAnimationUpdate(float xPos, float yPos) {
-                if (!isAttached()) {
-                    return;
-                }
-                if (yPos < mAlphaTransitionStart) {
-                    checkTransitionEnd();
-                    return;
-                }
-                if (mAlphaAnimator == null || mAlphaAnimator.isStarted()
-                        || mAlphaAnimator.isRunning()) {
-                    return;
-                }
-                mAlphaAnimator.start();
-            }
-        };
+    @Override
+    public void onOneHandedAnimationCancel(
+            OneHandedAnimationController.OneHandedTransitionAnimator animator) {
+        if (mAlphaAnimator != null) {
+            mAlphaAnimator.cancel();
+        }
+    }
+
+    @Override
+    public void onAnimationUpdate(SurfaceControl.Transaction tx, float xPos, float yPos) {
+        if (!isAttached()) {
+            return;
+        }
+        if (yPos < mAlphaTransitionStart) {
+            checkTransitionEnd();
+            return;
+        }
+        if (mAlphaAnimator == null || mAlphaAnimator.isStarted() || mAlphaAnimator.isRunning()) {
+            return;
+        }
+        mAlphaAnimator.start();
     }
 
     @Override
@@ -145,6 +133,7 @@
 
     /**
      * Called when onDisplayAdded() or onDisplayRemoved() callback.
+     *
      * @param displayLayout The latest {@link DisplayLayout} representing current displayId
      */
     public void onDisplayChanged(DisplayLayout displayLayout) {
@@ -196,10 +185,6 @@
         mTargetViewContainer = null;
     }
 
-    @Nullable OneHandedAnimationCallback getAnimationCallback() {
-        return mAnimationCallback;
-    }
-
     /**
      * Returns layout params for the dismiss target, using the latest display metrics.
      */
@@ -264,15 +249,17 @@
     private void setupAlphaTransition(boolean isEntering) {
         final float start = isEntering ? 0.0f : 1.0f;
         final float end = isEntering ? 1.0f : 0.0f;
+        final int duration = isEntering ? mAlphaAnimationDurationMs : Math.round(
+                mAlphaAnimationDurationMs * (1.0f - mTutorialHeightRatio));
         mAlphaAnimator = ValueAnimator.ofFloat(start, end);
         mAlphaAnimator.setInterpolator(new LinearInterpolator());
-        mAlphaAnimator.setDuration(mAlphaAnimationDurationMs);
+        mAlphaAnimator.setDuration(duration);
         mAlphaAnimator.addUpdateListener(
                 animator -> mTargetViewContainer.setAlpha((float) animator.getAnimatedValue()));
     }
 
     private void checkTransitionEnd() {
-        if (mAlphaAnimator != null && mAlphaAnimator.isRunning()) {
+        if (mAlphaAnimator != null && (mAlphaAnimator.isRunning() || mAlphaAnimator.isStarted())) {
             mAlphaAnimator.end();
             mAlphaAnimator.removeAllUpdateListeners();
             mAlphaAnimator = null;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizerTest.java
index 3f47c04..99c6107 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizerTest.java
@@ -22,7 +22,10 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.testing.AndroidTestingRunner;
@@ -54,17 +57,17 @@
     private OneHandedBackgroundPanelOrganizer mSpiedBackgroundPanelOrganizer;
     private WindowContainerToken mToken;
     private SurfaceControl mLeash;
-    private TestableLooper mTestableLooper;
 
     @Mock
     IWindowContainerToken mMockRealToken;
     @Mock
     DisplayController mMockDisplayController;
+    @Mock
+    OneHandedSettingsUtil mMockSettingsUtil;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mTestableLooper = TestableLooper.get(this);
         mToken = new WindowContainerToken(mMockRealToken);
         mLeash = new SurfaceControl();
         mDisplay = mContext.getDisplay();
@@ -74,32 +77,36 @@
                 FEATURE_ONE_HANDED_BACKGROUND_PANEL);
 
         mSpiedBackgroundPanelOrganizer = spy(
-                new OneHandedBackgroundPanelOrganizer(mContext, mDisplayLayout, Runnable::run));
+                new OneHandedBackgroundPanelOrganizer(mContext, mDisplayLayout, mMockSettingsUtil,
+                        Runnable::run));
+        mSpiedBackgroundPanelOrganizer.onDisplayChanged(mDisplayLayout);
     }
 
     @Test
     public void testOnDisplayAreaAppeared() {
         mSpiedBackgroundPanelOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash);
-        mTestableLooper.processAllMessages();
 
-        assertThat(mSpiedBackgroundPanelOrganizer.getBackgroundSurface()).isNotNull();
+        assertThat(mSpiedBackgroundPanelOrganizer.isRegistered()).isTrue();
+        verify(mSpiedBackgroundPanelOrganizer, never()).showBackgroundPanelLayer();
     }
 
     @Test
     public void testShowBackgroundLayer() {
-        mSpiedBackgroundPanelOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash);
-        mSpiedBackgroundPanelOrganizer.showBackgroundPanelLayer();
-        mTestableLooper.processAllMessages();
+        mSpiedBackgroundPanelOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, null);
+        mSpiedBackgroundPanelOrganizer.onStart();
 
-        assertThat(mSpiedBackgroundPanelOrganizer.mIsShowing).isTrue();
+        verify(mSpiedBackgroundPanelOrganizer).showBackgroundPanelLayer();
     }
 
     @Test
     public void testRemoveBackgroundLayer() {
         mSpiedBackgroundPanelOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash);
-        mSpiedBackgroundPanelOrganizer.removeBackgroundPanelLayer();
-        mTestableLooper.processAllMessages();
 
-        assertThat(mSpiedBackgroundPanelOrganizer.mIsShowing).isFalse();
+        assertThat(mSpiedBackgroundPanelOrganizer.isRegistered()).isNotNull();
+
+        reset(mSpiedBackgroundPanelOrganizer);
+        mSpiedBackgroundPanelOrganizer.removeBackgroundPanelLayer();
+
+        assertThat(mSpiedBackgroundPanelOrganizer.mBackgroundSurface).isNull();
     }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
index b224ae6..911fe07 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
@@ -110,7 +110,7 @@
         when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay);
         when(mMockDisplayAreaOrganizer.getDisplayAreaTokenMap()).thenReturn(new ArrayMap<>());
         when(mMockDisplayAreaOrganizer.isReady()).thenReturn(true);
-        when(mMockBackgroundOrganizer.getBackgroundSurface()).thenReturn(mMockLeash);
+        when(mMockBackgroundOrganizer.isRegistered()).thenReturn(true);
         when(mMockSettingsUitl.getSettingsOneHandedModeEnabled(any(), anyInt())).thenReturn(
                 mDefaultEnabled);
         when(mMockSettingsUitl.getSettingsOneHandedModeTimeout(any(), anyInt())).thenReturn(
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java
index e61f061..bea69c5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java
@@ -102,7 +102,7 @@
 
         when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay);
         when(mMockDisplayAreaOrganizer.getDisplayAreaTokenMap()).thenReturn(new ArrayMap<>());
-        when(mMockBackgroundOrganizer.getBackgroundSurface()).thenReturn(mMockLeash);
+        when(mMockBackgroundOrganizer.isRegistered()).thenReturn(true);
         when(mMockSettingsUitl.getSettingsOneHandedModeEnabled(any(), anyInt())).thenReturn(
                 mDefaultEnabled);
         when(mMockSettingsUitl.getSettingsOneHandedModeTimeout(any(), anyInt())).thenReturn(
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java
index ae1d3b2..b1434ca 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java
@@ -66,7 +66,7 @@
         mDisplayLayout = new DisplayLayout(mContext, mDisplay);
         mSpiedTransitionState = spy(new OneHandedState());
         mSpiedTutorialHandler = spy(
-                new OneHandedTutorialHandler(mContext, mMockWindowManager));
+                new OneHandedTutorialHandler(mContext, mMockSettingsUtil, mMockWindowManager));
         mTimeoutHandler = new OneHandedTimeoutHandler(mMockShellMainExecutor);
     }