Merge "Initial implementation of dismiss gesture + inline settings icon" into nyc-dev
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2016, 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.
|
||||
-->
|
||||
<com.android.systemui.statusbar.NotificationSettingsIconRow
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:systemui="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
>
|
||||
|
||||
<com.android.systemui.statusbar.AlphaOptimizedImageView
|
||||
android:id="@+id/gear_icon"
|
||||
android:layout_width="@dimen/notification_gear_size"
|
||||
android:layout_height="@dimen/notification_gear_size"
|
||||
android:layout_marginTop="@dimen/notification_gear_top_margin"
|
||||
android:layout_marginStart="@dimen/notification_gear_side_margin"
|
||||
android:layout_marginEnd="@dimen/notification_gear_side_margin"
|
||||
android:src="@drawable/ic_settings"
|
||||
android:tint="@color/notification_gear_color"
|
||||
android:visibility="gone"
|
||||
android:alpha="0"
|
||||
android:background="?android:attr/selectableItemBackgroundBorderless"
|
||||
/>
|
||||
|
||||
</com.android.systemui.statusbar.NotificationSettingsIconRow>
|
||||
@@ -23,6 +23,14 @@
|
||||
android:clickable="true"
|
||||
>
|
||||
|
||||
<ViewStub
|
||||
android:layout="@layout/notification_settings_icon_row"
|
||||
android:id="@+id/settings_icon_row_stub"
|
||||
android:inflatedId="@+id/notification_settings_icon_row"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
/>
|
||||
|
||||
<com.android.systemui.statusbar.NotificationBackgroundView
|
||||
android:id="@+id/backgroundNormal"
|
||||
android:layout_width="match_parent"
|
||||
|
||||
@@ -102,6 +102,9 @@
|
||||
<!-- The color of the circle around the primary user in the user switcher -->
|
||||
<color name="current_user_border_color">@color/system_accent_color</color>
|
||||
|
||||
<!-- The color of the gear shown behind a notification -->
|
||||
<color name="notification_gear_color">#ff757575</color>
|
||||
|
||||
<!-- The "inside" of a notification, reached via longpress -->
|
||||
<color name="notification_guts_bg_color">#eeeeee</color>
|
||||
<color name="notification_guts_slider_color">@*android:color/material_deep_teal_500</color>
|
||||
|
||||
@@ -78,6 +78,15 @@
|
||||
<!-- Minimum layouted height of a notification in the statusbar-->
|
||||
<dimen name="min_notification_layout_height">48dp</dimen>
|
||||
|
||||
<!-- Size of gear icon displayed behind a notification -->
|
||||
<dimen name="notification_gear_size">24dp</dimen>
|
||||
|
||||
<!-- The space above the gear icon displayed behind a notification -->
|
||||
<dimen name="notification_gear_top_margin">30dp</dimen>
|
||||
|
||||
<!-- The space on either side of the gear icon displayed behind a notification -->
|
||||
<dimen name="notification_gear_side_margin">20dp</dimen>
|
||||
|
||||
<!-- size at which Notification icons will be drawn in the status bar -->
|
||||
<dimen name="status_bar_icon_drawing_size">17dip</dimen>
|
||||
|
||||
|
||||
@@ -69,9 +69,9 @@ public class SwipeHelper implements Gefingerpoken {
|
||||
private float mPerpendicularInitialTouchPos;
|
||||
private boolean mDragging;
|
||||
private View mCurrView;
|
||||
private View mCurrAnimView;
|
||||
private boolean mCanCurrViewBeDimissed;
|
||||
private float mDensityScale;
|
||||
private float mTranslation = 0;
|
||||
|
||||
private boolean mLongPressSent;
|
||||
private LongPressListener mLongPressListener;
|
||||
@@ -121,7 +121,7 @@ public class SwipeHelper implements Gefingerpoken {
|
||||
return mSwipeDirection == X ? ev.getY() : ev.getX();
|
||||
}
|
||||
|
||||
private float getTranslation(View v) {
|
||||
protected float getTranslation(View v) {
|
||||
return mSwipeDirection == X ? v.getTranslationX() : v.getTranslationY();
|
||||
}
|
||||
|
||||
@@ -130,7 +130,7 @@ public class SwipeHelper implements Gefingerpoken {
|
||||
vt.getYVelocity();
|
||||
}
|
||||
|
||||
private ObjectAnimator createTranslationAnimation(View v, float newPos) {
|
||||
protected ObjectAnimator createTranslationAnimation(View v, float newPos) {
|
||||
ObjectAnimator anim = ObjectAnimator.ofFloat(v,
|
||||
mSwipeDirection == X ? View.TRANSLATION_X : View.TRANSLATION_Y, newPos);
|
||||
return anim;
|
||||
@@ -141,7 +141,17 @@ public class SwipeHelper implements Gefingerpoken {
|
||||
vt.getXVelocity();
|
||||
}
|
||||
|
||||
private void setTranslation(View v, float translate) {
|
||||
protected Animator getViewTranslationAnimator(View v, float target,
|
||||
AnimatorUpdateListener listener) {
|
||||
ObjectAnimator anim = createTranslationAnimation(v, target);
|
||||
anim.addUpdateListener(listener);
|
||||
return anim;
|
||||
}
|
||||
|
||||
protected void setTranslation(View v, float translate) {
|
||||
if (v == null) {
|
||||
return;
|
||||
}
|
||||
if (mSwipeDirection == X) {
|
||||
v.setTranslationX(translate);
|
||||
} else {
|
||||
@@ -237,15 +247,16 @@ public class SwipeHelper implements Gefingerpoken {
|
||||
mTouchAboveFalsingThreshold = false;
|
||||
mDragging = false;
|
||||
mLongPressSent = false;
|
||||
mCurrView = mCallback.getChildAtPosition(ev);
|
||||
mVelocityTracker.clear();
|
||||
mCurrView = mCallback.getChildAtPosition(ev);
|
||||
|
||||
if (mCurrView != null) {
|
||||
mCurrAnimView = mCallback.getChildContentView(mCurrView);
|
||||
onDownUpdate(mCurrView);
|
||||
mCanCurrViewBeDimissed = mCallback.canChildBeDismissed(mCurrView);
|
||||
mVelocityTracker.addMovement(ev);
|
||||
mInitialTouchPos = getPos(ev);
|
||||
mPerpendicularInitialTouchPos = getPerpendicularPos(ev);
|
||||
|
||||
mTranslation = getTranslation(mCurrView);
|
||||
if (mLongPressListener != null) {
|
||||
if (mWatchLongPress == null) {
|
||||
mWatchLongPress = new Runnable() {
|
||||
@@ -268,7 +279,6 @@ public class SwipeHelper implements Gefingerpoken {
|
||||
}
|
||||
mHandler.postDelayed(mWatchLongPress, mLongPressTimeout);
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -283,8 +293,8 @@ public class SwipeHelper implements Gefingerpoken {
|
||||
&& Math.abs(delta) > Math.abs(deltaPerpendicular)) {
|
||||
mCallback.onBeginDrag(mCurrView);
|
||||
mDragging = true;
|
||||
mInitialTouchPos = getPos(ev) - getTranslation(mCurrAnimView);
|
||||
|
||||
mInitialTouchPos = getPos(ev);
|
||||
mTranslation = getTranslation(mCurrView);
|
||||
removeLongPressCallback();
|
||||
}
|
||||
}
|
||||
@@ -295,7 +305,6 @@ public class SwipeHelper implements Gefingerpoken {
|
||||
final boolean captured = (mDragging || mLongPressSent);
|
||||
mDragging = false;
|
||||
mCurrView = null;
|
||||
mCurrAnimView = null;
|
||||
mLongPressSent = false;
|
||||
removeLongPressCallback();
|
||||
if (captured) return true;
|
||||
@@ -320,12 +329,11 @@ public class SwipeHelper implements Gefingerpoken {
|
||||
* @param useAccelerateInterpolator Should an accelerating Interpolator be used
|
||||
* @param fixedDuration If not 0, this exact duration will be taken
|
||||
*/
|
||||
public void dismissChild(final View view, float velocity, final Runnable endAction,
|
||||
public void dismissChild(final View animView, float velocity, final Runnable endAction,
|
||||
long delay, boolean useAccelerateInterpolator, long fixedDuration) {
|
||||
final View animView = mCallback.getChildContentView(view);
|
||||
final boolean canAnimViewBeDismissed = mCallback.canChildBeDismissed(view);
|
||||
final boolean canBeDismissed = mCallback.canChildBeDismissed(animView);
|
||||
float newPos;
|
||||
boolean isLayoutRtl = view.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
|
||||
boolean isLayoutRtl = animView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
|
||||
|
||||
if (velocity < 0
|
||||
|| (velocity == 0 && getTranslation(animView) < 0)
|
||||
@@ -355,7 +363,13 @@ public class SwipeHelper implements Gefingerpoken {
|
||||
if (!mDisableHwLayers) {
|
||||
animView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
|
||||
}
|
||||
ObjectAnimator anim = createTranslationAnimation(animView, newPos);
|
||||
AnimatorUpdateListener updateListener = new AnimatorUpdateListener() {
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
onTranslationUpdate(animView, (float) animation.getAnimatedValue(), canBeDismissed);
|
||||
}
|
||||
};
|
||||
|
||||
Animator anim = getViewTranslationAnimator(animView, newPos, updateListener);
|
||||
if (useAccelerateInterpolator) {
|
||||
anim.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
|
||||
} else {
|
||||
@@ -367,8 +381,8 @@ public class SwipeHelper implements Gefingerpoken {
|
||||
}
|
||||
anim.addListener(new AnimatorListenerAdapter() {
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
updateSwipeProgressFromOffset(animView, canAnimViewBeDismissed);
|
||||
mCallback.onChildDismissed(view);
|
||||
updateSwipeProgressFromOffset(animView, canBeDismissed);
|
||||
mCallback.onChildDismissed(animView);
|
||||
if (endAction != null) {
|
||||
endAction.run();
|
||||
}
|
||||
@@ -377,11 +391,6 @@ public class SwipeHelper implements Gefingerpoken {
|
||||
}
|
||||
}
|
||||
});
|
||||
anim.addUpdateListener(new AnimatorUpdateListener() {
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
updateSwipeProgressFromOffset(animView, canAnimViewBeDismissed);
|
||||
}
|
||||
});
|
||||
prepareDismissAnimation(animView, anim);
|
||||
anim.start();
|
||||
}
|
||||
@@ -393,21 +402,21 @@ public class SwipeHelper implements Gefingerpoken {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
public void snapChild(final View view, float velocity) {
|
||||
final View animView = mCallback.getChildContentView(view);
|
||||
final boolean canAnimViewBeDismissed = mCallback.canChildBeDismissed(animView);
|
||||
ObjectAnimator anim = createTranslationAnimation(animView, 0);
|
||||
public void snapChild(final View animView, final float targetLeft, float velocity) {
|
||||
final boolean canBeDismissed = mCallback.canChildBeDismissed(animView);
|
||||
AnimatorUpdateListener updateListener = new AnimatorUpdateListener() {
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
onTranslationUpdate(animView, (float) animation.getAnimatedValue(), canBeDismissed);
|
||||
}
|
||||
};
|
||||
|
||||
Animator anim = getViewTranslationAnimator(animView, targetLeft, updateListener);
|
||||
int duration = SNAP_ANIM_LEN;
|
||||
anim.setDuration(duration);
|
||||
anim.addUpdateListener(new AnimatorUpdateListener() {
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
updateSwipeProgressFromOffset(animView, canAnimViewBeDismissed);
|
||||
}
|
||||
});
|
||||
anim.addListener(new AnimatorListenerAdapter() {
|
||||
public void onAnimationEnd(Animator animator) {
|
||||
updateSwipeProgressFromOffset(animView, canAnimViewBeDismissed);
|
||||
mCallback.onChildSnappedBack(animView);
|
||||
updateSwipeProgressFromOffset(animView, canBeDismissed);
|
||||
mCallback.onChildSnappedBack(animView, targetLeft);
|
||||
}
|
||||
});
|
||||
prepareSnapBackAnimation(animView, anim);
|
||||
@@ -421,6 +430,28 @@ public class SwipeHelper implements Gefingerpoken {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when there's a down event.
|
||||
*/
|
||||
public void onDownUpdate(View currView) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Called on a move event.
|
||||
*/
|
||||
protected void onMoveUpdate(View view, float totalTranslation, float delta) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Called in {@link AnimatorUpdateListener#onAnimationUpdate(ValueAnimator)} when the current
|
||||
* view is being animated to dismiss or snap.
|
||||
*/
|
||||
public void onTranslationUpdate(View animView, float value, boolean canBeDismissed) {
|
||||
updateSwipeProgressFromOffset(animView, canBeDismissed);
|
||||
}
|
||||
|
||||
public boolean onTouchEvent(MotionEvent ev) {
|
||||
if (mLongPressSent) {
|
||||
return true;
|
||||
@@ -456,17 +487,18 @@ public class SwipeHelper implements Gefingerpoken {
|
||||
// don't let items that can't be dismissed be dragged more than
|
||||
// maxScrollDistance
|
||||
if (CONSTRAIN_SWIPE && !mCallback.canChildBeDismissed(mCurrView)) {
|
||||
float size = getSize(mCurrAnimView);
|
||||
float maxScrollDistance = 0.15f * size;
|
||||
float size = getSize(mCurrView);
|
||||
float maxScrollDistance = 0.25f * size;
|
||||
if (absDelta >= size) {
|
||||
delta = delta > 0 ? maxScrollDistance : -maxScrollDistance;
|
||||
} else {
|
||||
delta = maxScrollDistance * (float) Math.sin((delta/size)*(Math.PI/2));
|
||||
}
|
||||
}
|
||||
setTranslation(mCurrAnimView, delta);
|
||||
|
||||
updateSwipeProgressFromOffset(mCurrAnimView, mCanCurrViewBeDimissed);
|
||||
setTranslation(mCurrView, mTranslation + delta);
|
||||
updateSwipeProgressFromOffset(mCurrView, mCanCurrViewBeDimissed);
|
||||
onMoveUpdate(mCurrView, mTranslation + delta, delta);
|
||||
}
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
@@ -478,12 +510,13 @@ public class SwipeHelper implements Gefingerpoken {
|
||||
float velocity = getVelocity(mVelocityTracker);
|
||||
float perpendicularVelocity = getPerpendicularVelocity(mVelocityTracker);
|
||||
|
||||
float translation = getTranslation(mCurrView);
|
||||
// Decide whether to dismiss the current view
|
||||
boolean childSwipedFarEnough = DISMISS_IF_SWIPED_FAR_ENOUGH &&
|
||||
Math.abs(getTranslation(mCurrAnimView)) > 0.4 * getSize(mCurrAnimView);
|
||||
Math.abs(translation) > 0.4 * getSize(mCurrView);
|
||||
boolean childSwipedFastEnough = (Math.abs(velocity) > escapeVelocity) &&
|
||||
(Math.abs(velocity) > Math.abs(perpendicularVelocity)) &&
|
||||
(velocity > 0) == (getTranslation(mCurrAnimView) > 0);
|
||||
(velocity > 0) == (translation > 0);
|
||||
boolean falsingDetected = mCallback.isAntiFalsingNeeded();
|
||||
|
||||
if (mFalsingManager.isClassiferEnabled()) {
|
||||
@@ -502,7 +535,7 @@ public class SwipeHelper implements Gefingerpoken {
|
||||
} else {
|
||||
// snappity
|
||||
mCallback.onDragCancelled(mCurrView);
|
||||
snapChild(mCurrView, velocity);
|
||||
snapChild(mCurrView, 0 /* leftTarget */, velocity);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -518,8 +551,6 @@ public class SwipeHelper implements Gefingerpoken {
|
||||
public interface Callback {
|
||||
View getChildAtPosition(MotionEvent ev);
|
||||
|
||||
View getChildContentView(View v);
|
||||
|
||||
boolean canChildBeDismissed(View v);
|
||||
|
||||
boolean isAntiFalsingNeeded();
|
||||
@@ -530,7 +561,13 @@ public class SwipeHelper implements Gefingerpoken {
|
||||
|
||||
void onDragCancelled(View v);
|
||||
|
||||
void onChildSnappedBack(View animView);
|
||||
/**
|
||||
* Called when the child is snapped to a position.
|
||||
*
|
||||
* @param animView the view that was snapped.
|
||||
* @param targetLeft the left position the view was snapped to.
|
||||
*/
|
||||
void onChildSnappedBack(View animView, float targetLeft);
|
||||
|
||||
/**
|
||||
* Updates the swipe progress on a child.
|
||||
|
||||
@@ -494,7 +494,7 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
|
||||
* onChildDismissed() calls.
|
||||
*/
|
||||
@Override
|
||||
public void onChildSnappedBack(View v) {
|
||||
public void onChildSnappedBack(View v, float targetLeft) {
|
||||
TaskView tv = (TaskView) v;
|
||||
|
||||
// Re-enable clipping with the stack
|
||||
@@ -516,11 +516,6 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getChildContentView(View v) {
|
||||
return v;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAntiFalsingNeeded() {
|
||||
return false;
|
||||
|
||||
@@ -103,6 +103,7 @@ import com.android.systemui.statusbar.policy.HeadsUpManager;
|
||||
import com.android.systemui.statusbar.policy.PreviewInflater;
|
||||
import com.android.systemui.statusbar.policy.RemoteInputView;
|
||||
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
|
||||
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout.GearDisplayedListener;
|
||||
import com.android.systemui.statusbar.stack.StackStateAnimator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -115,7 +116,7 @@ import static com.android.keyguard.KeyguardHostView.OnDismissAction;
|
||||
public abstract class BaseStatusBar extends SystemUI implements
|
||||
CommandQueue.Callbacks, ActivatableNotificationView.OnActivatedListener,
|
||||
ExpandableNotificationRow.ExpansionLogger, NotificationData.Environment,
|
||||
ExpandableNotificationRow.OnExpandClickListener {
|
||||
ExpandableNotificationRow.OnExpandClickListener, GearDisplayedListener {
|
||||
public static final String TAG = "StatusBar";
|
||||
public static final boolean DEBUG = false;
|
||||
public static final boolean MULTIUSER_DEBUG = false;
|
||||
@@ -220,6 +221,7 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
|
||||
// which notification is currently being longpress-examined by the user
|
||||
private NotificationGuts mNotificationGutsExposed;
|
||||
private ExpandableNotificationRow mNotificationGearDisplayed;
|
||||
|
||||
private KeyboardShortcuts mKeyboardShortcuts;
|
||||
|
||||
@@ -1008,6 +1010,10 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
guts.bindImportance(sbn, row, mNotificationData.getImportance(sbn.getKey()));
|
||||
}
|
||||
|
||||
protected GearDisplayedListener getGearDisplayedListener() {
|
||||
return this;
|
||||
}
|
||||
|
||||
protected SwipeHelper.LongPressListener getNotificationLongClicker() {
|
||||
return new SwipeHelper.LongPressListener() {
|
||||
@Override
|
||||
@@ -1020,7 +1026,7 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
return false;
|
||||
}
|
||||
|
||||
ExpandableNotificationRow row = (ExpandableNotificationRow) v;
|
||||
final ExpandableNotificationRow row = (ExpandableNotificationRow) v;
|
||||
bindGuts(row);
|
||||
|
||||
// Assume we are a status_bar_notification_row
|
||||
@@ -1052,6 +1058,14 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
= ViewAnimationUtils.createCircularReveal(guts, x, y, 0, r);
|
||||
a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
|
||||
a.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
|
||||
a.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
super.onAnimationEnd(animation);
|
||||
// Move the notification view back over the gear
|
||||
row.resetTranslation();
|
||||
}
|
||||
});
|
||||
a.start();
|
||||
guts.setExposed(true);
|
||||
mStackScroller.onHeightChanged(null, true /* needsAnimation */);
|
||||
@@ -1063,6 +1077,11 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGearDisplayed(ExpandableNotificationRow row) {
|
||||
mNotificationGearDisplayed = row;
|
||||
}
|
||||
|
||||
public void dismissPopups() {
|
||||
dismissPopups(-1, -1);
|
||||
}
|
||||
@@ -1095,6 +1114,11 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
v.setExposed(false);
|
||||
mStackScroller.onHeightChanged(null, true /* needsAnimation */);
|
||||
}
|
||||
|
||||
if (mNotificationGearDisplayed != null) {
|
||||
mNotificationGearDisplayed.resetTranslation();
|
||||
mNotificationGearDisplayed = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -16,6 +16,12 @@
|
||||
|
||||
package com.android.systemui.statusbar;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.animation.ValueAnimator.AnimatorUpdateListener;
|
||||
import android.app.Notification;
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.AnimatedVectorDrawable;
|
||||
@@ -45,6 +51,7 @@ import com.android.systemui.statusbar.stack.StackScrollState;
|
||||
import com.android.systemui.statusbar.stack.StackStateAnimator;
|
||||
import com.android.systemui.statusbar.stack.StackViewState;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ExpandableNotificationRow extends ActivatableNotificationView {
|
||||
@@ -87,6 +94,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
|
||||
*/
|
||||
private boolean mOnKeyguard;
|
||||
|
||||
private AnimatorSet mTranslateAnim;
|
||||
private ArrayList<View> mTranslateableViews;
|
||||
private NotificationContentView mPublicLayout;
|
||||
private NotificationContentView mPrivateLayout;
|
||||
private int mMaxExpandHeight;
|
||||
@@ -96,6 +105,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
|
||||
private ExpansionLogger mLogger;
|
||||
private String mLoggingKey;
|
||||
private boolean mWasReset;
|
||||
private NotificationSettingsIconRow mSettingsIconRow;
|
||||
private NotificationGuts mGuts;
|
||||
private NotificationData.Entry mEntry;
|
||||
private StatusBarNotification mStatusBarNotification;
|
||||
@@ -108,6 +118,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
|
||||
private boolean mChildrenExpanded;
|
||||
private boolean mIsSummaryWithChildren;
|
||||
private NotificationChildrenContainer mChildrenContainer;
|
||||
private ViewStub mSettingsIconRowStub;
|
||||
private ViewStub mGutsStub;
|
||||
private boolean mIsSystemChildExpanded;
|
||||
private boolean mIsPinned;
|
||||
@@ -527,6 +538,17 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
|
||||
mGuts.setVisibility(oldGuts.getVisibility());
|
||||
addView(mGuts, index);
|
||||
}
|
||||
if (mSettingsIconRow != null) {
|
||||
View oldSettings = mSettingsIconRow;
|
||||
int settingsIndex = indexOfChild(oldSettings);
|
||||
removeView(oldSettings);
|
||||
mSettingsIconRow = (NotificationSettingsIconRow) LayoutInflater.from(mContext).inflate(
|
||||
R.layout.notification_settings_icon_row, this, false);
|
||||
mSettingsIconRow.setNotificationRowParent(ExpandableNotificationRow.this);
|
||||
mSettingsIconRow.setVisibility(oldSettings.getVisibility());
|
||||
addView(mSettingsIconRow, settingsIndex);
|
||||
|
||||
}
|
||||
mPrivateLayout.reInflateViews();
|
||||
mPublicLayout.reInflateViews();
|
||||
}
|
||||
@@ -573,6 +595,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
|
||||
mPublicLayout.reset(mIsHeadsUp);
|
||||
mPrivateLayout.reset(mIsHeadsUp);
|
||||
resetHeight();
|
||||
resetTranslation();
|
||||
logExpansionEvent(false, wasExpanded);
|
||||
}
|
||||
|
||||
@@ -596,6 +619,14 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
|
||||
mPrivateLayout.setExpandClickListener(mExpandClickListener);
|
||||
mPrivateLayout.setContainingNotification(this);
|
||||
mPublicLayout.setExpandClickListener(mExpandClickListener);
|
||||
mSettingsIconRowStub = (ViewStub) findViewById(R.id.settings_icon_row_stub);
|
||||
mSettingsIconRowStub.setOnInflateListener(new ViewStub.OnInflateListener() {
|
||||
@Override
|
||||
public void onInflate(ViewStub stub, View inflated) {
|
||||
mSettingsIconRow = (NotificationSettingsIconRow) inflated;
|
||||
mSettingsIconRow.setNotificationRowParent(ExpandableNotificationRow.this);
|
||||
}
|
||||
});
|
||||
mGutsStub = (ViewStub) findViewById(R.id.notification_guts_stub);
|
||||
mGutsStub.setOnInflateListener(new ViewStub.OnInflateListener() {
|
||||
@Override
|
||||
@@ -603,6 +634,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
|
||||
mGuts = (NotificationGuts) inflated;
|
||||
mGuts.setClipTopAmount(getClipTopAmount());
|
||||
mGuts.setActualHeight(getActualHeight());
|
||||
mTranslateableViews.add(mGuts);
|
||||
mGutsStub = null;
|
||||
}
|
||||
});
|
||||
@@ -613,9 +645,89 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
|
||||
public void onInflate(ViewStub stub, View inflated) {
|
||||
mChildrenContainer = (NotificationChildrenContainer) inflated;
|
||||
mChildrenContainer.setNotificationParent(ExpandableNotificationRow.this);
|
||||
mTranslateableViews.add(mChildrenContainer);
|
||||
}
|
||||
});
|
||||
mVetoButton = findViewById(R.id.veto);
|
||||
|
||||
// Add the views that we translate to reveal the gear
|
||||
mTranslateableViews = new ArrayList<View>();
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
mTranslateableViews.add(getChildAt(i));
|
||||
}
|
||||
// Remove views that don't translate
|
||||
mTranslateableViews.remove(mVetoButton);
|
||||
mTranslateableViews.remove(mSettingsIconRowStub);
|
||||
mTranslateableViews.remove(mChildrenContainerStub);
|
||||
mTranslateableViews.remove(mGutsStub);
|
||||
}
|
||||
|
||||
public void setTranslationForOutline(float translationX) {
|
||||
setOutlineRect(false, translationX, getTop(), getRight() + translationX, getBottom());
|
||||
}
|
||||
|
||||
public void resetTranslation() {
|
||||
if (mTranslateableViews != null) {
|
||||
for (int i = 0; i < mTranslateableViews.size(); i++) {
|
||||
mTranslateableViews.get(i).setTranslationX(0);
|
||||
}
|
||||
setTranslationForOutline(0);
|
||||
}
|
||||
if (mSettingsIconRow != null) {
|
||||
mSettingsIconRow.resetState();
|
||||
}
|
||||
}
|
||||
|
||||
public void animateTranslateNotification(final float leftTarget) {
|
||||
if (mTranslateAnim != null) {
|
||||
mTranslateAnim.cancel();
|
||||
}
|
||||
AnimatorSet set = new AnimatorSet();
|
||||
if (mTranslateableViews != null) {
|
||||
for (int i = 0; i < mTranslateableViews.size(); i++) {
|
||||
final View animView = mTranslateableViews.get(i);
|
||||
final ObjectAnimator translateAnim = ObjectAnimator.ofFloat(
|
||||
animView, "translationX", leftTarget);
|
||||
if (i == 0) {
|
||||
translateAnim.addUpdateListener(new AnimatorUpdateListener() {
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
setTranslationForOutline((float) animation.getAnimatedValue());
|
||||
}
|
||||
});
|
||||
}
|
||||
translateAnim.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator anim) {
|
||||
if (mSettingsIconRow != null && leftTarget == 0) {
|
||||
mSettingsIconRow.resetState();
|
||||
}
|
||||
mTranslateAnim = null;
|
||||
}
|
||||
});
|
||||
set.play(translateAnim);
|
||||
}
|
||||
}
|
||||
mTranslateAnim = set;
|
||||
set.start();
|
||||
}
|
||||
|
||||
public float getSpaceForGear() {
|
||||
if (mSettingsIconRow != null) {
|
||||
return mSettingsIconRow.getSpaceForGear();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public NotificationSettingsIconRow getSettingsRow() {
|
||||
if (mSettingsIconRow == null) {
|
||||
mSettingsIconRowStub.inflate();
|
||||
}
|
||||
return mSettingsIconRow;
|
||||
}
|
||||
|
||||
public ArrayList<View> getContentViews() {
|
||||
return mTranslateableViews;
|
||||
}
|
||||
|
||||
public void inflateGuts() {
|
||||
@@ -772,7 +884,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
|
||||
if (mIsSummaryWithChildren) {
|
||||
mChildrenContainer.updateGroupOverflow();
|
||||
}
|
||||
notifyHeightChanged(false /* needsAnimation */);
|
||||
notifyHeightChanged(false /* needsAnimation */);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1097,6 +1209,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
|
||||
mNotificationHeaderWrapper = NotificationViewWrapper.wrap(getContext(),
|
||||
mNotificationHeader);
|
||||
addView(mNotificationHeader, indexOfChild(mChildrenContainer) + 1);
|
||||
mTranslateableViews.add(mNotificationHeader);
|
||||
} else {
|
||||
header.reapply(getContext(), mNotificationHeader);
|
||||
mNotificationHeaderWrapper.notifyContentUpdated(mEntry.notification);
|
||||
|
||||
@@ -81,8 +81,13 @@ public abstract class ExpandableOutlineView extends ExpandableView {
|
||||
}
|
||||
|
||||
protected void setOutlineRect(float left, float top, float right, float bottom) {
|
||||
setOutlineRect(true, left, top, right, bottom);
|
||||
}
|
||||
|
||||
protected void setOutlineRect(boolean clipToOutline, float left, float top, float right,
|
||||
float bottom) {
|
||||
mCustomOutline = true;
|
||||
setClipToOutline(true);
|
||||
setClipToOutline(clipToOutline);
|
||||
|
||||
mOutlineRect.set((int) left, (int) top, (int) right, (int) bottom);
|
||||
|
||||
|
||||
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import com.android.systemui.Interpolators;
|
||||
import com.android.systemui.R;
|
||||
|
||||
public class NotificationSettingsIconRow extends FrameLayout implements View.OnClickListener {
|
||||
|
||||
public interface SettingsIconRowListener {
|
||||
/**
|
||||
* Called when the gear behind a notification is touched.
|
||||
*/
|
||||
public void onGearTouched(ExpandableNotificationRow row);
|
||||
}
|
||||
|
||||
private ExpandableNotificationRow mParent;
|
||||
private AlphaOptimizedImageView mGearIcon;
|
||||
private float mHorizSpaceForGear;
|
||||
private SettingsIconRowListener mListener;
|
||||
|
||||
private ValueAnimator mFadeAnimator;
|
||||
private boolean mSettingsFadedIn = false;
|
||||
private boolean mAnimating = false;
|
||||
private boolean mOnLeft = true;
|
||||
|
||||
public NotificationSettingsIconRow(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public NotificationSettingsIconRow(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public NotificationSettingsIconRow(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
this(context, attrs, defStyleAttr, 0);
|
||||
}
|
||||
|
||||
public NotificationSettingsIconRow(Context context, AttributeSet attrs, int defStyleAttr,
|
||||
int defStyleRes) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
mGearIcon = (AlphaOptimizedImageView) findViewById(R.id.gear_icon);
|
||||
mGearIcon.setOnClickListener(this);
|
||||
|
||||
final float iconMargin =
|
||||
((ViewGroup.MarginLayoutParams) mGearIcon.getLayoutParams()).getMarginStart();
|
||||
final float iconWidth =
|
||||
getResources().getDimensionPixelOffset(R.dimen.notification_gear_size);
|
||||
mHorizSpaceForGear = (iconWidth + iconMargin * 2);
|
||||
resetState();
|
||||
}
|
||||
|
||||
public void setGearListener(SettingsIconRowListener listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
public void setNotificationRowParent(ExpandableNotificationRow parent) {
|
||||
mParent = parent;
|
||||
}
|
||||
|
||||
public ExpandableNotificationRow getNotificationParent() {
|
||||
return mParent;
|
||||
}
|
||||
|
||||
public void resetState() {
|
||||
setGearAlpha(0f);
|
||||
mAnimating = false;
|
||||
setIconLocation(true /* on left */);
|
||||
}
|
||||
|
||||
private void setGearAlpha(float alpha) {
|
||||
if (alpha == 0) {
|
||||
mSettingsFadedIn = false; // Can fade in again once it's gone.
|
||||
mGearIcon.setVisibility(View.INVISIBLE);
|
||||
} else {
|
||||
if (alpha == 1) {
|
||||
mSettingsFadedIn = true;
|
||||
}
|
||||
mGearIcon.setVisibility(View.VISIBLE);
|
||||
}
|
||||
mGearIcon.setAlpha(alpha);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the horizontal space in pixels required to display the gear behind a notification.
|
||||
*/
|
||||
public float getSpaceForGear() {
|
||||
return mHorizSpaceForGear;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether the gear is visible at 1 alpha. Does not indicate
|
||||
* if entire view is visible.
|
||||
*/
|
||||
public boolean isVisible() {
|
||||
return mSettingsFadedIn;
|
||||
}
|
||||
|
||||
public void cancelFadeAnimator() {
|
||||
if (mFadeAnimator != null) {
|
||||
mFadeAnimator.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
public void updateSettingsIcons(final float transX, final float size) {
|
||||
if (mAnimating || (mGearIcon.getAlpha() == 0)) {
|
||||
// Don't adjust when animating or settings aren't visible
|
||||
return;
|
||||
}
|
||||
setIconLocation(transX > 0 /* fromLeft */);
|
||||
final float fadeThreshold = size * 0.3f;
|
||||
final float absTrans = Math.abs(transX);
|
||||
float desiredAlpha = 0;
|
||||
|
||||
// if ((fromLeft && transX <= fadeThreshold) || (!fromLeft && absTrans <= fadeThreshold)) {
|
||||
if (absTrans <= fadeThreshold) {
|
||||
desiredAlpha = 1;
|
||||
} else {
|
||||
desiredAlpha = 1 - ((absTrans - fadeThreshold) / (size - fadeThreshold));
|
||||
}
|
||||
setGearAlpha(desiredAlpha);
|
||||
}
|
||||
|
||||
public void fadeInSettings(final boolean fromLeft, final float transX,
|
||||
final float notiThreshold) {
|
||||
setIconLocation(transX > 0 /* fromLeft */);
|
||||
mFadeAnimator = ValueAnimator.ofFloat(mGearIcon.getAlpha(), 1);
|
||||
mFadeAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
final float absTrans = Math.abs(transX);
|
||||
|
||||
boolean pastGear = (fromLeft && transX <= notiThreshold)
|
||||
|| (!fromLeft && absTrans <= notiThreshold);
|
||||
if (pastGear && !mSettingsFadedIn) {
|
||||
setGearAlpha((float) animation.getAnimatedValue());
|
||||
}
|
||||
}
|
||||
});
|
||||
mFadeAnimator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
super.onAnimationCancel(animation);
|
||||
mAnimating = false;
|
||||
mSettingsFadedIn = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
super.onAnimationStart(animation);
|
||||
mAnimating = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
super.onAnimationEnd(animation);
|
||||
mAnimating = false;
|
||||
mSettingsFadedIn = true;
|
||||
}
|
||||
});
|
||||
mFadeAnimator.setInterpolator(Interpolators.ALPHA_IN);
|
||||
mFadeAnimator.setDuration(200);
|
||||
mFadeAnimator.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
mListener.onGearTouched(mParent);
|
||||
}
|
||||
|
||||
private void setIconLocation(boolean onLeft) {
|
||||
if (onLeft == mOnLeft) {
|
||||
// Same side? Do nothing.
|
||||
return;
|
||||
}
|
||||
mGearIcon.setTranslationX(onLeft ? 0 : (getWidth() - mHorizSpaceForGear));
|
||||
mOnLeft = onLeft;
|
||||
}
|
||||
}
|
||||
@@ -746,6 +746,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
|
||||
mStackScroller = (NotificationStackScrollLayout) mStatusBarWindow.findViewById(
|
||||
R.id.notification_stack_scroller);
|
||||
mStackScroller.setLongPressListener(getNotificationLongClicker());
|
||||
mStackScroller.setGearDisplayedListener(getGearDisplayedListener());
|
||||
mStackScroller.setPhoneStatusBar(this);
|
||||
mStackScroller.setGroupManager(mGroupManager);
|
||||
mStackScroller.setHeadsUpManager(mHeadsUpManager);
|
||||
|
||||
@@ -18,10 +18,12 @@ package com.android.systemui.statusbar.stack;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.PropertyValuesHolder;
|
||||
import android.animation.TimeAnimator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.animation.ValueAnimator.AnimatorUpdateListener;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
@@ -32,6 +34,7 @@ import android.graphics.PointF;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffXfermode;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Handler;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
@@ -56,6 +59,8 @@ import com.android.systemui.statusbar.EmptyShadeView;
|
||||
import com.android.systemui.statusbar.ExpandableNotificationRow;
|
||||
import com.android.systemui.statusbar.ExpandableView;
|
||||
import com.android.systemui.statusbar.NotificationOverflowContainer;
|
||||
import com.android.systemui.statusbar.NotificationSettingsIconRow;
|
||||
import com.android.systemui.statusbar.NotificationSettingsIconRow.SettingsIconRowListener;
|
||||
import com.android.systemui.statusbar.StackScrollerDecorView;
|
||||
import com.android.systemui.statusbar.StatusBarState;
|
||||
import com.android.systemui.statusbar.phone.NotificationGroupManager;
|
||||
@@ -72,7 +77,8 @@ import java.util.HashSet;
|
||||
*/
|
||||
public class NotificationStackScrollLayout extends ViewGroup
|
||||
implements SwipeHelper.Callback, ExpandHelper.Callback, ScrollAdapter,
|
||||
ExpandableView.OnHeightChangedListener, NotificationGroupManager.OnGroupChangeListener {
|
||||
ExpandableView.OnHeightChangedListener, NotificationGroupManager.OnGroupChangeListener,
|
||||
SettingsIconRowListener {
|
||||
|
||||
public static final float BACKGROUND_ALPHA_DIMMED = 0.7f;
|
||||
private static final String TAG = "StackScroller";
|
||||
@@ -207,6 +213,11 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
*/
|
||||
private int mMaxScrollAfterExpand;
|
||||
private SwipeHelper.LongPressListener mLongPressListener;
|
||||
private GearDisplayedListener mGearDisplayedListener;
|
||||
|
||||
private NotificationSettingsIconRow mCurrIconRow;
|
||||
private View mTranslatingParentView;
|
||||
private View mGearExposedView;
|
||||
|
||||
/**
|
||||
* Should in this touch motion only be scrolling allowed? It's true when the scroller was
|
||||
@@ -304,8 +315,7 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
minHeight, maxHeight);
|
||||
mExpandHelper.setEventSource(this);
|
||||
mExpandHelper.setScrollAdapter(this);
|
||||
|
||||
mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, getContext());
|
||||
mSwipeHelper = new NotificationSwipeHelper(SwipeHelper.X, this, getContext());
|
||||
mSwipeHelper.setLongPressListener(mLongPressListener);
|
||||
mStackScrollAlgorithm = new StackScrollAlgorithm(context);
|
||||
initView(context);
|
||||
@@ -320,6 +330,13 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
mBackgroundPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGearTouched(ExpandableNotificationRow row) {
|
||||
if (mLongPressListener != null) {
|
||||
mLongPressListener.onLongPress(row, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
canvas.drawRect(0, mCurrentBounds.top, getWidth(), mCurrentBounds.bottom, mBackgroundPaint);
|
||||
@@ -630,6 +647,10 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
mLongPressListener = listener;
|
||||
}
|
||||
|
||||
public void setGearDisplayedListener(GearDisplayedListener listener) {
|
||||
mGearDisplayedListener = listener;
|
||||
}
|
||||
|
||||
public void setQsContainer(ViewGroup qsContainer) {
|
||||
mQsContainer = qsContainer;
|
||||
}
|
||||
@@ -666,7 +687,7 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChildSnappedBack(View animView) {
|
||||
public void onChildSnappedBack(View animView, float targetLeft) {
|
||||
mAmbientState.onDragFinished(animView);
|
||||
if (!mDragAnimPendingChildren.contains(animView)) {
|
||||
if (mAnimationsEnabled) {
|
||||
@@ -678,6 +699,13 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
// We start the swipe and snap back in the same frame, we don't want any animation
|
||||
mDragAnimPendingChildren.remove(animView);
|
||||
}
|
||||
|
||||
if (targetLeft == 0 && mCurrIconRow != null) {
|
||||
mCurrIconRow.resetState();
|
||||
if (mGearExposedView != null && mGearExposedView == mTranslatingParentView) {
|
||||
mGearExposedView = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -686,7 +714,7 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
mScrimController.setTopHeadsUpDragAmount(animView,
|
||||
Math.min(Math.abs(swipeProgress - 1.0f), 1.0f));
|
||||
}
|
||||
return false;
|
||||
return true; // Don't fade out the notification
|
||||
}
|
||||
|
||||
public void onBeginDrag(View v) {
|
||||
@@ -726,8 +754,21 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
return mPhoneStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getChildAtPosition(MotionEvent ev) {
|
||||
return getChildAtPosition(ev.getX(), ev.getY());
|
||||
View child = getChildAtPosition(ev.getX(), ev.getY());
|
||||
if (child instanceof ExpandableNotificationRow) {
|
||||
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
|
||||
ExpandableNotificationRow parent = row.getNotificationParent();
|
||||
if (mGearExposedView != null && parent != null
|
||||
&& parent.areChildrenExpanded() && mGearExposedView == parent) {
|
||||
// In this case the group is expanded and showing the gear for the
|
||||
// group, further interaction should apply to the group, not any
|
||||
// child notifications so we use the parent of the child.
|
||||
child = row.getNotificationParent();
|
||||
}
|
||||
}
|
||||
return child;
|
||||
}
|
||||
|
||||
public ExpandableView getClosestChildAtRawPosition(float touchX, float touchY) {
|
||||
@@ -841,10 +882,6 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
return mScrollingEnabled;
|
||||
}
|
||||
|
||||
public View getChildContentView(View v) {
|
||||
return v;
|
||||
}
|
||||
|
||||
public boolean canChildBeDismissed(View v) {
|
||||
return StackScrollAlgorithm.canChildBeDismissed(v);
|
||||
}
|
||||
@@ -3008,6 +3045,9 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
disableClipOptimization();
|
||||
}
|
||||
handleDismissAllClipping();
|
||||
if (mCurrIconRow != null & mCurrIconRow.isVisible()) {
|
||||
mCurrIconRow.getNotificationParent().animateTranslateNotification(0 /* left target */);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleDismissAllClipping() {
|
||||
@@ -3268,6 +3308,247 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
public void flingTopOverscroll(float velocity, boolean open);
|
||||
}
|
||||
|
||||
/**
|
||||
* A listener that is notified when the gear is shown behind a notification.
|
||||
*/
|
||||
public interface GearDisplayedListener {
|
||||
void onGearDisplayed(ExpandableNotificationRow row);
|
||||
}
|
||||
|
||||
private class NotificationSwipeHelper extends SwipeHelper {
|
||||
private static final int MOVE_STATE_LEFT = -1;
|
||||
private static final int MOVE_STATE_UNDEFINED = 0;
|
||||
private static final int MOVE_STATE_RIGHT = 1;
|
||||
|
||||
private static final long GEAR_SHOW_DELAY = 60;
|
||||
|
||||
private ArrayList<View> mTranslatingViews = new ArrayList<>();
|
||||
private CheckForDrag mCheckForDrag;
|
||||
private Handler mHandler;
|
||||
private int mMoveState = MOVE_STATE_UNDEFINED;
|
||||
|
||||
public NotificationSwipeHelper(int swipeDirection, Callback callback, Context context) {
|
||||
super(swipeDirection, callback, context);
|
||||
mHandler = new Handler();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDownUpdate(View currView) {
|
||||
// Set the active view
|
||||
mTranslatingParentView = currView;
|
||||
|
||||
// Reset check for drag gesture
|
||||
mCheckForDrag = null;
|
||||
|
||||
// Slide back any notifications that might be showing a gear
|
||||
resetExposedGearView();
|
||||
|
||||
if (currView instanceof ExpandableNotificationRow) {
|
||||
// Set the listener for the current row's gear
|
||||
mCurrIconRow = ((ExpandableNotificationRow) currView).getSettingsRow();
|
||||
mCurrIconRow.setGearListener(NotificationStackScrollLayout.this);
|
||||
|
||||
// And the translating children
|
||||
mTranslatingViews = ((ExpandableNotificationRow) currView).getContentViews();
|
||||
}
|
||||
mMoveState = MOVE_STATE_UNDEFINED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMoveUpdate(View view, float translation, float delta) {
|
||||
final int newMoveState = (delta < 0) ? MOVE_STATE_RIGHT : MOVE_STATE_LEFT;
|
||||
if (mMoveState != MOVE_STATE_UNDEFINED && mMoveState != newMoveState) {
|
||||
// Changed directions, make sure we check for drag again.
|
||||
mCheckForDrag = null;
|
||||
}
|
||||
mMoveState = newMoveState;
|
||||
|
||||
if (view instanceof ExpandableNotificationRow) {
|
||||
((ExpandableNotificationRow) view).setTranslationForOutline(translation);
|
||||
if (!isPinnedHeadsUp(view)) {
|
||||
// Only show the gear if we're not a heads up view.
|
||||
checkForDrag();
|
||||
if (mCurrIconRow != null) {
|
||||
mCurrIconRow.updateSettingsIcons(translation, getSize(view));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dismissChild(final View view, float velocity) {
|
||||
cancelCheckForDrag();
|
||||
super.dismissChild(view, velocity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void snapChild(final View animView, final float targetLeft, float velocity) {
|
||||
final float snapBackThreshold = getSpaceForGear(animView);
|
||||
final float translation = getTranslation(animView);
|
||||
final boolean fromLeft = translation > 0;
|
||||
final float absTrans = Math.abs(translation);
|
||||
final float notiThreshold = getSize(mTranslatingParentView) * 0.4f;
|
||||
|
||||
boolean pastGear = (fromLeft && translation >= snapBackThreshold * 0.4f
|
||||
&& translation <= notiThreshold) ||
|
||||
(!fromLeft && absTrans >= snapBackThreshold * 0.4f
|
||||
&& absTrans <= notiThreshold);
|
||||
|
||||
if (pastGear && !isPinnedHeadsUp(animView)) {
|
||||
// bouncity
|
||||
final float target = fromLeft ? snapBackThreshold : -snapBackThreshold;
|
||||
mGearExposedView = mTranslatingParentView;
|
||||
if (mGearDisplayedListener != null
|
||||
&& (animView instanceof ExpandableNotificationRow)) {
|
||||
mGearDisplayedListener.onGearDisplayed((ExpandableNotificationRow) animView);
|
||||
}
|
||||
super.snapChild(animView, target, velocity);
|
||||
} else {
|
||||
super.snapChild(animView, 0, velocity);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTranslationUpdate(View animView, float value, boolean canBeDismissed) {
|
||||
if (mDismissAllInProgress) {
|
||||
// When dismissing all, we translate the entire view instead.
|
||||
super.onTranslationUpdate(animView, value, canBeDismissed);
|
||||
return;
|
||||
}
|
||||
if (animView instanceof ExpandableNotificationRow) {
|
||||
((ExpandableNotificationRow) animView).setTranslationForOutline(value);
|
||||
}
|
||||
if (mCurrIconRow != null) {
|
||||
mCurrIconRow.updateSettingsIcons(value, getSize(animView));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Animator getViewTranslationAnimator(View v, float target,
|
||||
AnimatorUpdateListener listener) {
|
||||
if (mDismissAllInProgress) {
|
||||
// When dismissing all, we translate the entire view instead.
|
||||
return super.getViewTranslationAnimator(v, target, listener);
|
||||
}
|
||||
ArrayList<Animator> animators = new ArrayList<Animator>();
|
||||
for (int i = 0; i < mTranslatingViews.size(); i++) {
|
||||
ObjectAnimator anim = createTranslationAnimation(mTranslatingViews.get(i), target);
|
||||
animators.add(anim);
|
||||
if (i == 0 && listener != null) {
|
||||
anim.addUpdateListener(listener);
|
||||
}
|
||||
}
|
||||
AnimatorSet set = new AnimatorSet();
|
||||
set.playTogether(animators);
|
||||
return set;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTranslation(View v, float translate) {
|
||||
if (mDismissAllInProgress) {
|
||||
// When dismissing all, we translate the entire view instead.
|
||||
super.setTranslation(v, translate);
|
||||
return;
|
||||
}
|
||||
// Translate the group of views
|
||||
for (int i = 0; i < mTranslatingViews.size(); i++) {
|
||||
if (mTranslatingViews.get(i) != null) {
|
||||
super.setTranslation(mTranslatingViews.get(i), translate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getTranslation(View v) {
|
||||
if (mDismissAllInProgress) {
|
||||
// When dismissing all, we translate the entire view instead.
|
||||
return super.getTranslation(v);
|
||||
}
|
||||
// All of the views in the list should have same translation, just use first one.
|
||||
if (mTranslatingViews.size() > 0) {
|
||||
return super.getTranslation(mTranslatingViews.get(0));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the horizontal space in pixels required to display the gear behind a
|
||||
* notification.
|
||||
*/
|
||||
private float getSpaceForGear(View view) {
|
||||
if (view instanceof ExpandableNotificationRow) {
|
||||
return ((ExpandableNotificationRow) view).getSpaceForGear();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private void checkForDrag() {
|
||||
if (mCheckForDrag == null) {
|
||||
mCheckForDrag = new CheckForDrag();
|
||||
mHandler.postDelayed(mCheckForDrag, GEAR_SHOW_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
private void cancelCheckForDrag() {
|
||||
if (mCurrIconRow != null) {
|
||||
mCurrIconRow.cancelFadeAnimator();
|
||||
}
|
||||
mHandler.removeCallbacks(mCheckForDrag);
|
||||
mCheckForDrag = null;
|
||||
}
|
||||
|
||||
private final class CheckForDrag implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
final float translation = getTranslation(mTranslatingParentView);
|
||||
final float absTransX = Math.abs(translation);
|
||||
final float bounceBackToGearWidth = getSpaceForGear(mTranslatingParentView);
|
||||
final float notiThreshold = getSize(mTranslatingParentView) * 0.4f;
|
||||
if (mCurrIconRow != null && absTransX >= bounceBackToGearWidth * 0.4
|
||||
&& absTransX < notiThreshold) {
|
||||
// Show icon
|
||||
mCurrIconRow.fadeInSettings(translation > 0 /* fromLeft */, translation,
|
||||
notiThreshold);
|
||||
} else {
|
||||
// Allow more to be posted if this wasn't a drag.
|
||||
mCheckForDrag = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void resetExposedGearView() {
|
||||
if (mGearExposedView == null || mGearExposedView == mTranslatingParentView) {
|
||||
// If no gear is showing or it's showing for this view we do nothing.
|
||||
return;
|
||||
}
|
||||
|
||||
final View prevGearExposedView = mGearExposedView;
|
||||
mGearExposedView = null;
|
||||
|
||||
AnimatorListenerAdapter listener = new AnimatorListenerAdapter() {
|
||||
public void onAnimationEnd(Animator animator) {
|
||||
if (prevGearExposedView instanceof ExpandableNotificationRow) {
|
||||
((ExpandableNotificationRow) prevGearExposedView).getSettingsRow()
|
||||
.resetState();
|
||||
}
|
||||
}
|
||||
};
|
||||
AnimatorUpdateListener updateListener = new AnimatorUpdateListener() {
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
if (prevGearExposedView instanceof ExpandableNotificationRow) {
|
||||
((ExpandableNotificationRow) prevGearExposedView)
|
||||
.setTranslationForOutline((float) animation.getAnimatedValue());
|
||||
}
|
||||
}
|
||||
};
|
||||
Animator set = getViewTranslationAnimator(prevGearExposedView, 0, updateListener);
|
||||
set.addListener(listener);
|
||||
set.start();
|
||||
}
|
||||
}
|
||||
|
||||
static class AnimationEvent {
|
||||
|
||||
static AnimationFilter[] FILTERS = new AnimationFilter[] {
|
||||
|
||||
Reference in New Issue
Block a user