OSDN Git Service

Improving the haptic notification experience
authorSelim Cinek <cinek@google.com>
Wed, 26 Jul 2017 19:20:38 +0000 (12:20 -0700)
committerSelim Cinek <cinek@google.com>
Wed, 26 Jul 2017 20:19:35 +0000 (13:19 -0700)
Added haptic feedback when expanding the notification panel
and when icons do animations while the user is touching
the screen.

Test: manual, add notification, observe
Change-Id: Id389bd0c7b6ac5584d4b93ac2c24c812a225d347
Fixes: 62392747

packages/SystemUI/res/values/config.xml
packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java

index 6eea030..a1aba59 100644 (file)
          one bar higher than they actually are -->
     <bool name="config_inflateSignalStrength">false</bool>
 
+    <!-- Should we vibrate on an icon animation of the shelf. This should only be active if the
+     vibrator is capable of subtle vibrations -->
+    <bool name="config_vibrateOnIconAnimation">false</bool>
+
 </resources>
index 1889806..41db927 100644 (file)
@@ -78,6 +78,9 @@ public class NotificationShelf extends ActivatableNotificationView implements
     private boolean mNoAnimationsInThisFrame;
     private boolean mAnimationsEnabled = true;
     private boolean mShowNotificationShelf;
+    private boolean mVibrationOnAnimation;
+    private boolean mUserTouchingScreen;
+    private boolean mTouchActive;
 
     public NotificationShelf(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -94,12 +97,24 @@ public class NotificationShelf extends ActivatableNotificationView implements
         setClipChildren(false);
         setClipToPadding(false);
         mShelfIcons.setShowAllIcons(false);
+        mVibrationOnAnimation = mContext.getResources().getBoolean(
+                R.bool.config_vibrateOnIconAnimation);
+        updateVibrationOnAnimation();
         mViewInvertHelper = new ViewInvertHelper(mShelfIcons,
                 NotificationPanelView.DOZE_ANIMATION_DURATION);
         mShelfState = new ShelfState();
         initDimens();
     }
 
+    private void updateVibrationOnAnimation() {
+        mShelfIcons.setVibrateOnAnimation(mVibrationOnAnimation && mTouchActive);
+    }
+
+    public void setTouchActive(boolean touchActive) {
+        mTouchActive = touchActive;
+        updateVibrationOnAnimation();
+    }
+
     public void bind(AmbientState ambientState, NotificationStackScrollLayout hostLayout) {
         mAmbientState = ambientState;
         mHostLayout = hostLayout;
index 05d47ec..e729f60 100644 (file)
@@ -131,6 +131,7 @@ public class StatusBarIconView extends AnimatedImageView {
     private final NotificationIconDozeHelper mDozer;
     private int mContrastedDrawableColor;
     private int mCachedContrastBackgroundColor = NO_COLOR;
+    private boolean mIsInShelf;
 
     public StatusBarIconView(Context context, String slot, StatusBarNotification sbn) {
         this(context, slot, sbn, false);
@@ -766,6 +767,14 @@ public class StatusBarIconView extends AnimatedImageView {
         }
     }
 
+    public void setIsInShelf(boolean isInShelf) {
+        mIsInShelf = isInShelf;
+    }
+
+    public boolean isInShelf() {
+        return mIsInShelf;
+    }
+
     public interface OnVisibilityChangedListener {
         void onVisibilityChanged(int newVisibility);
     }
index 38c8d31..e0d9748 100644 (file)
@@ -22,6 +22,8 @@ import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
 import android.graphics.drawable.Icon;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
 import android.support.v4.util.ArrayMap;
 import android.support.v4.util.ArraySet;
 import android.util.AttributeSet;
@@ -122,6 +124,8 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
     private float mVisualOverflowAdaption;
     private boolean mDisallowNextAnimation;
     private boolean mAnimationsEnabled = true;
+    private boolean mVibrateOnAnimation;
+    private Vibrator mVibrator;
     private ArrayMap<String, ArrayList<StatusBarIcon>> mReplacingIcons;
     private int mDarkOffsetX;
 
@@ -129,6 +133,7 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
         super(context, attrs);
         initDimens();
         setWillNotDraw(!DEBUG);
+        mVibrator = mContext.getSystemService(Vibrator.class);
     }
 
     private void initDimens() {
@@ -499,6 +504,10 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
         return width - (getWidth() - getActualPaddingStart() - getActualPaddingEnd()) > 0;
     }
 
+    public void setVibrateOnAnimation(boolean vibrateOnAnimation) {
+        mVibrateOnAnimation = vibrateOnAnimation;
+    }
+
     public int getIconSize() {
         return mIconSize;
     }
@@ -611,6 +620,13 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
                 } else {
                     super.applyToView(view);
                 }
