PeopleHub swipe-to-dismiss

Test: manual
Change-Id: I9ee003d78f048574e5b9b5b4f297104028eb87a5
This commit is contained in:
Steve Elliott
2019-10-28 11:20:24 -04:00
parent 0cf1081834
commit 946b00c4eb
9 changed files with 183 additions and 62 deletions

View File

@@ -159,6 +159,8 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
private int mHeadsUpAddStartLocation;
private float mHeadsUpLocation;
private boolean mIsAppearing;
private boolean mDismissed;
private boolean mRefocusOnDismiss;
public ActivatableNotificationView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -1048,6 +1050,27 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
return getHeight();
}
/** Mark that this view has been dismissed. */
public void dismiss(boolean refocusOnDismiss) {
mDismissed = true;
mRefocusOnDismiss = refocusOnDismiss;
}
/** Mark that this view is no longer dismissed. */
public void unDismiss() {
mDismissed = false;
}
/** Is this view marked as dismissed? */
public boolean isDismissed() {
return mDismissed;
}
/** Should a re-focus occur upon dismissing this view? */
public boolean shouldRefocusOnDismiss() {
return mRefocusOnDismiss || isAccessibilityFocused();
}
public interface OnActivatedListener {
void onActivated(ActivatableNotificationView view);
void onActivationReset(ActivatableNotificationView view);

View File

@@ -95,6 +95,7 @@ import com.android.systemui.statusbar.notification.stack.AnimationProperties;
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.SwipeableView;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -114,7 +115,7 @@ import java.util.function.Consumer;
* the group summary (which contains 1 or more child notifications).
*/
public class ExpandableNotificationRow extends ActivatableNotificationView
implements PluginListener<NotificationMenuRowPlugin> {
implements PluginListener<NotificationMenuRowPlugin>, SwipeableView {
private static final boolean DEBUG = false;
private static final int DEFAULT_DIVIDER_ALPHA = 0x29;
@@ -288,7 +289,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
};
private boolean mForceUnlocked;
private boolean mDismissed;
private boolean mKeepInParent;
private boolean mRemoved;
private static final Property<ExpandableNotificationRow, Float> TRANSLATE_CONTENT =
@@ -307,7 +307,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
private boolean mHeadsupDisappearRunning;
private View mChildAfterViewWhenDismissed;
private View mGroupParentWhenDismissed;
private boolean mRefocusOnDismiss;
private float mContentTransformationAmount;
private boolean mIconsVisible = true;
private boolean mAboveShelf;
@@ -1164,6 +1163,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
}
@Override
public boolean hasFinishedInitialization() {
return getEntry().hasFinishedInitialization();
}
/**
* Get a handle to a NotificationMenuRowPlugin whose menu view has been added to our hierarchy,
* or null if there is no menu row
@@ -1323,11 +1327,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
}
public void setDismissed(boolean fromAccessibility) {
@Override
public void dismiss(boolean refocusOnDismiss) {
super.dismiss(refocusOnDismiss);
setLongPressListener(null);
mDismissed = true;
mGroupParentWhenDismissed = mNotificationParent;
mRefocusOnDismiss = fromAccessibility;
mChildAfterViewWhenDismissed = null;
mEntry.icon.setDismissed();
if (isChildInGroup()) {
@@ -1340,10 +1344,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
}
public boolean isDismissed() {
return mDismissed;
}
public boolean keepInParent() {
return mKeepInParent;
}
@@ -1445,7 +1445,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
groupSummary.getRow().performDismiss(fromAccessibility);
}
}
setDismissed(fromAccessibility);
dismiss(fromAccessibility);
if (mEntry.isClearable()) {
// TODO: beverlyt, log dismissal
// TODO: track dismiss sentiment
@@ -3021,10 +3021,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
return false;
}
public boolean shouldRefocusOnDismiss() {
return mRefocusOnDismiss || isAccessibilityFocused();
}
public interface OnExpandClickListener {
void onExpandClicked(NotificationEntry clickedEntry, boolean nowExpanded);
}

View File

@@ -23,7 +23,6 @@ import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.PendingIntent;
import android.content.Intent;
import android.provider.Settings;
import android.view.LayoutInflater;
@@ -310,6 +309,8 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
mParent.removeView(mPeopleHubView);
}
} else {
mPeopleHubView.unDismiss();
mPeopleHubView.resetTranslation();
if (!currentlyVisible) {
if (mPeopleHubView.getTransientContainer() != null) {
mPeopleHubView.getTransientContainer().removeTransientView(mPeopleHubView);
@@ -419,8 +420,9 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
}
}
private void handlePeopleHubClick(PendingIntent pendingIntent) {
mActivityStarter.startPendingIntentDismissingKeyguard(pendingIntent, null, mPeopleHubView);
void hidePeopleRow() {
mPeopleHubVisible = false;
updateSectionBoundaries();
}
/**

View File

@@ -4920,7 +4920,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
} else {
child.setMinClipTopAmount(0);
}
previousChildWillBeDismissed = StackScrollAlgorithm.canChildBeDismissed(child);
previousChildWillBeDismissed = canChildBeDismissed(child);
}
}
@@ -5523,7 +5523,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
performDismissAllAnimations(viewsToHide, closeShade, () -> {
for (ExpandableNotificationRow rowToRemove : viewsToRemove) {
if (StackScrollAlgorithm.canChildBeDismissed(rowToRemove)) {
if (canChildBeDismissed(rowToRemove)) {
if (selection == ROWS_ALL) {
// TODO: This is a listener method; we shouldn't be calling it. Can we just
// call performRemoveNotification as below?
@@ -5552,7 +5552,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
private boolean includeChildInDismissAll(
ExpandableNotificationRow row,
@SelectedRows int selection) {
return StackScrollAlgorithm.canChildBeDismissed(row) && matchesSelection(row, selection);
return canChildBeDismissed(row) && matchesSelection(row, selection);
}
/**
@@ -6223,7 +6223,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
*/
@Override
public void onChildDismissed(View view) {
ExpandableNotificationRow row = (ExpandableNotificationRow) view;
if (!(view instanceof ActivatableNotificationView)) {
return;
}
ActivatableNotificationView row = (ActivatableNotificationView) view;
if (!row.isDismissed()) {
handleChildViewDismissed(view);
}
@@ -6261,6 +6264,12 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
row.performDismissWithBlockingHelper(false /* fromAccessibility */);
}
if (view instanceof PeopleHubView) {
PeopleHubView row = (PeopleHubView) view;
row.dismiss(false);
mSectionsManager.hidePeopleRow();
}
if (!isBlockingHelperShown) {
mSwipedOutViews.add(view);
}
@@ -6349,9 +6358,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
return 0;
}
@Override
@Override
public boolean canChildBeDismissed(View v) {
return StackScrollAlgorithm.canChildBeDismissed(v);
return NotificationStackScrollLayout.canChildBeDismissed(v);
}
@Override
@@ -6361,6 +6370,23 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
}
};
private static boolean canChildBeDismissed(View v) {
if (v instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) v;
if (row.isBlockingHelperShowingAndTranslationFinished()) {
return true;
}
if (row.areGutsExposed() || !row.getEntry().hasFinishedInitialization()) {
return false;
}
return row.canViewBeDismissed();
}
if (v instanceof PeopleHubView) {
return true;
}
return false;
}
// ---------------------- DragDownHelper.OnDragDownListener ------------------------------------
@ShadeViewRefactor(RefactorComponent.INPUT)

