Merge "Update colors for Digital Wellbeing Toast." into sc-dev
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 1c7242d..342313d 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -76,6 +76,7 @@
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.os.UserHandle;
+import android.os.VibrationEffect;
 import android.text.Layout;
 import android.text.StaticLayout;
 import android.text.TextPaint;
@@ -132,6 +133,7 @@
 import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.TranslateEdgeEffect;
+import com.android.launcher3.util.VibratorWrapper;
 import com.android.launcher3.util.ViewPool;
 import com.android.quickstep.AnimatedFloat;
 import com.android.quickstep.BaseActivityInterface;
@@ -342,6 +344,17 @@
     private static final float INITIAL_DISMISS_TRANSLATION_INTERPOLATION_OFFSET = 0.55f;
     private static final float ADDITIONAL_DISMISS_TRANSLATION_INTERPOLATION_OFFSET = 0.05f;
 
+    private static final int SNAP_TO_PAGE_VIBRATION_PRIMITIVE =
+            Utilities.ATLEAST_S ? VibrationEffect.Composition.PRIMITIVE_LOW_TICK : -1;
+    private static final float SNAP_TO_PAGE_VIBRATION_PRIMITIVE_SCALE = 0.4f;
+    private static final VibrationEffect SNAP_TO_PAGE_VIBRATION_FALLBACK =
+            VibrationEffect.createPredefined(VibrationEffect.EFFECT_TEXTURE_TICK);
+    private static final int EDGE_IMPACT_VIBRATION_PRIMITIVE =
+            Utilities.ATLEAST_R ? VibrationEffect.Composition.PRIMITIVE_TICK : -1;
+    private static final float EDGE_IMPACT_VIBRATION_PRIMITIVE_SCALE = 0.8f;
+    private static final VibrationEffect EDGE_IMPACT_VIBRATION_FALLBACK =
+            VibrationEffect.createPredefined(VibrationEffect.EFFECT_TICK);
+
     protected final RecentsOrientedState mOrientationState;
     protected final BaseActivityInterface<STATE_TYPE, ACTIVITY_TYPE> mSizeStrategy;
     protected RecentsAnimationController mRecentsAnimationController;
@@ -960,6 +973,8 @@
     @Override
     protected void onPageEndTransition() {
         super.onPageEndTransition();
+        VibratorWrapper.INSTANCE.get(mContext).vibrate(SNAP_TO_PAGE_VIBRATION_PRIMITIVE,
+                SNAP_TO_PAGE_VIBRATION_PRIMITIVE_SCALE, SNAP_TO_PAGE_VIBRATION_FALLBACK);
         if (isClearAllHidden()) {
             mActionsView.updateDisabledFlags(OverviewActionsView.DISABLED_SCROLLING, false);
         }
@@ -1063,6 +1078,13 @@
     }
 
     @Override
+    protected void onEdgeAbsorbingScroll() {
+        super.onEdgeAbsorbingScroll();
+        VibratorWrapper.INSTANCE.get(mContext).vibrate(EDGE_IMPACT_VIBRATION_PRIMITIVE,
+                EDGE_IMPACT_VIBRATION_PRIMITIVE_SCALE, EDGE_IMPACT_VIBRATION_FALLBACK);
+    }
+
+    @Override
     protected void determineScrollingStart(MotionEvent ev, float touchSlopScale) {
         // Enables swiping to the left or right only if the task overlay is not modal.
         if (!isModal()) {
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index b26a7ea..9725b06 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -479,9 +479,11 @@
                 if (newPos < mMinScroll && oldPos >= mMinScroll) {
                     mEdgeGlowLeft.onAbsorb((int) mScroller.getCurrVelocity());
                     mScroller.abortAnimation();
+                    onEdgeAbsorbingScroll();
                 } else if (newPos > mMaxScroll && oldPos <= mMaxScroll) {
                     mEdgeGlowRight.onAbsorb((int) mScroller.getCurrVelocity());
                     mScroller.abortAnimation();
+                    onEdgeAbsorbingScroll();
                 }
             }
 
@@ -1332,6 +1334,13 @@
 
     protected void onNotSnappingToPageInFreeScroll() { }
 
+    /**
+     * Called when the view edges absorb part of the scroll. Subclasses can override this
+     * to provide custom behavior during animation.
+     */
+    protected void onEdgeAbsorbingScroll() {
+    }
+
     protected boolean shouldFlingForVelocity(int velocity) {
         float threshold = mAllowEasyFling ? mEasyFlingThresholdVelocity : mFlingThresholdVelocity;
         return Math.abs(velocity) > threshold;
diff --git a/src/com/android/launcher3/util/VibratorWrapper.java b/src/com/android/launcher3/util/VibratorWrapper.java
index b0defd4..14bf6c2 100644
--- a/src/com/android/launcher3/util/VibratorWrapper.java
+++ b/src/com/android/launcher3/util/VibratorWrapper.java
@@ -21,15 +21,19 @@
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 
+import android.annotation.SuppressLint;
 import android.annotation.TargetApi;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.database.ContentObserver;
+import android.media.AudioAttributes;
 import android.os.Build;
 import android.os.VibrationEffect;
 import android.os.Vibrator;
 import android.provider.Settings;
 
+import com.android.launcher3.Utilities;
+
 /**
  * Wrapper around {@link Vibrator} to easily perform haptic feedback where necessary.
  */
@@ -39,6 +43,11 @@
     public static final MainThreadInitializedObject<VibratorWrapper> INSTANCE =
             new MainThreadInitializedObject<>(VibratorWrapper::new);
 
+    public static final AudioAttributes VIBRATION_ATTRS = new AudioAttributes.Builder()
+            .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
+            .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
+            .build();
+
     public static final VibrationEffect EFFECT_CLICK =
             createPredefined(VibrationEffect.EFFECT_CLICK);
 
@@ -81,4 +90,24 @@
             UI_HELPER_EXECUTOR.execute(() -> mVibrator.vibrate(vibrationEffect));
         }
     }
+
+    /**
+     * Vibrates with a single primitive, if supported, or use a fallback effect instead. This only
+     * vibrates if haptic feedback is available and enabled.
+     */
+    @SuppressLint("NewApi")
+    public void vibrate(int primitiveId, float primitiveScale, VibrationEffect fallbackEffect) {
+        if (mHasVibrator && mIsHapticFeedbackEnabled) {
+            UI_HELPER_EXECUTOR.execute(() -> {
+                if (Utilities.ATLEAST_R && primitiveId >= 0
+                        && mVibrator.areAllPrimitivesSupported(primitiveId)) {
+                    mVibrator.vibrate(VibrationEffect.startComposition()
+                            .addPrimitive(primitiveId, primitiveScale)
+                            .compose(), VIBRATION_ATTRS);
+                } else {
+                    mVibrator.vibrate(fallbackEffect, VIBRATION_ATTRS);
+                }
+            });
+        }
+    }
 }