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
This commit is contained in:
@@ -14,7 +14,8 @@
|
||||
~ limitations under the License
|
||||
-->
|
||||
|
||||
<FrameLayout
|
||||
<!-- Extends FrameLayout -->
|
||||
<com.android.systemui.statusbar.NotificationOverflowContainer
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@@ -48,4 +49,4 @@
|
||||
android:layout_height="wrap_content"
|
||||
/>
|
||||
</com.android.systemui.statusbar.LatestItemView>
|
||||
</FrameLayout>
|
||||
</com.android.systemui.statusbar.NotificationOverflowContainer>
|
||||
|
||||
@@ -83,7 +83,7 @@
|
||||
<dimen name="notification_mid_height">128dp</dimen>
|
||||
|
||||
<!-- Height of a small notification in the status bar plus glow, padding, etc -->
|
||||
<dimen name="notification_row_min_height">70dp</dimen>
|
||||
<dimen name="notification_row_min_height">68dp</dimen>
|
||||
|
||||
<!-- Height of a large notification in the status bar plus glow, padding, etc -->
|
||||
<dimen name="notification_row_max_height">260dp</dimen>
|
||||
@@ -98,7 +98,7 @@
|
||||
<dimen name="status_bar_icon_padding">0dp</dimen>
|
||||
|
||||
<!-- half the distance between notifications in the panel -->
|
||||
<dimen name="notification_divider_height">3dp</dimen>
|
||||
<dimen name="notification_divider_height">2dp</dimen>
|
||||
|
||||
<!-- Notification drawer tuning parameters (phone UI) -->
|
||||
<!-- Initial velocity of the shade when expanding on its own -->
|
||||
|
||||
@@ -83,7 +83,7 @@ import java.util.ArrayList;
|
||||
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 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
protected int mZenMode;
|
||||
|
||||
protected boolean mOnKeyguard;
|
||||
protected View mKeyguardIconOverflowContainer;
|
||||
protected NotificationOverflowIconsView mOverflowIconsView;
|
||||
protected NotificationOverflowContainer mKeyguardIconOverflowContainer;
|
||||
|
||||
public boolean isDeviceProvisioned() {
|
||||
return mDeviceProvisioned;
|
||||
@@ -882,6 +881,7 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
}
|
||||
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 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
*/
|
||||
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 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
|| !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 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -25,7 +25,8 @@ import android.widget.FrameLayout;
|
||||
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 @@ public class ExpandableNotificationRow extends FrameLayout {
|
||||
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 @@ public class ExpandableNotificationRow extends FrameLayout {
|
||||
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 class ExpandableNotificationRow extends FrameLayout {
|
||||
*/
|
||||
public void setDimmed(boolean dimmed) {
|
||||
mLatestItemView.setDimmed(dimmed);
|
||||
mActivator.setDimmed(dimmed);
|
||||
}
|
||||
|
||||
public int getMaxExpandHeight() {
|
||||
@@ -220,6 +226,28 @@ public class ExpandableNotificationRow extends FrameLayout {
|
||||
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.
|
||||
*
|
||||
|
||||
@@ -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 @@ public class LatestItemView extends FrameLayout {
|
||||
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 @@ public class LatestItemView extends FrameLayout {
|
||||
|
||||
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 @@ public class LatestItemView extends FrameLayout {
|
||||
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 @@ public class LatestItemView extends FrameLayout {
|
||||
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;
|
||||
}
|
||||
if (mOnActivatedListener != null) {
|
||||
mOnActivatedListener.onReset(this);
|
||||
}
|
||||
mActivated = false;
|
||||
removeCallbacks(mTapTimeoutRunnable);
|
||||
}
|
||||
|
||||
@@ -180,4 +190,13 @@ public class LatestItemView extends FrameLayout {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -99,6 +99,7 @@ import com.android.systemui.statusbar.InterceptedNotifications;
|
||||
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 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode {
|
||||
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;
|
||||
@@ -2878,6 +2876,12 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode {
|
||||
mNotificationPanel.setExpandedFraction(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivated(View view) {
|
||||
userActivity();
|
||||
super.onActivated(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getMaxKeyguardNotifications() {
|
||||
return mKeyguardMaxNotificationCount;
|
||||
|
||||
Reference in New Issue
Block a user