View File

@@ -31,11 +31,10 @@ import com.android.systemui.SwipeHelper;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
class NotificationSwipeHelper extends SwipeHelper
implements NotificationSwipeActionHelper {
class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeActionHelper {
@VisibleForTesting
protected static final long COVER_MENU_DELAY = 4000;
private static final String TAG = "NotificationSwipeHelper";
@@ -58,12 +57,7 @@ class NotificationSwipeHelper extends SwipeHelper
super(swipeDirection, callback, context, falsingManager);
mMenuListener = menuListener;
mCallback = callback;
mFalsingCheck = new Runnable() {
@Override
public void run() {
resetExposedMenuView(true /* animate */, true /* force */);
}
};
mFalsingCheck = () -> resetExposedMenuView(true /* animate */, true /* force */);
}
public View getTranslatingParentView() {
@@ -126,14 +120,14 @@ class NotificationSwipeHelper extends SwipeHelper
// Slide back any notifications that might be showing a menu
resetExposedMenuView(true /* animate */, false /* force */);
if (currView instanceof ExpandableNotificationRow) {
initializeRow((ExpandableNotificationRow) currView);
if (currView instanceof SwipeableView) {
initializeRow((SwipeableView) currView);
}
}
@VisibleForTesting
protected void initializeRow(ExpandableNotificationRow row) {
if (row.getEntry().hasFinishedInitialization()) {
protected void initializeRow(SwipeableView row) {
if (row.hasFinishedInitialization()) {
mCurrMenuRow = row.createMenu();
if (mCurrMenuRow != null) {
mCurrMenuRow.setMenuClickListener(mMenuListener);
@@ -304,8 +298,8 @@ class NotificationSwipeHelper extends SwipeHelper
@Override
public Animator getViewTranslationAnimator(View v, float target,
ValueAnimator.AnimatorUpdateListener listener) {
if (v instanceof ExpandableNotificationRow) {
return ((ExpandableNotificationRow) v).getTranslateViewAnimator(target, listener);
if (v instanceof SwipeableView) {
return ((SwipeableView) v).getTranslateViewAnimator(target, listener);
} else {
return superGetViewTranslationAnimator(v, target, listener);
}
@@ -313,15 +307,15 @@ class NotificationSwipeHelper extends SwipeHelper
@Override
public void setTranslation(View v, float translate) {
if (v instanceof ExpandableNotificationRow) {
((ExpandableNotificationRow) v).setTranslation(translate);
if (v instanceof SwipeableView) {
((SwipeableView) v).setTranslation(translate);
}
}
@Override
public float getTranslation(View v) {
if (v instanceof ExpandableNotificationRow) {
return ((ExpandableNotificationRow) v).getTranslation();
if (v instanceof SwipeableView) {
return ((SwipeableView) v).getTranslation();
}
else {
return 0f;
@@ -410,8 +404,8 @@ class NotificationSwipeHelper extends SwipeHelper
if (anim != null) {
anim.start();
}
} else if (prevMenuExposedView instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) prevMenuExposedView;
} else if (prevMenuExposedView instanceof SwipeableView) {
SwipeableView row = (SwipeableView) prevMenuExposedView;
if (!row.isRemoved()) {
row.resetTranslation();
}

View File

@@ -16,23 +16,39 @@
package com.android.systemui.statusbar.notification.stack
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ObjectAnimator
import android.animation.ValueAnimator
import android.content.Context
import android.util.AttributeSet
import android.util.FloatProperty
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import com.android.systemui.R
import com.android.systemui.statusbar.notification.people.PersonViewModel
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin
import com.android.systemui.statusbar.notification.people.DataListener
import com.android.systemui.statusbar.notification.people.PersonViewModel
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView
private val TRANSLATE_CONTENT = object : FloatProperty<PeopleHubView>("translate") {
override fun setValue(view: PeopleHubView, value: Float) {
view.translation = value
}
override fun get(view: PeopleHubView) = view.translation
}
class PeopleHubView(context: Context, attrs: AttributeSet) :
ActivatableNotificationView(context, attrs) {
ActivatableNotificationView(context, attrs), SwipeableView {
private lateinit var contents: ViewGroup
private lateinit var personControllers: List<PersonDataListenerImpl>
private var translateAnim: ObjectAnimator? = null
val personViewAdapters: Sequence<DataListener<PersonViewModel?>>
get() = personControllers.asSequence()
@@ -49,6 +65,34 @@ class PeopleHubView(context: Context, attrs: AttributeSet) :
override fun getContentView(): View = contents
override fun hasFinishedInitialization(): Boolean = true
override fun createMenu(): NotificationMenuRowPlugin? = null
override fun getTranslateViewAnimator(
leftTarget: Float,
listener: ValueAnimator.AnimatorUpdateListener?
): Animator =
ObjectAnimator
.ofFloat(this, TRANSLATE_CONTENT, leftTarget)
.apply {
listener?.let { addUpdateListener(listener) }
addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(anim: Animator) {
translateAnim = null
}
})
}
.also {
translateAnim?.cancel()
translateAnim = it
}
override fun resetTranslation() {
translateAnim?.cancel()
translationX = 0f
}
private inner class PersonDataListenerImpl(val viewGroup: ViewGroup) :
DataListener<PersonViewModel?> {

View File

@@ -195,20 +195,6 @@ public class StackScrollAlgorithm {
}
}
public static boolean canChildBeDismissed(View v) {
if (!(v instanceof ExpandableNotificationRow)) {
return false;
}
ExpandableNotificationRow row = (ExpandableNotificationRow) v;
if (row.isBlockingHelperShowingAndTranslationFinished()) {
return true;
}
if (row.areGutsExposed() || !row.getEntry().hasFinishedInitialization()) {
return false;
}
return row.canViewBeDismissed();
}
/**
* Updates the dimmed, activated and hiding sensitive states of the children.
*/

View File

@@ -0,0 +1,50 @@
/*
* Copyright (C) 2019 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.notification.stack;
import android.animation.Animator;
import android.animation.ValueAnimator;
import androidx.annotation.Nullable;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
/** A View that is swipeable inside of the notification shade. */
public interface SwipeableView {
/** Has this view finished initializing? */
boolean hasFinishedInitialization();
/** Optionally creates a menu for this view. */
@Nullable NotificationMenuRowPlugin createMenu();
/** Animator for translating the view, simulating a swipe. */
Animator getTranslateViewAnimator(
float leftTarget, ValueAnimator.AnimatorUpdateListener listener);
/** Sets the translation amount for an in-progress swipe. */
void setTranslation(float translate);
/** Gets the current translation amount. */
float getTranslation();
/** Has this view been removed? */
boolean isRemoved();
/** Resets the swipe translation back to zero state. */
void resetTranslation();
}

View File

@@ -226,7 +226,7 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
any(NotificationMenuRowPlugin.MenuItem.class));
reset(listener);
mGroupRow.setDismissed(true);
mGroupRow.dismiss(true);
mGroupRow.doLongClickCallback(0,0);
verify(listener, times(0)).onLongPress(eq(mGroupRow), eq(0), eq(0),
any(NotificationMenuRowPlugin.MenuItem.class));