+                boolean wasInShelf = icon.isInShelf();
+                boolean inShelf = iconAppearAmount == 1.0f;
+                icon.setIsInShelf(inShelf);
+                if (mVibrateOnAnimation && !justAdded && mAnimationsEnabled
+                        && wasInShelf != inShelf) {
+                    mVibrator.vibrate(VibrationEffect.get(VibrationEffect.EFFECT_TICK));
+                }
             }
             justAdded = false;
             justReplaced = false;
index 16d85be..d3ee550 100644 (file)
@@ -24,6 +24,8 @@ import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.os.SystemClock;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.InputDevice;
@@ -98,6 +100,7 @@ public abstract class PanelView extends FrameLayout {
     private FlingAnimationUtils mFlingAnimationUtilsClosing;
     private FlingAnimationUtils mFlingAnimationUtilsDismissing;
     private FalsingManager mFalsingManager;
+    private final Vibrator mVibrator;
 
     /**
      * Whether an instant expand request is currently pending and we are just waiting for layout.
@@ -199,6 +202,7 @@ public abstract class PanelView extends FrameLayout {
         mFalsingManager = FalsingManager.getInstance(context);
         mNotificationsDragEnabled =
                 getResources().getBoolean(R.bool.config_enableNotificationShadeDrag);
+        mVibrator = mContext.getSystemService(Vibrator.class);
     }
 
     protected void loadDimens() {
@@ -390,6 +394,7 @@ public abstract class PanelView extends FrameLayout {
         runPeekAnimation(INITIAL_OPENING_PEEK_DURATION, getOpeningHeight(),
                 false /* collapseWhenFinished */);
         notifyBarPanelExpansionChanged();
+        mVibrator.vibrate(VibrationEffect.get(VibrationEffect.EFFECT_CLICK));
     }
 
     protected abstract float getOpeningHeight();
index adc33a1..0dcf6d2 100644 (file)
@@ -246,6 +246,11 @@ public class StatusBarWindowView extends FrameLayout {
         return false;
     }
 
+    public void setTouchActive(boolean touchActive) {
+        mTouchActive = touchActive;
+        mStackScrollLayout.setTouchActive(touchActive);
+    }
+
     @Override
     public boolean dispatchTouchEvent(MotionEvent ev) {
         boolean isDown = ev.getActionMasked() == MotionEvent.ACTION_DOWN;
@@ -253,11 +258,11 @@ public class StatusBarWindowView extends FrameLayout {
             mNotificationPanel.startExpandLatencyTracking();
         }
         if (isDown) {
-            mTouchActive = true;
+            setTouchActive(true);
             mTouchCancelled = false;
         } else if (ev.getActionMasked() == MotionEvent.ACTION_UP
                 || ev.getActionMasked() == MotionEvent.ACTION_CANCEL) {
-            mTouchActive = false;
+            setTouchActive(false);
         }
         if (mTouchCancelled) {
             return false;
index 42cebe2..8a0b0d9 100644 (file)
@@ -4290,6 +4290,10 @@ public class NotificationStackScrollLayout extends ViewGroup
                 mAmbientState.getScrollY()));
     }
 
+    public void setTouchActive(boolean touchActive) {
+        mShelf.setTouchActive(touchActive);
+    }
+
     /**
      * A listener that is notified when some child locations might have changed.
      */