Zoom notifications on lockscreen after tapping.
For the double tap interaction, this change introduces a new cue
that the notifications must be double-tapped: With the first tap,
the tapped notifications gets larger and the others fade out a bit.
Change-Id: Ib48ff0291aee1a5ec083b9e7ed1021bc420514cf
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index d48637d..2ea5add 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -83,7 +83,7 @@
import java.util.Locale;
public abstract class BaseStatusBar extends SystemUI implements
- CommandQueue.Callbacks {
+ CommandQueue.Callbacks, LatestItemView.OnActivatedListener {
public static final String TAG = "StatusBar";
public static final boolean DEBUG = false;
public static final boolean MULTIUSER_DEBUG = false;
@@ -169,8 +169,7 @@
protected int mZenMode;
protected boolean mOnKeyguard;
- protected View mKeyguardIconOverflowContainer;
- protected NotificationOverflowIconsView mOverflowIconsView;
+ protected NotificationOverflowContainer mKeyguardIconOverflowContainer;
public boolean isDeviceProvisioned() {
return mDeviceProvisioned;
@@ -882,6 +881,7 @@
}
entry.row = row;
entry.row.setHeightRange(mRowMinHeight, mRowMaxHeight);
+ entry.row.setOnActivatedListener(this);
entry.content = content;
entry.expanded = contentViewLocal;
entry.expandedPublic = publicViewLocal;
@@ -1067,7 +1067,7 @@
*/
protected void updateRowStates() {
int maxKeyguardNotifications = getMaxKeyguardNotifications();
- mOverflowIconsView.removeAllViews();
+ mKeyguardIconOverflowContainer.getIconsView().removeAllViews();
int n = mNotificationData.size();
int visibleNotifications = 0;
for (int i = n-1; i >= 0; i--) {
@@ -1087,7 +1087,7 @@
|| !showOnKeyguard)) {
entry.row.setVisibility(View.GONE);
if (showOnKeyguard) {
- mOverflowIconsView.addNotification(entry);
+ mKeyguardIconOverflowContainer.getIconsView().addNotification(entry);
}
} else {
entry.row.setVisibility(View.VISIBLE);
@@ -1095,13 +1095,49 @@
}
}
- if (mOnKeyguard && mOverflowIconsView.getChildCount() > 0) {
+ if (mOnKeyguard && mKeyguardIconOverflowContainer.getIconsView().getChildCount() > 0) {
mKeyguardIconOverflowContainer.setVisibility(View.VISIBLE);
} else {
mKeyguardIconOverflowContainer.setVisibility(View.GONE);
}
}
+ @Override
+ public void onActivated(View view) {
+ int n = mNotificationData.size();
+ for (int i = 0; i < n; i++) {
+ NotificationData.Entry entry = mNotificationData.get(i);
+ if (entry.row.getVisibility() != View.GONE) {
+ if (view == entry.row) {
+ entry.row.getActivator().activate();
+ } else {
+ entry.row.getActivator().activateInverse();
+ }
+ }
+ }
+ if (mKeyguardIconOverflowContainer.getVisibility() != View.GONE) {
+ if (view == mKeyguardIconOverflowContainer) {
+ mKeyguardIconOverflowContainer.getActivator().activate();
+ } else {
+ mKeyguardIconOverflowContainer.getActivator().activateInverse();
+ }
+ }
+ }
+
+ @Override
+ public void onReset(View view) {
+ int n = mNotificationData.size();
+ for (int i = 0; i < n; i++) {
+ NotificationData.Entry entry = mNotificationData.get(i);
+ if (entry.row.getVisibility() != View.GONE) {
+ entry.row.getActivator().reset();
+ }
+ }
+ if (mKeyguardIconOverflowContainer.getVisibility() != View.GONE) {
+ mKeyguardIconOverflowContainer.getActivator().reset();
+ }
+ }
+
private boolean shouldShowOnKeyguard(StatusBarNotification sbn) {
return sbn.getNotification().priority >= Notification.PRIORITY_LOW;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index a9fda63..fdf4dbf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -25,7 +25,8 @@
import com.android.internal.widget.SizeAdaptiveLayout;
import com.android.systemui.R;
-public class ExpandableNotificationRow extends FrameLayout {
+public class ExpandableNotificationRow extends FrameLayout
+ implements LatestItemView.OnActivatedListener {
private int mRowMinHeight;
private int mRowMaxHeight;
@@ -51,6 +52,8 @@
private SizeAdaptiveLayout mPrivateLayout;
private int mMaxExpandHeight;
private boolean mMaxHeightNeedsUpdate;
+ private NotificationActivator mActivator;
+ private LatestItemView.OnActivatedListener mOnActivatedListener;
public ExpandableNotificationRow(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -62,8 +65,10 @@
mPublicLayout = (SizeAdaptiveLayout) findViewById(R.id.expandedPublic);
mPrivateLayout = (SizeAdaptiveLayout) findViewById(R.id.expanded);
mLatestItemView = (LatestItemView) findViewById(R.id.container);
- }
+ mActivator = new NotificationActivator(this);
+ mLatestItemView.setOnActivatedListener(this);
+ }
public void setHeightRange(int rowMinHeight, int rowMaxHeight) {
mRowMinHeight = rowMinHeight;
@@ -202,6 +207,7 @@
*/
public void setDimmed(boolean dimmed) {
mLatestItemView.setDimmed(dimmed);
+ mActivator.setDimmed(dimmed);
}
public int getMaxExpandHeight() {
@@ -220,6 +226,28 @@
mLatestItemView.setLocked(locked);
}
+ public void setOnActivatedListener(LatestItemView.OnActivatedListener listener) {
+ mOnActivatedListener = listener;
+ }
+
+ public NotificationActivator getActivator() {
+ return mActivator;
+ }
+
+ @Override
+ public void onActivated(View view) {
+ if (mOnActivatedListener != null) {
+ mOnActivatedListener.onActivated(this);
+ }
+ }
+
+ @Override
+ public void onReset(View view) {
+ if (mOnActivatedListener != null) {
+ mOnActivatedListener.onReset(this);
+ }
+ }
+
/**
* Sets the resource id for the background of this notification.
*
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LatestItemView.java b/packages/SystemUI/src/com/android/systemui/statusbar/LatestItemView.java
index 74d837d..5e90084 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LatestItemView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LatestItemView.java
@@ -17,7 +17,6 @@
package com.android.systemui.statusbar;
import android.content.Context;
-import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
@@ -46,7 +45,8 @@
private float mDownX;
private float mDownY;
private final float mTouchSlop;
- private boolean mHotspotActive;
+
+ private OnActivatedListener mOnActivatedListener;
public LatestItemView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -90,14 +90,17 @@
private boolean handleTouchEventLocked(MotionEvent event) {
int action = event.getActionMasked();
- Drawable background = getBackground();
switch (action) {
case MotionEvent.ACTION_DOWN:
mDownX = event.getX();
mDownY = event.getY();
- if (!mActivated) {
- background.setHotspot(0, event.getX(), event.getY());
- mHotspotActive = true;
+
+ // Call the listener tentatively directly, even if we don't know whether the user
+ // will stay within the touch slop, as the listener is implemented as a scale
+ // animation, which is cancellable without jarring effects when swiping away
+ // notifications.
+ if (mOnActivatedListener != null) {
+ mOnActivatedListener.onActivated(this);
}
break;
case MotionEvent.ACTION_MOVE:
@@ -109,7 +112,7 @@
case MotionEvent.ACTION_UP:
if (isWithinTouchSlop(event)) {
if (!mActivated) {
- mActivated = true;
+ makeActive(event.getX(), event.getY());
postDelayed(mTapTimeoutRunnable, DOUBLETAP_TIMEOUT_MS);
} else {
performClick();
@@ -128,17 +131,24 @@
return true;
}
+ private void makeActive(float x, float y) {
+ getBackground().setHotspot(0, x, y);
+ mActivated = true;
+ }
+
/**
* Cancels the hotspot and makes the notification inactive.
*/
private void makeInactive() {
- if (mHotspotActive) {
+ if (mActivated) {
// Make sure that we clear the hotspot from the center.
- getBackground().setHotspot(0, getWidth()/2, getHeight()/2);
+ getBackground().setHotspot(0, getWidth() / 2, getHeight() / 2);
getBackground().removeHotspot(0);
- mHotspotActive = false;
+ mActivated = false;
}
- mActivated = false;
+ if (mOnActivatedListener != null) {
+ mOnActivatedListener.onReset(this);
+ }
removeCallbacks(mTapTimeoutRunnable);
}
@@ -180,4 +190,13 @@
private void updateBackgroundResource() {
setBackgroundResource(mDimmed ? mDimmedBgResId : mBgResId);
}
+
+ public void setOnActivatedListener(OnActivatedListener onActivatedListener) {
+ mOnActivatedListener = onActivatedListener;
+ }
+
+ public interface OnActivatedListener {
+ void onActivated(View view);
+ void onReset(View view);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationActivator.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationActivator.java
new file mode 100644
index 0000000..620e457
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationActivator.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2014 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.systemui.statusbar;
+
+import android.content.Context;
+import android.view.View;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+
+import com.android.systemui.R;
+
+/**
+ * A helper class used by both {@link com.android.systemui.statusbar.ExpandableNotificationRow} and
+ * {@link com.android.systemui.statusbar.NotificationOverflowIconsView} to make a notification look
+ * active after tapping it once on the Keyguard.
+ */
+public class NotificationActivator {
+
+ private static final int ANIMATION_LENGTH_MS = 220;
+ private static final float INVERSE_ALPHA = 0.9f;
+ private static final float DIMMED_SCALE = 0.95f;
+
+ private final View mTargetView;
+
+ private final Interpolator mFastOutSlowInInterpolator;
+ private final Interpolator mLinearOutSlowInInterpolator;
+ private final int mTranslationZ;
+
+ public NotificationActivator(View targetView) {
+ mTargetView = targetView;
+ Context ctx = targetView.getContext();
+ mFastOutSlowInInterpolator =
+ AnimationUtils.loadInterpolator(ctx, android.R.interpolator.fast_out_slow_in);
+ mLinearOutSlowInInterpolator =
+ AnimationUtils.loadInterpolator(ctx, android.R.interpolator.linear_out_slow_in);
+ mTranslationZ =
+ ctx.getResources().getDimensionPixelSize(R.dimen.z_distance_between_notifications);
+ mTargetView.animate().setDuration(ANIMATION_LENGTH_MS);
+ }
+
+ public void activateInverse() {
+ mTargetView.animate().withLayer().alpha(INVERSE_ALPHA);
+ }
+
+ public void activate() {
+ mTargetView.animate()
+ .setInterpolator(mLinearOutSlowInInterpolator)
+ .scaleX(1)
+ .scaleY(1)
+ .translationZBy(mTranslationZ);
+ }
+
+ public void reset() {
+ mTargetView.animate()
+ .setInterpolator(mFastOutSlowInInterpolator)
+ .scaleX(DIMMED_SCALE)
+ .scaleY(DIMMED_SCALE)
+ .translationZBy(-mTranslationZ);
+ if (mTargetView.getAlpha() != 1.0f) {
+ mTargetView.animate().withLayer().alpha(1);
+ }
+ }
+
+ public void setDimmed(boolean dimmed) {
+ if (dimmed) {
+ mTargetView.setScaleX(DIMMED_SCALE);
+ mTargetView.setScaleY(DIMMED_SCALE);
+ } else {
+ mTargetView.setScaleX(1);
+ mTargetView.setScaleY(1);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java
new file mode 100644
index 0000000..be58dad
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2014 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.systemui.statusbar;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.TextView;
+
+import com.android.systemui.R;
+
+/**
+ * Container view for overflowing notification icons on Keyguard.
+ */
+public class NotificationOverflowContainer extends FrameLayout
+ implements LatestItemView.OnActivatedListener {
+
+ private NotificationOverflowIconsView mIconsView;
+ private LatestItemView.OnActivatedListener mOnActivatedListener;
+ private NotificationActivator mActivator;
+
+ public NotificationOverflowContainer(Context context) {
+ super(context);
+ }
+
+ public NotificationOverflowContainer(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public NotificationOverflowContainer(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public NotificationOverflowContainer(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mIconsView = (NotificationOverflowIconsView) findViewById(R.id.overflow_icons_view);
+ mIconsView.setMoreText((TextView) findViewById(R.id.more_text));
+
+ LatestItemView latestItemView = (LatestItemView) findViewById(R.id.container);
+ mActivator = new NotificationActivator(this);
+ mActivator.setDimmed(true);
+ latestItemView.setOnActivatedListener(this);
+ latestItemView.setLocked(true);
+ }
+
+ public NotificationOverflowIconsView getIconsView() {
+ return mIconsView;
+ }
+
+ public void setOnActivatedListener(LatestItemView.OnActivatedListener onActivatedListener) {
+ mOnActivatedListener = onActivatedListener;
+ }
+
+ @Override
+ public void onActivated(View view) {
+ if (mOnActivatedListener != null) {
+ mOnActivatedListener.onActivated(this);
+ }
+ }
+
+ @Override
+ public void onReset(View view) {
+ if (mOnActivatedListener != null) {
+ mOnActivatedListener.onReset(this);
+ }
+ }
+
+ public NotificationActivator getActivator() {
+ return mActivator;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index ec9f3ab..1d01f91 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -99,6 +99,7 @@
import com.android.systemui.statusbar.LatestItemView;
import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.NotificationData.Entry;
+import com.android.systemui.statusbar.NotificationOverflowContainer;
import com.android.systemui.statusbar.NotificationOverflowIconsView;
import com.android.systemui.statusbar.SignalClusterView;
import com.android.systemui.statusbar.StatusBarIconView;
@@ -519,13 +520,10 @@
mStackScroller.setLongPressListener(getNotificationLongClicker());
mStackScroller.setChildLocationsChangedListener(mOnChildLocationsChangedListener);
- mKeyguardIconOverflowContainer = LayoutInflater.from(mContext).inflate(
- R.layout.status_bar_notification_keyguard_overflow, mStackScroller, false);
- ((LatestItemView) mKeyguardIconOverflowContainer.findViewById(R.id.container)).setLocked(true);
- mOverflowIconsView = (NotificationOverflowIconsView) mKeyguardIconOverflowContainer.findViewById(
- R.id.overflow_icons_view);
- mOverflowIconsView.setMoreText(
- (TextView) mKeyguardIconOverflowContainer.findViewById(R.id.more_text));
+ mKeyguardIconOverflowContainer =
+ (NotificationOverflowContainer) LayoutInflater.from(mContext).inflate(
+ R.layout.status_bar_notification_keyguard_overflow, mStackScroller, false);
+ mKeyguardIconOverflowContainer.setOnActivatedListener(this);
mStackScroller.addView(mKeyguardIconOverflowContainer);
mExpandedContents = mStackScroller;
@@ -2879,6 +2877,12 @@
}
@Override
+ public void onActivated(View view) {
+ userActivity();
+ super.onActivated(view);
+ }
+
+ @Override
protected int getMaxKeyguardNotifications() {
return mKeyguardMaxNotificationCount;
}