Merge "[Notif] Add Blocking helper to swipe" into pi-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
f431ded9dd
@@ -31,6 +31,7 @@ import com.android.systemui.classifier.FalsingManager;
|
|||||||
import com.android.systemui.keyguard.DismissCallbackRegistry;
|
import com.android.systemui.keyguard.DismissCallbackRegistry;
|
||||||
import com.android.systemui.qs.QSTileHost;
|
import com.android.systemui.qs.QSTileHost;
|
||||||
import com.android.systemui.statusbar.KeyguardIndicationController;
|
import com.android.systemui.statusbar.KeyguardIndicationController;
|
||||||
|
import com.android.systemui.statusbar.NotificationBlockingHelperManager;
|
||||||
import com.android.systemui.statusbar.NotificationEntryManager;
|
import com.android.systemui.statusbar.NotificationEntryManager;
|
||||||
import com.android.systemui.statusbar.NotificationGutsManager;
|
import com.android.systemui.statusbar.NotificationGutsManager;
|
||||||
import com.android.systemui.statusbar.NotificationListener;
|
import com.android.systemui.statusbar.NotificationListener;
|
||||||
@@ -130,6 +131,8 @@ public class SystemUIFactory {
|
|||||||
providers.put(NotificationGroupManager.class, NotificationGroupManager::new);
|
providers.put(NotificationGroupManager.class, NotificationGroupManager::new);
|
||||||
providers.put(NotificationMediaManager.class, () -> new NotificationMediaManager(context));
|
providers.put(NotificationMediaManager.class, () -> new NotificationMediaManager(context));
|
||||||
providers.put(NotificationGutsManager.class, () -> new NotificationGutsManager(context));
|
providers.put(NotificationGutsManager.class, () -> new NotificationGutsManager(context));
|
||||||
|
providers.put(NotificationBlockingHelperManager.class,
|
||||||
|
() -> new NotificationBlockingHelperManager(context));
|
||||||
providers.put(NotificationRemoteInputManager.class,
|
providers.put(NotificationRemoteInputManager.class,
|
||||||
() -> new NotificationRemoteInputManager(context));
|
() -> new NotificationRemoteInputManager(context));
|
||||||
providers.put(SmartReplyConstants.class,
|
providers.put(SmartReplyConstants.class,
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.android.systemui.statusbar;
|
package com.android.systemui.statusbar;
|
||||||
|
|
||||||
|
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
|
||||||
import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
|
import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
|
||||||
import static com.android.systemui.statusbar.notification.NotificationInflater.InflationCallback;
|
import static com.android.systemui.statusbar.notification.NotificationInflater.InflationCallback;
|
||||||
|
|
||||||
@@ -34,7 +35,6 @@ import android.graphics.drawable.ColorDrawable;
|
|||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.service.notification.NotificationListenerService;
|
|
||||||
import android.service.notification.StatusBarNotification;
|
import android.service.notification.StatusBarNotification;
|
||||||
import android.util.ArraySet;
|
import android.util.ArraySet;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
@@ -127,6 +127,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
|||||||
private boolean mHasUserChangedExpansion;
|
private boolean mHasUserChangedExpansion;
|
||||||
/** If {@link #mHasUserChangedExpansion}, has the user expanded this row */
|
/** If {@link #mHasUserChangedExpansion}, has the user expanded this row */
|
||||||
private boolean mUserExpanded;
|
private boolean mUserExpanded;
|
||||||
|
/** Whether the blocking helper is showing on this notification (even if dismissed) */
|
||||||
|
private boolean mIsBlockingHelperShowing;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Has this notification been expanded while it was pinned
|
* Has this notification been expanded while it was pinned
|
||||||
@@ -400,8 +402,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
|||||||
updateIconVisibilities();
|
updateIconVisibilities();
|
||||||
updateShelfIconColor();
|
updateShelfIconColor();
|
||||||
|
|
||||||
showBlockingHelper(mEntry.userSentiment ==
|
showBlockingHelperButton(mEntry.userSentiment == USER_SENTIMENT_NEGATIVE);
|
||||||
NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE);
|
|
||||||
updateRippleAllowed();
|
updateRippleAllowed();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -594,6 +595,13 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
|||||||
return mNotificationParent != null;
|
return mNotificationParent != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return whether this notification is the only child in the group summary
|
||||||
|
*/
|
||||||
|
public boolean isOnlyChildInGroup() {
|
||||||
|
return mGroupManager.isOnlyChildInGroup(getStatusBarNotification());
|
||||||
|
}
|
||||||
|
|
||||||
public ExpandableNotificationRow getNotificationParent() {
|
public ExpandableNotificationRow getNotificationParent() {
|
||||||
return mNotificationParent;
|
return mNotificationParent;
|
||||||
}
|
}
|
||||||
@@ -1150,11 +1158,31 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
|||||||
return mGroupParentWhenDismissed;
|
return mGroupParentWhenDismissed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dismisses the notification with the option of showing the blocking helper in-place if we have
|
||||||
|
* a negative user sentiment.
|
||||||
|
*
|
||||||
|
* @param fromAccessibility whether this dismiss is coming from an accessibility action
|
||||||
|
* @return whether a blocking helper is shown in this row
|
||||||
|
*/
|
||||||
|
public boolean performDismissWithBlockingHelper(boolean fromAccessibility) {
|
||||||
|
NotificationBlockingHelperManager manager =
|
||||||
|
Dependency.get(NotificationBlockingHelperManager.class);
|
||||||
|
boolean isBlockingHelperShown = manager.perhapsShowBlockingHelper(this, mMenuRow);
|
||||||
|
|
||||||
|
// Continue with dismiss since we don't want the blocking helper to be directly associated
|
||||||
|
// with a certain notification.
|
||||||
|
performDismiss(fromAccessibility);
|
||||||
|
return isBlockingHelperShown;
|
||||||
|
}
|
||||||
|
|
||||||
public void performDismiss(boolean fromAccessibility) {
|
public void performDismiss(boolean fromAccessibility) {
|
||||||
if (mGroupManager.isOnlyChildInGroup(getStatusBarNotification())) {
|
if (isOnlyChildInGroup()) {
|
||||||
ExpandableNotificationRow groupSummary =
|
ExpandableNotificationRow groupSummary =
|
||||||
mGroupManager.getLogicalGroupSummary(getStatusBarNotification());
|
mGroupManager.getLogicalGroupSummary(getStatusBarNotification());
|
||||||
if (groupSummary.isClearable()) {
|
if (groupSummary.isClearable()) {
|
||||||
|
// If this is the only child in the group, dismiss the group, but don't try to show
|
||||||
|
// the blocking helper affordance!
|
||||||
groupSummary.performDismiss(fromAccessibility);
|
groupSummary.performDismiss(fromAccessibility);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1166,6 +1194,14 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setBlockingHelperShowing(boolean isBlockingHelperShowing) {
|
||||||
|
mIsBlockingHelperShowing = isBlockingHelperShowing;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isBlockingHelperShowing() {
|
||||||
|
return mIsBlockingHelperShowing;
|
||||||
|
}
|
||||||
|
|
||||||
public void setOnDismissRunnable(Runnable onDismissRunnable) {
|
public void setOnDismissRunnable(Runnable onDismissRunnable) {
|
||||||
mOnDismissRunnable = onDismissRunnable;
|
mOnDismissRunnable = onDismissRunnable;
|
||||||
}
|
}
|
||||||
@@ -1390,7 +1426,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
|||||||
requestLayout();
|
requestLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void showBlockingHelper(boolean show) {
|
public void showBlockingHelperButton(boolean show) {
|
||||||
mHelperButton.setVisibility(show ? View.VISIBLE : View.GONE);
|
mHelperButton.setVisibility(show ? View.VISIBLE : View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1423,7 +1459,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
|||||||
mPrivateLayout = (NotificationContentView) findViewById(R.id.expanded);
|
mPrivateLayout = (NotificationContentView) findViewById(R.id.expanded);
|
||||||
mLayouts = new NotificationContentView[] {mPrivateLayout, mPublicLayout};
|
mLayouts = new NotificationContentView[] {mPrivateLayout, mPublicLayout};
|
||||||
|
|
||||||
final NotificationGutsManager gutsMan = Dependency.get(NotificationGutsManager.class);
|
|
||||||
mHelperButton = findViewById(R.id.helper);
|
mHelperButton = findViewById(R.id.helper);
|
||||||
mHelperButton.setOnClickListener(view -> {
|
mHelperButton.setOnClickListener(view -> {
|
||||||
doLongClickCallback();
|
doLongClickCallback();
|
||||||
@@ -2526,7 +2561,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
|||||||
}
|
}
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case AccessibilityNodeInfo.ACTION_DISMISS:
|
case AccessibilityNodeInfo.ACTION_DISMISS:
|
||||||
performDismiss(true /* fromAccessibility */);
|
performDismissWithBlockingHelper(true /* fromAccessibility */);
|
||||||
return true;
|
return true;
|
||||||
case AccessibilityNodeInfo.ACTION_COLLAPSE:
|
case AccessibilityNodeInfo.ACTION_COLLAPSE:
|
||||||
case AccessibilityNodeInfo.ACTION_EXPAND:
|
case AccessibilityNodeInfo.ACTION_EXPAND:
|
||||||
|
|||||||
@@ -0,0 +1,137 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2018 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.support.annotation.VisibleForTesting;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.android.systemui.Dependency;
|
||||||
|
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
|
||||||
|
|
||||||
|
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manager for the notification blocking helper - tracks and helps create the blocking helper
|
||||||
|
* affordance.
|
||||||
|
*/
|
||||||
|
public class NotificationBlockingHelperManager {
|
||||||
|
/** Enables debug logging and always makes the blocking helper show up after a dismiss. */
|
||||||
|
private static final boolean DEBUG = false;
|
||||||
|
private static final String TAG = "BlockingHelper";
|
||||||
|
|
||||||
|
private final Context mContext;
|
||||||
|
/** Row that the blocking helper will be shown in (via {@link NotificationGuts}. */
|
||||||
|
private ExpandableNotificationRow mBlockingHelperRow;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the notification shade/stack is expanded - used to determine blocking helper
|
||||||
|
* eligibility.
|
||||||
|
*/
|
||||||
|
private boolean mIsShadeExpanded;
|
||||||
|
|
||||||
|
public NotificationBlockingHelperManager(Context context) {
|
||||||
|
mContext = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Potentially shows the blocking helper, represented via the {@link NotificationInfo} menu
|
||||||
|
* item, in the current row if user sentiment is negative.
|
||||||
|
*
|
||||||
|
* @param row row to render the blocking helper in
|
||||||
|
* @param menuRow menu used to generate the {@link NotificationInfo} view that houses the
|
||||||
|
* blocking helper UI
|
||||||
|
* @return whether we're showing a blocking helper in the given notification row
|
||||||
|
*/
|
||||||
|
boolean perhapsShowBlockingHelper(
|
||||||
|
ExpandableNotificationRow row, NotificationMenuRowPlugin menuRow) {
|
||||||
|
int numChildren = row.getNumberOfNotificationChildren();
|
||||||
|
|
||||||
|
// We only show the blocking helper if:
|
||||||
|
// - The dismissed row is a valid group (>1 or 0 children) or the only child in the group
|
||||||
|
// - The notification shade is fully expanded (guarantees we're not touching a HUN).
|
||||||
|
// - User sentiment is negative
|
||||||
|
if (DEBUG
|
||||||
|
|| row.getEntry().userSentiment == USER_SENTIMENT_NEGATIVE
|
||||||
|
&& mIsShadeExpanded
|
||||||
|
&& (!row.isChildInGroup() || row.isOnlyChildInGroup())) {
|
||||||
|
// Dismiss any current blocking helper before continuing forward (only one can be shown
|
||||||
|
// at a given time).
|
||||||
|
dismissCurrentBlockingHelper();
|
||||||
|
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.d(TAG, "Manager.perhapsShowBlockingHelper: Showing new blocking helper");
|
||||||
|
}
|
||||||
|
NotificationGutsManager manager = Dependency.get(NotificationGutsManager.class);
|
||||||
|
|
||||||
|
// Enable blocking helper on the row before moving forward so everything in the guts is
|
||||||
|
// correctly prepped.
|
||||||
|
mBlockingHelperRow = row;
|
||||||
|
mBlockingHelperRow.setBlockingHelperShowing(true);
|
||||||
|
|
||||||
|
// We don't care about the touch origin (x, y) since we're opening guts without any
|
||||||
|
// explicit user interaction.
|
||||||
|
manager.openGuts(mBlockingHelperRow, 0, 0, menuRow.getLongpressMenuItem(mContext));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dismiss the currently showing blocking helper, if any, through a notification update.
|
||||||
|
*
|
||||||
|
* @return whether the blocking helper was dismissed
|
||||||
|
*/
|
||||||
|
boolean dismissCurrentBlockingHelper() {
|
||||||
|
if (!isBlockingHelperRowNull()) {
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.d(TAG, "Manager.dismissCurrentBlockingHelper: Dismissing current helper");
|
||||||
|
}
|
||||||
|
if (!mBlockingHelperRow.isBlockingHelperShowing()) {
|
||||||
|
Log.e(TAG, "Manager.dismissCurrentBlockingHelper: "
|
||||||
|
+ "Non-null row is not showing a blocking helper");
|
||||||
|
}
|
||||||
|
|
||||||
|
mBlockingHelperRow.setBlockingHelperShowing(false);
|
||||||
|
if (mBlockingHelperRow.isAttachedToWindow()) {
|
||||||
|
Dependency.get(NotificationEntryManager.class).updateNotifications();
|
||||||
|
}
|
||||||
|
mBlockingHelperRow = null;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the expansion status of the notification shade/stack.
|
||||||
|
*
|
||||||
|
* @param expandedHeight how much the shade is expanded ({code 0} indicating it's collapsed)
|
||||||
|
*/
|
||||||
|
public void setNotificationShadeExpanded(float expandedHeight) {
|
||||||
|
mIsShadeExpanded = expandedHeight > 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
boolean isBlockingHelperRowNull() {
|
||||||
|
return mBlockingHelperRow == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
void setBlockingHelperRowForTest(ExpandableNotificationRow blockingHelperRowForTest) {
|
||||||
|
mBlockingHelperRow = blockingHelperRowForTest;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -30,6 +30,7 @@ import android.view.ViewAnimationUtils;
|
|||||||
import android.view.accessibility.AccessibilityEvent;
|
import android.view.accessibility.AccessibilityEvent;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
|
|
||||||
|
import com.android.systemui.Dependency;
|
||||||
import com.android.systemui.Interpolators;
|
import com.android.systemui.Interpolators;
|
||||||
import com.android.systemui.R;
|
import com.android.systemui.R;
|
||||||
import com.android.systemui.statusbar.stack.StackStateAnimator;
|
import com.android.systemui.statusbar.stack.StackStateAnimator;
|
||||||
@@ -189,8 +190,12 @@ public class NotificationGuts extends FrameLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void openControls(
|
public void openControls(
|
||||||
int x, int y, boolean needsFalsingProtection, @Nullable Runnable onAnimationEnd) {
|
boolean shouldDoCircularReveal,
|
||||||
animateOpen(x, y, onAnimationEnd);
|
int x,
|
||||||
|
int y,
|
||||||
|
boolean needsFalsingProtection,
|
||||||
|
@Nullable Runnable onAnimationEnd) {
|
||||||
|
animateOpen(shouldDoCircularReveal, x, y, onAnimationEnd);
|
||||||
setExposed(true /* exposed */, needsFalsingProtection);
|
setExposed(true /* exposed */, needsFalsingProtection);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,7 +209,20 @@ public class NotificationGuts extends FrameLayout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes any exposed guts/views.
|
||||||
|
*
|
||||||
|
* @param x x coordinate to animate the close circular reveal with
|
||||||
|
* @param y y coordinate to animate the close circular reveal with
|
||||||
|
* @param save whether the state should be saved
|
||||||
|
* @param force whether the guts should be force-closed regardless of state.
|
||||||
|
*/
|
||||||
public void closeControls(int x, int y, boolean save, boolean force) {
|
public void closeControls(int x, int y, boolean save, boolean force) {
|
||||||
|
// First try to dismiss any blocking helper.
|
||||||
|
boolean wasBlockingHelperDismissed =
|
||||||
|
Dependency.get(NotificationBlockingHelperManager.class)
|
||||||
|
.dismissCurrentBlockingHelper();
|
||||||
|
|
||||||
if (getWindowToken() == null) {
|
if (getWindowToken() == null) {
|
||||||
if (mClosedListener != null) {
|
if (mClosedListener != null) {
|
||||||
mClosedListener.onGutsClosed(this);
|
mClosedListener.onGutsClosed(this);
|
||||||
@@ -212,8 +230,12 @@ public class NotificationGuts extends FrameLayout {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mGutsContent == null || !mGutsContent.handleCloseControls(save, force)) {
|
if (mGutsContent == null
|
||||||
animateClose(x, y);
|
|| !mGutsContent.handleCloseControls(save, force)
|
||||||
|
|| wasBlockingHelperDismissed) {
|
||||||
|
// We only want to do a circular reveal if we're not showing the blocking helper.
|
||||||
|
animateClose(x, y, !wasBlockingHelperDismissed /* shouldDoCircularReveal */);
|
||||||
|
|
||||||
setExposed(false, mNeedsFalsingProtection);
|
setExposed(false, mNeedsFalsingProtection);
|
||||||
if (mClosedListener != null) {
|
if (mClosedListener != null) {
|
||||||
mClosedListener.onGutsClosed(this);
|
mClosedListener.onGutsClosed(this);
|
||||||
@@ -221,47 +243,58 @@ public class NotificationGuts extends FrameLayout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void animateOpen(int x, int y, @Nullable Runnable onAnimationEnd) {
|
/** Animates in the guts view via either a fade or a circular reveal. */
|
||||||
final double horz = Math.max(getWidth() - x, x);
|
private void animateOpen(
|
||||||
final double vert = Math.max(getHeight() - y, y);
|
boolean shouldDoCircularReveal, int x, int y, @Nullable Runnable onAnimationEnd) {
|
||||||
final float r = (float) Math.hypot(horz, vert);
|
if (shouldDoCircularReveal) {
|
||||||
|
double horz = Math.max(getWidth() - x, x);
|
||||||
final Animator a
|
double vert = Math.max(getHeight() - y, y);
|
||||||
= ViewAnimationUtils.createCircularReveal(this, x, y, 0, r);
|
float r = (float) Math.hypot(horz, vert);
|
||||||
a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
|
// Circular reveal originating at (x, y)
|
||||||
a.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
|
Animator a = ViewAnimationUtils.createCircularReveal(this, x, y, 0, r);
|
||||||
a.addListener(new AnimatorListenerAdapter() {
|
a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
|
||||||
@Override
|
a.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
|
||||||
public void onAnimationEnd(Animator animation) {
|
a.addListener(new AnimateOpenListener(onAnimationEnd));
|
||||||
super.onAnimationEnd(animation);
|
a.start();
|
||||||
if (onAnimationEnd != null) {
|
} else {
|
||||||
onAnimationEnd.run();
|
// Fade in content
|
||||||
}
|
this.setAlpha(0f);
|
||||||
}
|
this.animate()
|
||||||
});
|
.alpha(1f)
|
||||||
a.start();
|
.setDuration(StackStateAnimator.ANIMATION_DURATION_BLOCKING_HELPER_FADE)
|
||||||
|
.setInterpolator(Interpolators.ALPHA_IN)
|
||||||
|
.setListener(new AnimateOpenListener(onAnimationEnd))
|
||||||
|
.start();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void animateClose(int x, int y) {
|
|
||||||
if (x == -1 || y == -1) {
|
/** Animates out the guts view via either a fade or a circular reveal. */
|
||||||
x = (getLeft() + getRight()) / 2;
|
private void animateClose(int x, int y, boolean shouldDoCircularReveal) {
|
||||||
y = (getTop() + getHeight() / 2);
|
if (shouldDoCircularReveal) {
|
||||||
}
|
// Circular reveal originating at (x, y)
|
||||||
final double horz = Math.max(getWidth() - x, x);
|
if (x == -1 || y == -1) {
|
||||||
final double vert = Math.max(getHeight() - y, y);
|
x = (getLeft() + getRight()) / 2;
|
||||||
final float r = (float) Math.hypot(horz, vert);
|
y = (getTop() + getHeight() / 2);
|
||||||
final Animator a = ViewAnimationUtils.createCircularReveal(this,
|
|
||||||
x, y, r, 0);
|
|
||||||
a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
|
|
||||||
a.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
|
|
||||||
a.addListener(new AnimatorListenerAdapter() {
|
|
||||||
@Override
|
|
||||||
public void onAnimationEnd(Animator animation) {
|
|
||||||
super.onAnimationEnd(animation);
|
|
||||||
setVisibility(View.GONE);
|
|
||||||
}
|
}
|
||||||
});
|
double horz = Math.max(getWidth() - x, x);
|
||||||
a.start();
|
double vert = Math.max(getHeight() - y, y);
|
||||||
|
float r = (float) Math.hypot(horz, vert);
|
||||||
|
Animator a = ViewAnimationUtils.createCircularReveal(this,
|
||||||
|
x, y, r, 0);
|
||||||
|
a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
|
||||||
|
a.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
|
||||||
|
a.addListener(new AnimateCloseListener(this /* view */));
|
||||||
|
a.start();
|
||||||
|
} else {
|
||||||
|
// Fade in the blocking helper.
|
||||||
|
this.animate()
|
||||||
|
.alpha(0f)
|
||||||
|
.setDuration(StackStateAnimator.ANIMATION_DURATION_BLOCKING_HELPER_FADE)
|
||||||
|
.setInterpolator(Interpolators.ALPHA_OUT)
|
||||||
|
.setListener(new AnimateCloseListener(this /* view */))
|
||||||
|
.start();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setActualHeight(int actualHeight) {
|
public void setActualHeight(int actualHeight) {
|
||||||
@@ -336,4 +369,36 @@ public class NotificationGuts extends FrameLayout {
|
|||||||
public boolean isLeavebehind() {
|
public boolean isLeavebehind() {
|
||||||
return mGutsContent != null && mGutsContent.isLeavebehind();
|
return mGutsContent != null && mGutsContent.isLeavebehind();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Listener for animations executed in {@link #animateOpen(boolean, int, int, Runnable)}. */
|
||||||
|
private static class AnimateOpenListener extends AnimatorListenerAdapter {
|
||||||
|
final Runnable mOnAnimationEnd;
|
||||||
|
|
||||||
|
private AnimateOpenListener(Runnable onAnimationEnd) {
|
||||||
|
mOnAnimationEnd = onAnimationEnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAnimationEnd(Animator animation) {
|
||||||
|
super.onAnimationEnd(animation);
|
||||||
|
if (mOnAnimationEnd != null) {
|
||||||
|
mOnAnimationEnd.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Listener for animations executed in {@link #animateClose(int, int, boolean)}. */
|
||||||
|
private static class AnimateCloseListener extends AnimatorListenerAdapter {
|
||||||
|
final View mView;
|
||||||
|
|
||||||
|
private AnimateCloseListener(View view) {
|
||||||
|
mView = view;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAnimationEnd(Animator animation) {
|
||||||
|
super.onAnimationEnd(animation);
|
||||||
|
mView.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
|
|||||||
import static android.service.notification.NotificationListenerService.Ranking
|
import static android.service.notification.NotificationListenerService.Ranking
|
||||||
.USER_SENTIMENT_NEGATIVE;
|
.USER_SENTIMENT_NEGATIVE;
|
||||||
|
|
||||||
import android.app.AppOpsManager;
|
|
||||||
import android.app.INotificationManager;
|
import android.app.INotificationManager;
|
||||||
import android.app.NotificationChannel;
|
import android.app.NotificationChannel;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@@ -34,6 +33,7 @@ import android.os.ServiceManager;
|
|||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.service.notification.StatusBarNotification;
|
import android.service.notification.StatusBarNotification;
|
||||||
|
import android.support.annotation.VisibleForTesting;
|
||||||
import android.util.ArraySet;
|
import android.util.ArraySet;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.HapticFeedbackConstants;
|
import android.view.HapticFeedbackConstants;
|
||||||
@@ -119,22 +119,6 @@ public class NotificationGutsManager implements Dumpable {
|
|||||||
bindGuts(row);
|
bindGuts(row);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void saveAndCloseNotificationMenu(
|
|
||||||
ExpandableNotificationRow row, NotificationGuts guts, View done) {
|
|
||||||
guts.resetFalsingCheck();
|
|
||||||
int[] rowLocation = new int[2];
|
|
||||||
int[] doneLocation = new int[2];
|
|
||||||
row.getLocationOnScreen(rowLocation);
|
|
||||||
done.getLocationOnScreen(doneLocation);
|
|
||||||
|
|
||||||
final int centerX = done.getWidth() / 2;
|
|
||||||
final int centerY = done.getHeight() / 2;
|
|
||||||
final int x = doneLocation[0] - rowLocation[0] + centerX;
|
|
||||||
final int y = doneLocation[1] - rowLocation[1] + centerY;
|
|
||||||
closeAndSaveGuts(false /* removeLeavebehind */, false /* force */,
|
|
||||||
true /* removeControls */, x, y, true /* resetMenu */);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends an intent to open the app settings for a particular package and optional
|
* Sends an intent to open the app settings for a particular package and optional
|
||||||
* channel.
|
* channel.
|
||||||
@@ -174,12 +158,12 @@ public class NotificationGutsManager implements Dumpable {
|
|||||||
|
|
||||||
private void bindGuts(final ExpandableNotificationRow row,
|
private void bindGuts(final ExpandableNotificationRow row,
|
||||||
NotificationMenuRowPlugin.MenuItem item) {
|
NotificationMenuRowPlugin.MenuItem item) {
|
||||||
|
StatusBarNotification sbn = row.getStatusBarNotification();
|
||||||
|
|
||||||
row.inflateGuts();
|
row.inflateGuts();
|
||||||
row.setGutsView(item);
|
row.setGutsView(item);
|
||||||
final StatusBarNotification sbn = row.getStatusBarNotification();
|
|
||||||
row.setTag(sbn.getPackageName());
|
row.setTag(sbn.getPackageName());
|
||||||
final NotificationGuts guts = row.getGuts();
|
row.getGuts().setClosedListener((NotificationGuts g) -> {
|
||||||
guts.setClosedListener((NotificationGuts g) -> {
|
|
||||||
if (!g.willBeRemoved() && !row.isRemoved()) {
|
if (!g.willBeRemoved() && !row.isRemoved()) {
|
||||||
mListContainer.onHeightChanged(
|
mListContainer.onHeightChanged(
|
||||||
row, !mPresenter.isPresenterFullyCollapsed() /* needsAnimation */);
|
row, !mPresenter.isPresenterFullyCollapsed() /* needsAnimation */);
|
||||||
@@ -197,87 +181,143 @@ public class NotificationGutsManager implements Dumpable {
|
|||||||
|
|
||||||
View gutsView = item.getGutsView();
|
View gutsView = item.getGutsView();
|
||||||
if (gutsView instanceof NotificationSnooze) {
|
if (gutsView instanceof NotificationSnooze) {
|
||||||
NotificationSnooze snoozeGuts = (NotificationSnooze) gutsView;
|
initializeSnoozeView(row, (NotificationSnooze) gutsView);
|
||||||
snoozeGuts.setSnoozeListener(mListContainer.getSwipeActionHelper());
|
} else if (gutsView instanceof AppOpsInfo) {
|
||||||
snoozeGuts.setStatusBarNotification(sbn);
|
initializeAppOpsInfo(row, (AppOpsInfo) gutsView);
|
||||||
snoozeGuts.setSnoozeOptions(row.getEntry().snoozeCriteria);
|
} else if (gutsView instanceof NotificationInfo) {
|
||||||
guts.setHeightChangedListener((NotificationGuts g) -> {
|
initializeNotificationInfo(row, (NotificationInfo) gutsView);
|
||||||
mListContainer.onHeightChanged(row, row.isShown() /* needsAnimation */);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (gutsView instanceof AppOpsInfo) {
|
/**
|
||||||
AppOpsInfo info = (AppOpsInfo) gutsView;
|
* Sets up the {@link NotificationSnooze} inside the notification row's guts.
|
||||||
final UserHandle userHandle = sbn.getUser();
|
*
|
||||||
PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
|
* @param row view to set up the guts for
|
||||||
userHandle.getIdentifier());
|
* @param notificationSnoozeView view to set up/bind within {@code row}
|
||||||
final AppOpsInfo.OnSettingsClickListener onSettingsClick = (View v,
|
*/
|
||||||
String pkg, int uid, ArraySet<Integer> ops) -> {
|
private void initializeSnoozeView(
|
||||||
mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_OPS_GUTS_SETTINGS);
|
final ExpandableNotificationRow row,
|
||||||
guts.resetFalsingCheck();
|
NotificationSnooze notificationSnoozeView) {
|
||||||
startAppOpsSettingsActivity(pkg, uid, ops, row);
|
NotificationGuts guts = row.getGuts();
|
||||||
};
|
StatusBarNotification sbn = row.getStatusBarNotification();
|
||||||
if (!row.getEntry().mActiveAppOps.isEmpty()) {
|
|
||||||
info.bindGuts(pmUser, onSettingsClick, sbn, row.getEntry().mActiveAppOps);
|
notificationSnoozeView.setSnoozeListener(mListContainer.getSwipeActionHelper());
|
||||||
}
|
notificationSnoozeView.setStatusBarNotification(sbn);
|
||||||
|
notificationSnoozeView.setSnoozeOptions(row.getEntry().snoozeCriteria);
|
||||||
|
guts.setHeightChangedListener((NotificationGuts g) -> {
|
||||||
|
mListContainer.onHeightChanged(row, row.isShown() /* needsAnimation */);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets up the {@link AppOpsInfo} inside the notification row's guts.
|
||||||
|
*
|
||||||
|
* @param row view to set up the guts for
|
||||||
|
* @param appOpsInfoView view to set up/bind within {@code row}
|
||||||
|
*/
|
||||||
|
private void initializeAppOpsInfo(
|
||||||
|
final ExpandableNotificationRow row,
|
||||||
|
AppOpsInfo appOpsInfoView) {
|
||||||
|
NotificationGuts guts = row.getGuts();
|
||||||
|
StatusBarNotification sbn = row.getStatusBarNotification();
|
||||||
|
UserHandle userHandle = sbn.getUser();
|
||||||
|
PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
|
||||||
|
userHandle.getIdentifier());
|
||||||
|
|
||||||
|
AppOpsInfo.OnSettingsClickListener onSettingsClick =
|
||||||
|
(View v, String pkg, int uid, ArraySet<Integer> ops) -> {
|
||||||
|
mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_OPS_GUTS_SETTINGS);
|
||||||
|
guts.resetFalsingCheck();
|
||||||
|
startAppOpsSettingsActivity(pkg, uid, ops, row);
|
||||||
|
};
|
||||||
|
if (!row.getEntry().mActiveAppOps.isEmpty()) {
|
||||||
|
appOpsInfoView.bindGuts(pmUser, onSettingsClick, sbn, row.getEntry().mActiveAppOps);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (gutsView instanceof NotificationInfo) {
|
/**
|
||||||
final UserHandle userHandle = sbn.getUser();
|
* Sets up the {@link NotificationInfo} inside the notification row's guts.
|
||||||
PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
|
*
|
||||||
userHandle.getIdentifier());
|
* @param row view to set up the guts for
|
||||||
final INotificationManager iNotificationManager = INotificationManager.Stub.asInterface(
|
* @param notificationInfoView view to set up/bind within {@code row}
|
||||||
ServiceManager.getService(Context.NOTIFICATION_SERVICE));
|
*/
|
||||||
final String pkg = sbn.getPackageName();
|
@VisibleForTesting
|
||||||
NotificationInfo info = (NotificationInfo) gutsView;
|
void initializeNotificationInfo(
|
||||||
// Settings link is only valid for notifications that specify a user, unless this is the
|
final ExpandableNotificationRow row,
|
||||||
// system user.
|
NotificationInfo notificationInfoView) {
|
||||||
NotificationInfo.OnSettingsClickListener onSettingsClick = null;
|
NotificationGuts guts = row.getGuts();
|
||||||
if (!userHandle.equals(UserHandle.ALL)
|
StatusBarNotification sbn = row.getStatusBarNotification();
|
||||||
|| mLockscreenUserManager.getCurrentUserId() == UserHandle.USER_SYSTEM) {
|
String packageName = sbn.getPackageName();
|
||||||
onSettingsClick = (View v, NotificationChannel channel, int appUid) -> {
|
// Settings link is only valid for notifications that specify a non-system user
|
||||||
mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_NOTE_INFO);
|
NotificationInfo.OnSettingsClickListener onSettingsClick = null;
|
||||||
|
UserHandle userHandle = sbn.getUser();
|
||||||
|
PackageManager pmUser = StatusBar.getPackageManagerForUser(
|
||||||
|
mContext, userHandle.getIdentifier());
|
||||||
|
INotificationManager iNotificationManager = INotificationManager.Stub.asInterface(
|
||||||
|
ServiceManager.getService(Context.NOTIFICATION_SERVICE));
|
||||||
|
final NotificationInfo.OnAppSettingsClickListener onAppSettingsClick =
|
||||||
|
(View v, Intent intent) -> {
|
||||||
|
mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_APP_NOTE_SETTINGS);
|
||||||
guts.resetFalsingCheck();
|
guts.resetFalsingCheck();
|
||||||
mOnSettingsClickListener.onClick(sbn.getKey());
|
mPresenter.startNotificationGutsIntent(intent, sbn.getUid(), row);
|
||||||
startAppNotificationSettingsActivity(pkg, appUid, channel, row);
|
|
||||||
};
|
};
|
||||||
}
|
boolean isForBlockingHelper = row.isBlockingHelperShowing();
|
||||||
final NotificationInfo.OnAppSettingsClickListener onAppSettingsClick = (View v,
|
|
||||||
Intent intent) -> {
|
|
||||||
mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_APP_NOTE_SETTINGS);
|
|
||||||
guts.resetFalsingCheck();
|
|
||||||
mPresenter.startNotificationGutsIntent(intent, sbn.getUid(), row);
|
|
||||||
};
|
|
||||||
final View.OnClickListener onDoneClick = (View v) -> {
|
|
||||||
saveAndCloseNotificationMenu(row, guts, v);
|
|
||||||
};
|
|
||||||
|
|
||||||
ArraySet<NotificationChannel> channels = new ArraySet<>();
|
if (!userHandle.equals(UserHandle.ALL)
|
||||||
channels.add(row.getEntry().channel);
|
|| mLockscreenUserManager.getCurrentUserId() == UserHandle.USER_SYSTEM) {
|
||||||
if (row.isSummaryWithChildren()) {
|
onSettingsClick = (View v, NotificationChannel channel, int appUid) -> {
|
||||||
// If this is a summary, then add in the children notification channels for the
|
mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_NOTE_INFO);
|
||||||
// same user and pkg.
|
guts.resetFalsingCheck();
|
||||||
final List<ExpandableNotificationRow> childrenRows = row.getNotificationChildren();
|
mOnSettingsClickListener.onClick(sbn.getKey());
|
||||||
final int numChildren = childrenRows.size();
|
startAppNotificationSettingsActivity(packageName, appUid, channel, row);
|
||||||
for (int i = 0; i < numChildren; i++) {
|
};
|
||||||
final ExpandableNotificationRow childRow = childrenRows.get(i);
|
}
|
||||||
final NotificationChannel childChannel = childRow.getEntry().channel;
|
|
||||||
final StatusBarNotification childSbn = childRow.getStatusBarNotification();
|
try {
|
||||||
if (childSbn.getUser().equals(userHandle) &&
|
notificationInfoView.bindNotification(
|
||||||
childSbn.getPackageName().equals(pkg)) {
|
pmUser,
|
||||||
channels.add(childChannel);
|
iNotificationManager,
|
||||||
}
|
packageName,
|
||||||
|
row.getEntry().channel,
|
||||||
|
getNumNotificationChannels(row, packageName, userHandle),
|
||||||
|
sbn,
|
||||||
|
mCheckSaveListener,
|
||||||
|
onSettingsClick,
|
||||||
|
onAppSettingsClick,
|
||||||
|
mNonBlockablePkgs,
|
||||||
|
isForBlockingHelper,
|
||||||
|
row.getEntry().userSentiment == USER_SENTIMENT_NEGATIVE);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Log.e(TAG, e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the number of channels covered by the notification row (including its children if
|
||||||
|
* it's a summary notification).
|
||||||
|
*/
|
||||||
|
private int getNumNotificationChannels(
|
||||||
|
ExpandableNotificationRow row, String packageName, UserHandle userHandle) {
|
||||||
|
ArraySet<NotificationChannel> channels = new ArraySet<>();
|
||||||
|
|
||||||
|
channels.add(row.getEntry().channel);
|
||||||
|
|
||||||
|
// If this is a summary, then add in the children notification channels for the
|
||||||
|
// same user and pkg.
|
||||||
|
if (row.isSummaryWithChildren()) {
|
||||||
|
final List<ExpandableNotificationRow> childrenRows = row.getNotificationChildren();
|
||||||
|
final int numChildren = childrenRows.size();
|
||||||
|
for (int i = 0; i < numChildren; i++) {
|
||||||
|
final ExpandableNotificationRow childRow = childrenRows.get(i);
|
||||||
|
final NotificationChannel childChannel = childRow.getEntry().channel;
|
||||||
|
final StatusBarNotification childSbn = childRow.getStatusBarNotification();
|
||||||
|
if (childSbn.getUser().equals(userHandle) &&
|
||||||
|
childSbn.getPackageName().equals(packageName)) {
|
||||||
|
channels.add(childChannel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try {
|
|
||||||
info.bindNotification(pmUser, iNotificationManager, pkg, row.getEntry().channel,
|
|
||||||
channels.size(), sbn, mCheckSaveListener, onSettingsClick,
|
|
||||||
onAppSettingsClick, mNonBlockablePkgs,
|
|
||||||
row.getEntry().userSentiment == USER_SENTIMENT_NEGATIVE);
|
|
||||||
} catch (RemoteException e) {
|
|
||||||
Log.e(TAG, e.toString());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return channels.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -312,37 +352,42 @@ public class NotificationGutsManager implements Dumpable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens guts on the given ExpandableNotificationRow |v|.
|
* Opens guts on the given ExpandableNotificationRow {@code view}. This handles opening guts for
|
||||||
|
* the normal half-swipe and long-press use cases via a circular reveal. When the blocking
|
||||||
|
* helper needs to be shown on the row, this will skip the circular reveal.
|
||||||
*
|
*
|
||||||
* @param v ExpandableNotificationRow to open guts on
|
* @param view ExpandableNotificationRow to open guts on
|
||||||
* @param x x coordinate of origin of circular reveal
|
* @param x x coordinate of origin of circular reveal
|
||||||
* @param y y coordinate of origin of circular reveal
|
* @param y y coordinate of origin of circular reveal
|
||||||
* @param item MenuItem the guts should display
|
* @param menuItem MenuItem the guts should display
|
||||||
* @return true if guts was opened
|
* @return true if guts was opened
|
||||||
*/
|
*/
|
||||||
public boolean openGuts(View v, int x, int y,
|
boolean openGuts(
|
||||||
NotificationMenuRowPlugin.MenuItem item) {
|
View view,
|
||||||
if (!(v instanceof ExpandableNotificationRow)) {
|
int x,
|
||||||
|
int y,
|
||||||
|
NotificationMenuRowPlugin.MenuItem menuItem) {
|
||||||
|
if (!(view instanceof ExpandableNotificationRow)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (v.getWindowToken() == null) {
|
if (view.getWindowToken() == null) {
|
||||||
Log.e(TAG, "Trying to show notification guts, but not attached to window");
|
Log.e(TAG, "Trying to show notification guts, but not attached to window");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
final ExpandableNotificationRow row = (ExpandableNotificationRow) v;
|
final ExpandableNotificationRow row = (ExpandableNotificationRow) view;
|
||||||
if (row.isDark()) {
|
if (row.isDark()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
v.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
|
view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
|
||||||
if (row.areGutsExposed()) {
|
if (row.areGutsExposed()) {
|
||||||
closeAndSaveGuts(false /* removeLeavebehind */, false /* force */,
|
closeAndSaveGuts(false /* removeLeavebehind */, false /* force */,
|
||||||
true /* removeControls */, -1 /* x */, -1 /* y */,
|
true /* removeControls */, -1 /* x */, -1 /* y */,
|
||||||
true /* resetMenu */);
|
true /* resetMenu */);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bindGuts(row, item);
|
bindGuts(row, menuItem);
|
||||||
NotificationGuts guts = row.getGuts();
|
NotificationGuts guts = row.getGuts();
|
||||||
|
|
||||||
// Assume we are a status_bar_notification_row
|
// Assume we are a status_bar_notification_row
|
||||||
@@ -372,15 +417,18 @@ public class NotificationGutsManager implements Dumpable {
|
|||||||
final boolean needsFalsingProtection =
|
final boolean needsFalsingProtection =
|
||||||
(mPresenter.isPresenterLocked() &&
|
(mPresenter.isPresenterLocked() &&
|
||||||
!mAccessibilityManager.isTouchExplorationEnabled());
|
!mAccessibilityManager.isTouchExplorationEnabled());
|
||||||
guts.openControls(x, y, needsFalsingProtection, () -> {
|
|
||||||
// Move the notification view back over the menu
|
guts.openControls(
|
||||||
row.resetTranslation();
|
!row.isBlockingHelperShowing(),
|
||||||
});
|
x,
|
||||||
|
y,
|
||||||
|
needsFalsingProtection,
|
||||||
|
row::resetTranslation);
|
||||||
|
|
||||||
row.closeRemoteInput();
|
row.closeRemoteInput();
|
||||||
mListContainer.onHeightChanged(row, true /* needsAnimation */);
|
mListContainer.onHeightChanged(row, true /* needsAnimation */);
|
||||||
mNotificationGutsExposed = guts;
|
mNotificationGutsExposed = guts;
|
||||||
mGutsMenuItem = item;
|
mGutsMenuItem = menuItem;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -46,9 +46,11 @@ import android.widget.ImageView;
|
|||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.internal.logging.MetricsLogger;
|
import com.android.internal.logging.MetricsLogger;
|
||||||
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
||||||
import com.android.settingslib.Utils;
|
import com.android.settingslib.Utils;
|
||||||
|
import com.android.systemui.Dependency;
|
||||||
import com.android.systemui.Interpolators;
|
import com.android.systemui.Interpolators;
|
||||||
import com.android.systemui.R;
|
import com.android.systemui.R;
|
||||||
|
|
||||||
@@ -81,11 +83,12 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
|
|||||||
private OnSettingsClickListener mOnSettingsClickListener;
|
private OnSettingsClickListener mOnSettingsClickListener;
|
||||||
private OnAppSettingsClickListener mAppSettingsClickListener;
|
private OnAppSettingsClickListener mAppSettingsClickListener;
|
||||||
private NotificationGuts mGutsContainer;
|
private NotificationGuts mGutsContainer;
|
||||||
|
|
||||||
|
/** Whether this view is being shown as part of the blocking helper */
|
||||||
|
private boolean mIsForBlockingHelper;
|
||||||
private boolean mNegativeUserSentiment;
|
private boolean mNegativeUserSentiment;
|
||||||
|
|
||||||
private OnClickListener mOnKeepShowing = v -> {
|
private OnClickListener mOnKeepShowing = this::closeControls;
|
||||||
closeControls(v);
|
|
||||||
};
|
|
||||||
|
|
||||||
private OnClickListener mOnStopMinNotifications = v -> {
|
private OnClickListener mOnStopMinNotifications = v -> {
|
||||||
swapContent(false);
|
swapContent(false);
|
||||||
@@ -114,7 +117,9 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
|
|||||||
void onClick(View v, Intent intent);
|
void onClick(View v, Intent intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void bindNotification(final PackageManager pm,
|
@VisibleForTesting
|
||||||
|
void bindNotification(
|
||||||
|
final PackageManager pm,
|
||||||
final INotificationManager iNotificationManager,
|
final INotificationManager iNotificationManager,
|
||||||
final String pkg,
|
final String pkg,
|
||||||
final NotificationChannel notificationChannel,
|
final NotificationChannel notificationChannel,
|
||||||
@@ -127,20 +132,24 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
|
|||||||
throws RemoteException {
|
throws RemoteException {
|
||||||
bindNotification(pm, iNotificationManager, pkg, notificationChannel, numChannels, sbn,
|
bindNotification(pm, iNotificationManager, pkg, notificationChannel, numChannels, sbn,
|
||||||
checkSaveListener, onSettingsClick, onAppSettingsClick, nonBlockablePkgs,
|
checkSaveListener, onSettingsClick, onAppSettingsClick, nonBlockablePkgs,
|
||||||
false /* negative sentiment */);
|
false /* isBlockingHelper */,
|
||||||
|
false /* isUserSentimentNegative */);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void bindNotification(final PackageManager pm,
|
public void bindNotification(
|
||||||
final INotificationManager iNotificationManager,
|
PackageManager pm,
|
||||||
final String pkg,
|
INotificationManager iNotificationManager,
|
||||||
final NotificationChannel notificationChannel,
|
String pkg,
|
||||||
final int numChannels,
|
NotificationChannel notificationChannel,
|
||||||
final StatusBarNotification sbn,
|
int numChannels,
|
||||||
final CheckSaveListener checkSaveListener,
|
StatusBarNotification sbn,
|
||||||
final OnSettingsClickListener onSettingsClick,
|
CheckSaveListener checkSaveListener,
|
||||||
final OnAppSettingsClickListener onAppSettingsClick,
|
OnSettingsClickListener onSettingsClick,
|
||||||
final Set<String> nonBlockablePkgs,
|
OnAppSettingsClickListener onAppSettingsClick,
|
||||||
boolean negativeUserSentiment) throws RemoteException {
|
Set<String> nonBlockablePkgs,
|
||||||
|
boolean isForBlockingHelper,
|
||||||
|
boolean isUserSentimentNegative)
|
||||||
|
throws RemoteException {
|
||||||
mINotificationManager = iNotificationManager;
|
mINotificationManager = iNotificationManager;
|
||||||
mPkg = pkg;
|
mPkg = pkg;
|
||||||
mNumNotificationChannels = numChannels;
|
mNumNotificationChannels = numChannels;
|
||||||
@@ -152,9 +161,10 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
|
|||||||
mOnSettingsClickListener = onSettingsClick;
|
mOnSettingsClickListener = onSettingsClick;
|
||||||
mSingleNotificationChannel = notificationChannel;
|
mSingleNotificationChannel = notificationChannel;
|
||||||
mStartingUserImportance = mChosenImportance = mSingleNotificationChannel.getImportance();
|
mStartingUserImportance = mChosenImportance = mSingleNotificationChannel.getImportance();
|
||||||
mNegativeUserSentiment = negativeUserSentiment;
|
mNegativeUserSentiment = isUserSentimentNegative;
|
||||||
mIsForeground =
|
mIsForeground =
|
||||||
(mSbn.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE) != 0;
|
(mSbn.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE) != 0;
|
||||||
|
mIsForBlockingHelper = isForBlockingHelper;
|
||||||
|
|
||||||
int numTotalChannels = mINotificationManager.getNumNotificationChannelsForPackage(
|
int numTotalChannels = mINotificationManager.getNumNotificationChannelsForPackage(
|
||||||
pkg, mAppUid, false /* includeDeleted */);
|
pkg, mAppUid, false /* includeDeleted */);
|
||||||
@@ -294,12 +304,14 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void bindButtons() {
|
private void bindButtons() {
|
||||||
|
// Set up stay-in-notification actions
|
||||||
View block = findViewById(R.id.block);
|
View block = findViewById(R.id.block);
|
||||||
block.setOnClickListener(mOnStopMinNotifications);
|
|
||||||
TextView keep = findViewById(R.id.keep);
|
TextView keep = findViewById(R.id.keep);
|
||||||
keep.setOnClickListener(mOnKeepShowing);
|
|
||||||
findViewById(R.id.undo).setOnClickListener(mOnUndo);
|
|
||||||
View minimize = findViewById(R.id.minimize);
|
View minimize = findViewById(R.id.minimize);
|
||||||
|
|
||||||
|
findViewById(R.id.undo).setOnClickListener(mOnUndo);
|
||||||
|
block.setOnClickListener(mOnStopMinNotifications);
|
||||||
|
keep.setOnClickListener(mOnKeepShowing);
|
||||||
minimize.setOnClickListener(mOnStopMinNotifications);
|
minimize.setOnClickListener(mOnStopMinNotifications);
|
||||||
|
|
||||||
if (mNonblockable) {
|
if (mNonblockable) {
|
||||||
@@ -314,7 +326,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
|
|||||||
minimize.setVisibility(GONE);
|
minimize.setVisibility(GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// app settings link
|
// Set up app settings link
|
||||||
TextView settingsLinkView = findViewById(R.id.app_settings);
|
TextView settingsLinkView = findViewById(R.id.app_settings);
|
||||||
Intent settingsIntent = getAppSettingsIntent(mPm, mPkg, mSingleNotificationChannel,
|
Intent settingsIntent = getAppSettingsIntent(mPm, mPkg, mSingleNotificationChannel,
|
||||||
mSbn.getId(), mSbn.getTag());
|
mSbn.getId(), mSbn.getTag());
|
||||||
@@ -421,16 +433,23 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
|
|||||||
return intent;
|
return intent;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void closeControls(View v) {
|
@VisibleForTesting
|
||||||
int[] parentLoc = new int[2];
|
void closeControls(View v) {
|
||||||
int[] targetLoc = new int[2];
|
if (mIsForBlockingHelper) {
|
||||||
mGutsContainer.getLocationOnScreen(parentLoc);
|
NotificationBlockingHelperManager manager =
|
||||||
v.getLocationOnScreen(targetLoc);
|
Dependency.get(NotificationBlockingHelperManager.class);
|
||||||
final int centerX = v.getWidth() / 2;
|
manager.dismissCurrentBlockingHelper();
|
||||||
final int centerY = v.getHeight() / 2;
|
} else {
|
||||||
final int x = targetLoc[0] - parentLoc[0] + centerX;
|
int[] parentLoc = new int[2];
|
||||||
final int y = targetLoc[1] - parentLoc[1] + centerY;
|
int[] targetLoc = new int[2];
|
||||||
mGutsContainer.closeControls(x, y, true /* save */, false /* force */);
|
mGutsContainer.getLocationOnScreen(parentLoc);
|
||||||
|
v.getLocationOnScreen(targetLoc);
|
||||||
|
final int centerX = v.getWidth() / 2;
|
||||||
|
final int centerY = v.getHeight() / 2;
|
||||||
|
final int x = targetLoc[0] - parentLoc[0] + centerX;
|
||||||
|
final int y = targetLoc[1] - parentLoc[1] + centerY;
|
||||||
|
mGutsContainer.closeControls(x, y, true /* save */, false /* force */);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -227,7 +227,6 @@ public class NotificationShelf extends ActivatableNotificationView implements
|
|||||||
expandAmount = Math.min(1.0f, expandAmount);
|
expandAmount = Math.min(1.0f, expandAmount);
|
||||||
}
|
}
|
||||||
// find the first view that doesn't overlap with the shelf
|
// find the first view that doesn't overlap with the shelf
|
||||||
int notificationIndex = 0;
|
|
||||||
int notGoneIndex = 0;
|
int notGoneIndex = 0;
|
||||||
int colorOfViewBeforeLast = NO_COLOR;
|
int colorOfViewBeforeLast = NO_COLOR;
|
||||||
boolean backgroundForceHidden = false;
|
boolean backgroundForceHidden = false;
|
||||||
@@ -247,13 +246,15 @@ public class NotificationShelf extends ActivatableNotificationView implements
|
|||||||
int baseZHeight = mAmbientState.getBaseZHeight();
|
int baseZHeight = mAmbientState.getBaseZHeight();
|
||||||
int backgroundTop = 0;
|
int backgroundTop = 0;
|
||||||
float firstElementRoundness = 0.0f;
|
float firstElementRoundness = 0.0f;
|
||||||
while (notificationIndex < mHostLayout.getChildCount()) {
|
|
||||||
ExpandableView child = (ExpandableView) mHostLayout.getChildAt(notificationIndex);
|
for (int i = 0; i < mHostLayout.getChildCount(); i++) {
|
||||||
notificationIndex++;
|
ExpandableView child = (ExpandableView) mHostLayout.getChildAt(i);
|
||||||
|
|
||||||
if (!(child instanceof ExpandableNotificationRow)
|
if (!(child instanceof ExpandableNotificationRow)
|
||||||
|| child.getVisibility() == GONE) {
|
|| child.getVisibility() == GONE) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
|
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
|
||||||
float notificationClipEnd;
|
float notificationClipEnd;
|
||||||
boolean aboveShelf = ViewState.getFinalTranslationZ(row) > baseZHeight
|
boolean aboveShelf = ViewState.getFinalTranslationZ(row) > baseZHeight
|
||||||
@@ -315,6 +316,9 @@ public class NotificationShelf extends ActivatableNotificationView implements
|
|||||||
notGoneIndex++;
|
notGoneIndex++;
|
||||||
previousColor = ownColorUntinted;
|
previousColor = ownColorUntinted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clipTransientViews();
|
||||||
|
|
||||||
setBackgroundTop(backgroundTop);
|
setBackgroundTop(backgroundTop);
|
||||||
setFirstElementRoundness(firstElementRoundness);
|
setFirstElementRoundness(firstElementRoundness);
|
||||||
mShelfIcons.setSpeedBumpIndex(mAmbientState.getSpeedBumpIndex());
|
mShelfIcons.setSpeedBumpIndex(mAmbientState.getSpeedBumpIndex());
|
||||||
@@ -337,6 +341,25 @@ public class NotificationShelf extends ActivatableNotificationView implements
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clips transient views to the top of the shelf - Transient views are only used for
|
||||||
|
* disappearing views/animations and need to be clipped correctly by the shelf to ensure they
|
||||||
|
* don't show underneath the notification stack when something is animating and the user
|
||||||
|
* swipes quickly.
|
||||||
|
*/
|
||||||
|
private void clipTransientViews() {
|
||||||
|
for (int i = 0; i < mHostLayout.getTransientViewCount(); i++) {
|
||||||
|
View transientView = mHostLayout.getTransientView(i);
|
||||||
|
if (transientView instanceof ExpandableNotificationRow) {
|
||||||
|
ExpandableNotificationRow transientRow = (ExpandableNotificationRow) transientView;
|
||||||
|
updateNotificationClipHeight(transientRow, getTranslationY());
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "NotificationShelf.clipTransientViews(): "
|
||||||
|
+ "Trying to clip non-row transient view");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void setFirstElementRoundness(float firstElementRoundness) {
|
private void setFirstElementRoundness(float firstElementRoundness) {
|
||||||
if (mFirstElementRoundness != firstElementRoundness) {
|
if (mFirstElementRoundness != firstElementRoundness) {
|
||||||
mFirstElementRoundness = firstElementRoundness;
|
mFirstElementRoundness = firstElementRoundness;
|
||||||
|
|||||||
@@ -125,24 +125,30 @@ public class NotificationViewHierarchyManager {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ArrayList<ExpandableNotificationRow> toRemove = new ArrayList<>();
|
ArrayList<ExpandableNotificationRow> viewsToRemove = new ArrayList<>();
|
||||||
for (int i=0; i< mListContainer.getContainerChildCount(); i++) {
|
for (int i=0; i< mListContainer.getContainerChildCount(); i++) {
|
||||||
View child = mListContainer.getContainerChildAt(i);
|
View child = mListContainer.getContainerChildAt(i);
|
||||||
if (!toShow.contains(child) && child instanceof ExpandableNotificationRow) {
|
if (!toShow.contains(child) && child instanceof ExpandableNotificationRow) {
|
||||||
toRemove.add((ExpandableNotificationRow) child);
|
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
|
||||||
|
|
||||||
|
// Blocking helper is effectively a detached view. Don't bother removing it from the
|
||||||
|
// layout.
|
||||||
|
if (!row.isBlockingHelperShowing()) {
|
||||||
|
viewsToRemove.add((ExpandableNotificationRow) child);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (ExpandableNotificationRow remove : toRemove) {
|
for (ExpandableNotificationRow viewToRemove : viewsToRemove) {
|
||||||
if (mGroupManager.isChildInGroupWithSummary(remove.getStatusBarNotification())) {
|
if (mGroupManager.isChildInGroupWithSummary(viewToRemove.getStatusBarNotification())) {
|
||||||
// we are only transferring this notification to its parent, don't generate an
|
// we are only transferring this notification to its parent, don't generate an
|
||||||
// animation
|
// animation
|
||||||
mListContainer.setChildTransferInProgress(true);
|
mListContainer.setChildTransferInProgress(true);
|
||||||
}
|
}
|
||||||
if (remove.isSummaryWithChildren()) {
|
if (viewToRemove.isSummaryWithChildren()) {
|
||||||
remove.removeAllChildren();
|
viewToRemove.removeAllChildren();
|
||||||
}
|
}
|
||||||
mListContainer.removeContainerView(remove);
|
mListContainer.removeContainerView(viewToRemove);
|
||||||
mListContainer.setChildTransferInProgress(false);
|
mListContainer.setChildTransferInProgress(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,6 +174,10 @@ public class NotificationViewHierarchyManager {
|
|||||||
// We don't care about non-notification views.
|
// We don't care about non-notification views.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (((ExpandableNotificationRow) child).isBlockingHelperShowing()) {
|
||||||
|
// Don't count/reorder notifications that are showing the blocking helper!
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
ExpandableNotificationRow targetChild = toShow.get(j);
|
ExpandableNotificationRow targetChild = toShow.get(j);
|
||||||
if (child != targetChild) {
|
if (child != targetChild) {
|
||||||
@@ -340,7 +350,7 @@ public class NotificationViewHierarchyManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
row.showBlockingHelper(entry.userSentiment ==
|
row.showBlockingHelperButton(entry.userSentiment ==
|
||||||
NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE);
|
NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE);
|
||||||
|
|
||||||
row.showAppOpsIcons(entry.mActiveAppOps);
|
row.showAppOpsIcons(entry.mActiveAppOps);
|
||||||
|
|||||||
@@ -169,7 +169,7 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener {
|
|||||||
if (group.suppressed) {
|
if (group.suppressed) {
|
||||||
handleSuppressedSummaryHeadsUpped(group.summary);
|
handleSuppressedSummaryHeadsUpped(group.summary);
|
||||||
}
|
}
|
||||||
if (!mIsUpdatingUnchangedGroup) {
|
if (!mIsUpdatingUnchangedGroup && mListener != null) {
|
||||||
mListener.onGroupsChanged();
|
mListener.onGroupsChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ import android.widget.ScrollView;
|
|||||||
import com.android.internal.logging.MetricsLogger;
|
import com.android.internal.logging.MetricsLogger;
|
||||||
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
||||||
import com.android.settingslib.Utils;
|
import com.android.settingslib.Utils;
|
||||||
|
import com.android.systemui.Dependency;
|
||||||
import com.android.systemui.ExpandHelper;
|
import com.android.systemui.ExpandHelper;
|
||||||
import com.android.systemui.Interpolators;
|
import com.android.systemui.Interpolators;
|
||||||
import com.android.systemui.R;
|
import com.android.systemui.R;
|
||||||
@@ -83,6 +84,7 @@ import com.android.systemui.statusbar.EmptyShadeView;
|
|||||||
import com.android.systemui.statusbar.ExpandableNotificationRow;
|
import com.android.systemui.statusbar.ExpandableNotificationRow;
|
||||||
import com.android.systemui.statusbar.ExpandableView;
|
import com.android.systemui.statusbar.ExpandableView;
|
||||||
import com.android.systemui.statusbar.FooterView;
|
import com.android.systemui.statusbar.FooterView;
|
||||||
|
import com.android.systemui.statusbar.NotificationBlockingHelperManager;
|
||||||
import com.android.systemui.statusbar.NotificationData;
|
import com.android.systemui.statusbar.NotificationData;
|
||||||
import com.android.systemui.statusbar.NotificationGuts;
|
import com.android.systemui.statusbar.NotificationGuts;
|
||||||
import com.android.systemui.statusbar.NotificationListContainer;
|
import com.android.systemui.statusbar.NotificationListContainer;
|
||||||
@@ -448,6 +450,13 @@ public class NotificationStackScrollLayout extends ViewGroup
|
|||||||
mRoundnessManager.setOnRoundingChangedCallback(this::invalidate);
|
mRoundnessManager.setOnRoundingChangedCallback(this::invalidate);
|
||||||
addOnExpandedHeightListener(mRoundnessManager::setExpanded);
|
addOnExpandedHeightListener(mRoundnessManager::setExpanded);
|
||||||
|
|
||||||
|
// Blocking helper manager wants to know the expanded state, update as well.
|
||||||
|
NotificationBlockingHelperManager blockingHelperManager =
|
||||||
|
Dependency.get(NotificationBlockingHelperManager.class);
|
||||||
|
addOnExpandedHeightListener((height, unused) -> {
|
||||||
|
blockingHelperManager.setNotificationShadeExpanded(height);
|
||||||
|
});
|
||||||
|
|
||||||
updateWillNotDraw();
|
updateWillNotDraw();
|
||||||
mBackgroundPaint.setAntiAlias(true);
|
mBackgroundPaint.setAntiAlias(true);
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
@@ -1038,45 +1047,63 @@ public class NotificationStackScrollLayout extends ViewGroup
|
|||||||
mQsContainer = qsContainer;
|
mQsContainer = qsContainer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles cleanup after the given {@code view} has been fully swiped out (including
|
||||||
|
* re-invoking dismiss logic in case the notification has not made its way out yet).
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void onChildDismissed(View v) {
|
public void onChildDismissed(View view) {
|
||||||
ExpandableNotificationRow row = (ExpandableNotificationRow) v;
|
ExpandableNotificationRow row = (ExpandableNotificationRow) view;
|
||||||
if (!row.isDismissed()) {
|
if (!row.isDismissed()) {
|
||||||
handleChildDismissed(v);
|
handleChildViewDismissed(view);
|
||||||
}
|
}
|
||||||
ViewGroup transientContainer = row.getTransientContainer();
|
ViewGroup transientContainer = row.getTransientContainer();
|
||||||
if (transientContainer != null) {
|
if (transientContainer != null) {
|
||||||
transientContainer.removeTransientView(v);
|
transientContainer.removeTransientView(view);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleChildDismissed(View v) {
|
/**
|
||||||
|
* Starts up notification dismiss and tells the notification, if any, to remove itself from
|
||||||
|
* layout.
|
||||||
|
*
|
||||||
|
* @param view view (e.g. notification) to dismiss from the layout
|
||||||
|
*/
|
||||||
|
private void handleChildViewDismissed(View view) {
|
||||||
if (mDismissAllInProgress) {
|
if (mDismissAllInProgress) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean isBlockingHelperShown = false;
|
||||||
|
|
||||||
setSwipingInProgress(false);
|
setSwipingInProgress(false);
|
||||||
if (mDragAnimPendingChildren.contains(v)) {
|
if (mDragAnimPendingChildren.contains(view)) {
|
||||||
// We start the swipe and finish it in the same frame, we don't want any animation
|
// We start the swipe and finish it in the same frame; we don't want a drag animation.
|
||||||
// for the drag
|
mDragAnimPendingChildren.remove(view);
|
||||||
mDragAnimPendingChildren.remove(v);
|
|
||||||
}
|
}
|
||||||
mSwipedOutViews.add(v);
|
mAmbientState.onDragFinished(view);
|
||||||
mAmbientState.onDragFinished(v);
|
|
||||||
updateContinuousShadowDrawing();
|
updateContinuousShadowDrawing();
|
||||||
if (v instanceof ExpandableNotificationRow) {
|
|
||||||
ExpandableNotificationRow row = (ExpandableNotificationRow) v;
|
if (view instanceof ExpandableNotificationRow) {
|
||||||
|
ExpandableNotificationRow row = (ExpandableNotificationRow) view;
|
||||||
if (row.isHeadsUp()) {
|
if (row.isHeadsUp()) {
|
||||||
mHeadsUpManager.addSwipedOutNotification(row.getStatusBarNotification().getKey());
|
mHeadsUpManager.addSwipedOutNotification(row.getStatusBarNotification().getKey());
|
||||||
}
|
}
|
||||||
}
|
isBlockingHelperShown =
|
||||||
if (v instanceof ExpandableNotificationRow) {
|
row.performDismissWithBlockingHelper(false /* fromAccessibility */);
|
||||||
((ExpandableNotificationRow) v).performDismiss(false /* fromAccessibility */);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isBlockingHelperShown) {
|
||||||
|
mSwipedOutViews.add(view);
|
||||||
|
}
|
||||||
mFalsingManager.onNotificationDismissed();
|
mFalsingManager.onNotificationDismissed();
|
||||||
if (mFalsingManager.shouldEnforceBouncer()) {
|
if (mFalsingManager.shouldEnforceBouncer()) {
|
||||||
mStatusBar.executeRunnableDismissingKeyguard(null, null /* cancelAction */,
|
mStatusBar.executeRunnableDismissingKeyguard(
|
||||||
false /* dismissShade */, true /* afterKeyguardGone */, false /* deferred */);
|
null,
|
||||||
|
null /* cancelAction */,
|
||||||
|
false /* dismissShade */,
|
||||||
|
true /* afterKeyguardGone */,
|
||||||
|
false /* deferred */);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2729,9 +2756,8 @@ public class NotificationStackScrollLayout extends ViewGroup
|
|||||||
updateScrollStateForRemovedChild(expandableView);
|
updateScrollStateForRemovedChild(expandableView);
|
||||||
boolean animationGenerated = generateRemoveAnimation(child);
|
boolean animationGenerated = generateRemoveAnimation(child);
|
||||||
if (animationGenerated) {
|
if (animationGenerated) {
|
||||||
if (!mSwipedOutViews.contains(child)) {
|
if (!mSwipedOutViews.contains(child)
|
||||||
container.getOverlay().add(child);
|
|| Math.abs(expandableView.getTranslation()) != expandableView.getWidth()) {
|
||||||
} else if (Math.abs(expandableView.getTranslation()) != expandableView.getWidth()) {
|
|
||||||
container.addTransientView(child, 0);
|
container.addTransientView(child, 0);
|
||||||
expandableView.setTransientContainer(container);
|
expandableView.setTransientContainer(container);
|
||||||
}
|
}
|
||||||
@@ -4618,7 +4644,7 @@ public class NotificationStackScrollLayout extends ViewGroup
|
|||||||
if (mIsExpanded) {
|
if (mIsExpanded) {
|
||||||
// We don't want to quick-dismiss when it's a heads up as this might lead to closing
|
// We don't want to quick-dismiss when it's a heads up as this might lead to closing
|
||||||
// of the panel early.
|
// of the panel early.
|
||||||
handleChildDismissed(view);
|
handleChildViewDismissed(view);
|
||||||
}
|
}
|
||||||
mStatusBar.getGutsManager().closeAndSaveGuts(true /* removeLeavebehind */,
|
mStatusBar.getGutsManager().closeAndSaveGuts(true /* removeLeavebehind */,
|
||||||
false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
|
false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ public class StackStateAnimator {
|
|||||||
= (int) (ANIMATION_DURATION_HEADS_UP_APPEAR
|
= (int) (ANIMATION_DURATION_HEADS_UP_APPEAR
|
||||||
* HeadsUpAppearInterpolator.getFractionUntilOvershoot());
|
* HeadsUpAppearInterpolator.getFractionUntilOvershoot());
|
||||||
public static final int ANIMATION_DURATION_HEADS_UP_DISAPPEAR = 300;
|
public static final int ANIMATION_DURATION_HEADS_UP_DISAPPEAR = 300;
|
||||||
|
public static final int ANIMATION_DURATION_BLOCKING_HELPER_FADE = 240;
|
||||||
public static final int ANIMATION_DELAY_PER_ELEMENT_INTERRUPTING = 80;
|
public static final int ANIMATION_DELAY_PER_ELEMENT_INTERRUPTING = 80;
|
||||||
public static final int ANIMATION_DELAY_PER_ELEMENT_MANUAL = 32;
|
public static final int ANIMATION_DELAY_PER_ELEMENT_MANUAL = 32;
|
||||||
public static final int ANIMATION_DELAY_PER_ELEMENT_GO_TO_FULL_SHADE = 48;
|
public static final int ANIMATION_DELAY_PER_ELEMENT_GO_TO_FULL_SHADE = 48;
|
||||||
@@ -79,6 +80,7 @@ public class StackStateAnimator {
|
|||||||
private ValueAnimator mBottomOverScrollAnimator;
|
private ValueAnimator mBottomOverScrollAnimator;
|
||||||
private int mHeadsUpAppearHeightBottom;
|
private int mHeadsUpAppearHeightBottom;
|
||||||
private boolean mShadeExpanded;
|
private boolean mShadeExpanded;
|
||||||
|
private ArrayList<ExpandableView> mTransientViewsToRemove = new ArrayList<>();
|
||||||
private NotificationShelf mShelf;
|
private NotificationShelf mShelf;
|
||||||
private float mStatusBarIconLocation;
|
private float mStatusBarIconLocation;
|
||||||
private int[] mTmpLocation = new int[2];
|
private int[] mTmpLocation = new int[2];
|
||||||
@@ -333,6 +335,12 @@ public class StackStateAnimator {
|
|||||||
|
|
||||||
private void onAnimationFinished() {
|
private void onAnimationFinished() {
|
||||||
mHostLayout.onChildAnimationFinished();
|
mHostLayout.onChildAnimationFinished();
|
||||||
|
|
||||||
|
for (ExpandableView transientViewsToRemove : mTransientViewsToRemove) {
|
||||||
|
transientViewsToRemove.getTransientContainer()
|
||||||
|
.removeTransientView(transientViewsToRemove);
|
||||||
|
}
|
||||||
|
mTransientViewsToRemove.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -362,7 +370,7 @@ public class StackStateAnimator {
|
|||||||
} else if (event.animationType ==
|
} else if (event.animationType ==
|
||||||
NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE) {
|
NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE) {
|
||||||
if (changingView.getVisibility() != View.VISIBLE) {
|
if (changingView.getVisibility() != View.VISIBLE) {
|
||||||
removeFromOverlay(changingView);
|
removeTransientView(changingView);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -402,8 +410,7 @@ public class StackStateAnimator {
|
|||||||
0, new Runnable() {
|
0, new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
// remove the temporary overlay
|
removeTransientView(changingView);
|
||||||
removeFromOverlay(changingView);
|
|
||||||
}
|
}
|
||||||
}, null);
|
}, null);
|
||||||
} else if (event.animationType ==
|
} else if (event.animationType ==
|
||||||
@@ -494,6 +501,12 @@ public class StackStateAnimator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void removeTransientView(ExpandableView viewToRemove) {
|
||||||
|
if (viewToRemove.getTransientContainer() != null) {
|
||||||
|
viewToRemove.getTransientContainer().removeTransientView(viewToRemove);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void removeFromOverlay(View changingView) {
|
public static void removeFromOverlay(View changingView) {
|
||||||
ViewGroup parent = (ViewGroup) changingView.getParent();
|
ViewGroup parent = (ViewGroup) changingView.getParent();
|
||||||
if (parent != null) {
|
if (parent != null) {
|
||||||
|
|||||||
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package com.android.systemui.statusbar;
|
package com.android.systemui.statusbar;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.anyInt;
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
@@ -40,8 +42,13 @@ import com.android.systemui.statusbar.stack.NotificationChildrenContainer;
|
|||||||
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.Spy;
|
||||||
|
import org.mockito.junit.MockitoJUnit;
|
||||||
|
import org.mockito.junit.MockitoRule;
|
||||||
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
@@ -49,52 +56,59 @@ import java.util.function.Consumer;
|
|||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class ExpandableNotificationRowTest extends SysuiTestCase {
|
public class ExpandableNotificationRowTest extends SysuiTestCase {
|
||||||
|
|
||||||
private ExpandableNotificationRow mGroup;
|
private ExpandableNotificationRow mGroupRow;
|
||||||
|
|
||||||
private NotificationTestHelper mNotificationTestHelper;
|
private NotificationTestHelper mNotificationTestHelper;
|
||||||
boolean mHeadsUpAnimatingAway = false;
|
boolean mHeadsUpAnimatingAway = false;
|
||||||
|
|
||||||
|
@Rule public MockitoRule mockito = MockitoJUnit.rule();
|
||||||
|
@Mock private NotificationBlockingHelperManager mBlockingHelperManager;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
mNotificationTestHelper = new NotificationTestHelper(mContext);
|
mNotificationTestHelper = new NotificationTestHelper(mContext);
|
||||||
mGroup = mNotificationTestHelper.createGroup();
|
mGroupRow = mNotificationTestHelper.createGroup();
|
||||||
mGroup.setHeadsUpAnimatingAwayListener(
|
mGroupRow.setHeadsUpAnimatingAwayListener(
|
||||||
animatingAway -> mHeadsUpAnimatingAway = animatingAway);
|
animatingAway -> mHeadsUpAnimatingAway = animatingAway);
|
||||||
|
mDependency.injectTestDependency(
|
||||||
|
NotificationBlockingHelperManager.class,
|
||||||
|
mBlockingHelperManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGroupSummaryNotShowingIconWhenPublic() {
|
public void testGroupSummaryNotShowingIconWhenPublic() {
|
||||||
mGroup.setSensitive(true, true);
|
mGroupRow.setSensitive(true, true);
|
||||||
mGroup.setHideSensitiveForIntrinsicHeight(true);
|
mGroupRow.setHideSensitiveForIntrinsicHeight(true);
|
||||||
Assert.assertTrue(mGroup.isSummaryWithChildren());
|
assertTrue(mGroupRow.isSummaryWithChildren());
|
||||||
Assert.assertFalse(mGroup.isShowingIcon());
|
assertFalse(mGroupRow.isShowingIcon());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNotificationHeaderVisibleWhenAnimating() {
|
public void testNotificationHeaderVisibleWhenAnimating() {
|
||||||
mGroup.setSensitive(true, true);
|
mGroupRow.setSensitive(true, true);
|
||||||
mGroup.setHideSensitive(true, false, 0, 0);
|
mGroupRow.setHideSensitive(true, false, 0, 0);
|
||||||
mGroup.setHideSensitive(false, true, 0, 0);
|
mGroupRow.setHideSensitive(false, true, 0, 0);
|
||||||
Assert.assertTrue(mGroup.getChildrenContainer().getVisibleHeader().getVisibility()
|
assertTrue(mGroupRow.getChildrenContainer().getVisibleHeader().getVisibility()
|
||||||
== View.VISIBLE);
|
== View.VISIBLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUserLockedResetEvenWhenNoChildren() {
|
public void testUserLockedResetEvenWhenNoChildren() {
|
||||||
mGroup.setUserLocked(true);
|
mGroupRow.setUserLocked(true);
|
||||||
mGroup.removeAllChildren();
|
mGroupRow.removeAllChildren();
|
||||||
mGroup.setUserLocked(false);
|
mGroupRow.setUserLocked(false);
|
||||||
Assert.assertFalse("The childrencontainer should not be userlocked but is, the state "
|
assertFalse("The childrencontainer should not be userlocked but is, the state "
|
||||||
+ "seems out of sync.", mGroup.getChildrenContainer().isUserLocked());
|
+ "seems out of sync.", mGroupRow.getChildrenContainer().isUserLocked());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReinflatedOnDensityChange() {
|
public void testReinflatedOnDensityChange() {
|
||||||
mGroup.setUserLocked(true);
|
mGroupRow.setUserLocked(true);
|
||||||
mGroup.removeAllChildren();
|
mGroupRow.removeAllChildren();
|
||||||
mGroup.setUserLocked(false);
|
mGroupRow.setUserLocked(false);
|
||||||
NotificationChildrenContainer mockContainer = mock(NotificationChildrenContainer.class);
|
NotificationChildrenContainer mockContainer = mock(NotificationChildrenContainer.class);
|
||||||
mGroup.setChildrenContainer(mockContainer);
|
mGroupRow.setChildrenContainer(mockContainer);
|
||||||
mGroup.onDensityOrFontScaleChanged();
|
mGroupRow.onDensityOrFontScaleChanged();
|
||||||
verify(mockContainer).reInflateViews(any(), any());
|
verify(mockContainer).reInflateViews(any(), any());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,38 +165,38 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testClickSound() throws Exception {
|
public void testClickSound() throws Exception {
|
||||||
Assert.assertTrue("Should play sounds by default.", mGroup.isSoundEffectsEnabled());
|
assertTrue("Should play sounds by default.", mGroupRow.isSoundEffectsEnabled());
|
||||||
mGroup.setDark(true /* dark */, false /* fade */, 0 /* delay */);
|
mGroupRow.setDark(true /* dark */, false /* fade */, 0 /* delay */);
|
||||||
mGroup.setSecureStateProvider(()-> false);
|
mGroupRow.setSecureStateProvider(()-> false);
|
||||||
Assert.assertFalse("Shouldn't play sounds when dark and trusted.",
|
assertFalse("Shouldn't play sounds when dark and trusted.",
|
||||||
mGroup.isSoundEffectsEnabled());
|
mGroupRow.isSoundEffectsEnabled());
|
||||||
mGroup.setSecureStateProvider(()-> true);
|
mGroupRow.setSecureStateProvider(()-> true);
|
||||||
Assert.assertTrue("Should always play sounds when not trusted.",
|
assertTrue("Should always play sounds when not trusted.",
|
||||||
mGroup.isSoundEffectsEnabled());
|
mGroupRow.isSoundEffectsEnabled());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSetDismissed_longPressListenerRemoved() {
|
public void testSetDismissed_longPressListenerRemoved() {
|
||||||
ExpandableNotificationRow.LongPressListener listener =
|
ExpandableNotificationRow.LongPressListener listener =
|
||||||
mock(ExpandableNotificationRow.LongPressListener.class);
|
mock(ExpandableNotificationRow.LongPressListener.class);
|
||||||
mGroup.setLongPressListener(listener);
|
mGroupRow.setLongPressListener(listener);
|
||||||
mGroup.doLongClickCallback(0,0);
|
mGroupRow.doLongClickCallback(0,0);
|
||||||
verify(listener, times(1)).onLongPress(eq(mGroup), eq(0), eq(0),
|
verify(listener, times(1)).onLongPress(eq(mGroupRow), eq(0), eq(0),
|
||||||
any(NotificationMenuRowPlugin.MenuItem.class));
|
any(NotificationMenuRowPlugin.MenuItem.class));
|
||||||
reset(listener);
|
reset(listener);
|
||||||
|
|
||||||
mGroup.setDismissed(true);
|
mGroupRow.setDismissed(true);
|
||||||
mGroup.doLongClickCallback(0,0);
|
mGroupRow.doLongClickCallback(0,0);
|
||||||
verify(listener, times(0)).onLongPress(eq(mGroup), eq(0), eq(0),
|
verify(listener, times(0)).onLongPress(eq(mGroupRow), eq(0), eq(0),
|
||||||
any(NotificationMenuRowPlugin.MenuItem.class));
|
any(NotificationMenuRowPlugin.MenuItem.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testShowAppOps_noHeader() {
|
public void testShowAppOps_noHeader() {
|
||||||
// public notification is custom layout - no header
|
// public notification is custom layout - no header
|
||||||
mGroup.setSensitive(true, true);
|
mGroupRow.setSensitive(true, true);
|
||||||
mGroup.setAppOpsOnClickListener(null);
|
mGroupRow.setAppOpsOnClickListener(null);
|
||||||
mGroup.showAppOpsIcons(null);
|
mGroupRow.showAppOpsIcons(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -190,17 +204,17 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
|
|||||||
NotificationHeaderView mockHeader = mock(NotificationHeaderView.class);
|
NotificationHeaderView mockHeader = mock(NotificationHeaderView.class);
|
||||||
|
|
||||||
NotificationContentView publicLayout = mock(NotificationContentView.class);
|
NotificationContentView publicLayout = mock(NotificationContentView.class);
|
||||||
mGroup.setPublicLayout(publicLayout);
|
mGroupRow.setPublicLayout(publicLayout);
|
||||||
NotificationContentView privateLayout = mock(NotificationContentView.class);
|
NotificationContentView privateLayout = mock(NotificationContentView.class);
|
||||||
mGroup.setPrivateLayout(privateLayout);
|
mGroupRow.setPrivateLayout(privateLayout);
|
||||||
NotificationChildrenContainer mockContainer = mock(NotificationChildrenContainer.class);
|
NotificationChildrenContainer mockContainer = mock(NotificationChildrenContainer.class);
|
||||||
when(mockContainer.getNotificationChildCount()).thenReturn(1);
|
when(mockContainer.getNotificationChildCount()).thenReturn(1);
|
||||||
when(mockContainer.getHeaderView()).thenReturn(mockHeader);
|
when(mockContainer.getHeaderView()).thenReturn(mockHeader);
|
||||||
mGroup.setChildrenContainer(mockContainer);
|
mGroupRow.setChildrenContainer(mockContainer);
|
||||||
|
|
||||||
ArraySet<Integer> ops = new ArraySet<>();
|
ArraySet<Integer> ops = new ArraySet<>();
|
||||||
ops.add(AppOpsManager.OP_ANSWER_PHONE_CALLS);
|
ops.add(AppOpsManager.OP_ANSWER_PHONE_CALLS);
|
||||||
mGroup.showAppOpsIcons(ops);
|
mGroupRow.showAppOpsIcons(ops);
|
||||||
|
|
||||||
verify(mockHeader, times(1)).showAppOpsIcons(ops);
|
verify(mockHeader, times(1)).showAppOpsIcons(ops);
|
||||||
verify(privateLayout, times(1)).showAppOpsIcons(ops);
|
verify(privateLayout, times(1)).showAppOpsIcons(ops);
|
||||||
@@ -214,17 +228,50 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
|
|||||||
ExpandableNotificationRow.OnAppOpsClickListener.class);
|
ExpandableNotificationRow.OnAppOpsClickListener.class);
|
||||||
View view = mock(View.class);
|
View view = mock(View.class);
|
||||||
|
|
||||||
mGroup.setAppOpsOnClickListener(l);
|
mGroupRow.setAppOpsOnClickListener(l);
|
||||||
|
|
||||||
mGroup.getAppOpsOnClickListener().onClick(view);
|
mGroupRow.getAppOpsOnClickListener().onClick(view);
|
||||||
verify(l, times(1)).onClick(any(), anyInt(), anyInt(), any());
|
verify(l, times(1)).onClick(any(), anyInt(), anyInt(), any());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testHeadsUpAnimatingAwayListener() {
|
public void testHeadsUpAnimatingAwayListener() {
|
||||||
mGroup.setHeadsUpAnimatingAway(true);
|
mGroupRow.setHeadsUpAnimatingAway(true);
|
||||||
Assert.assertEquals(true, mHeadsUpAnimatingAway);
|
Assert.assertEquals(true, mHeadsUpAnimatingAway);
|
||||||
mGroup.setHeadsUpAnimatingAway(false);
|
mGroupRow.setHeadsUpAnimatingAway(false);
|
||||||
Assert.assertEquals(false, mHeadsUpAnimatingAway);
|
Assert.assertEquals(false, mHeadsUpAnimatingAway);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPerformDismissWithBlockingHelper_falseWhenBlockingHelperIsntShown() {
|
||||||
|
when(mBlockingHelperManager.perhapsShowBlockingHelper(
|
||||||
|
eq(mGroupRow), any(NotificationMenuRowPlugin.class))).thenReturn(false);
|
||||||
|
|
||||||
|
assertFalse(
|
||||||
|
mGroupRow.performDismissWithBlockingHelper(false /* fromAccessibility */));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPerformDismissWithBlockingHelper_doesntPerformOnGroupSummary() {
|
||||||
|
ExpandableNotificationRow childRow = mGroupRow.getChildrenContainer().getViewAtPosition(0);
|
||||||
|
when(mBlockingHelperManager.perhapsShowBlockingHelper(eq(childRow), any(NotificationMenuRowPlugin.class)))
|
||||||
|
.thenReturn(true);
|
||||||
|
|
||||||
|
assertTrue(
|
||||||
|
childRow.performDismissWithBlockingHelper(false /* fromAccessibility */));
|
||||||
|
|
||||||
|
verify(mBlockingHelperManager, times(1))
|
||||||
|
.perhapsShowBlockingHelper(eq(childRow), any(NotificationMenuRowPlugin.class));
|
||||||
|
verify(mBlockingHelperManager, times(0))
|
||||||
|
.perhapsShowBlockingHelper(eq(mGroupRow), any(NotificationMenuRowPlugin.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIsBlockingHelperShowing_isCorrectlyUpdated() {
|
||||||
|
mGroupRow.setBlockingHelperShowing(true);
|
||||||
|
assertTrue(mGroupRow.isBlockingHelperShowing());
|
||||||
|
|
||||||
|
mGroupRow.setBlockingHelperShowing(false);
|
||||||
|
assertFalse(mGroupRow.isBlockingHelperShowing());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,214 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2018 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 com.android.systemui.SysuiTestCase;
|
||||||
|
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.support.test.filters.SmallTest;
|
||||||
|
import android.testing.AndroidTestingRunner;
|
||||||
|
import android.testing.TestableLooper;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.MockitoJUnit;
|
||||||
|
import org.mockito.junit.MockitoRule;
|
||||||
|
|
||||||
|
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
|
||||||
|
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
|
||||||
|
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_POSITIVE;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link NotificationBlockingHelperManager}.
|
||||||
|
*/
|
||||||
|
@SmallTest
|
||||||
|
@org.junit.runner.RunWith(AndroidTestingRunner.class)
|
||||||
|
@TestableLooper.RunWithLooper
|
||||||
|
public class NotificationBlockingHelperManagerTest extends SysuiTestCase {
|
||||||
|
|
||||||
|
private NotificationBlockingHelperManager mBlockingHelperManager;
|
||||||
|
|
||||||
|
private NotificationTestHelper mHelper;
|
||||||
|
|
||||||
|
@Rule public MockitoRule mockito = MockitoJUnit.rule();
|
||||||
|
@Mock private NotificationGutsManager mGutsManager;
|
||||||
|
@Mock private NotificationEntryManager mEntryManager;
|
||||||
|
@Mock private NotificationMenuRow mMenuRow;
|
||||||
|
@Mock private NotificationMenuRowPlugin.MenuItem mMenuItem;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
mBlockingHelperManager = new NotificationBlockingHelperManager(mContext);
|
||||||
|
|
||||||
|
mHelper = new NotificationTestHelper(mContext);
|
||||||
|
when(mGutsManager.openGuts(
|
||||||
|
any(View.class),
|
||||||
|
anyInt(),
|
||||||
|
anyInt(),
|
||||||
|
any(NotificationMenuRowPlugin.MenuItem.class)))
|
||||||
|
.thenReturn(true);
|
||||||
|
mDependency.injectTestDependency(NotificationGutsManager.class, mGutsManager);
|
||||||
|
mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
|
||||||
|
when(mMenuRow.getLongpressMenuItem(any(Context.class))).thenReturn(mMenuItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDismissCurrentBlockingHelper_nullBlockingHelperRow() {
|
||||||
|
// By default, this shouldn't dismiss (no pointers/vars set up!)
|
||||||
|
assertFalse(mBlockingHelperManager.dismissCurrentBlockingHelper());
|
||||||
|
assertTrue(mBlockingHelperManager.isBlockingHelperRowNull());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDismissCurrentBlockingHelper_withDetachedBlockingHelperRow() throws Exception {
|
||||||
|
ExpandableNotificationRow row = spy(mHelper.createRow());
|
||||||
|
row.setBlockingHelperShowing(true);
|
||||||
|
when(row.isAttachedToWindow()).thenReturn(false);
|
||||||
|
mBlockingHelperManager.setBlockingHelperRowForTest(row);
|
||||||
|
|
||||||
|
assertTrue(mBlockingHelperManager.dismissCurrentBlockingHelper());
|
||||||
|
assertTrue(mBlockingHelperManager.isBlockingHelperRowNull());
|
||||||
|
|
||||||
|
verify(mEntryManager, times(0)).updateNotifications();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDismissCurrentBlockingHelper_withAttachedBlockingHelperRow() throws Exception {
|
||||||
|
ExpandableNotificationRow row = spy(mHelper.createRow());
|
||||||
|
row.setBlockingHelperShowing(true);
|
||||||
|
when(row.isAttachedToWindow()).thenReturn(true);
|
||||||
|
mBlockingHelperManager.setBlockingHelperRowForTest(row);
|
||||||
|
|
||||||
|
assertTrue(mBlockingHelperManager.dismissCurrentBlockingHelper());
|
||||||
|
assertTrue(mBlockingHelperManager.isBlockingHelperRowNull());
|
||||||
|
|
||||||
|
verify(mEntryManager).updateNotifications();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPerhapsShowBlockingHelper_shown() throws Exception {
|
||||||
|
ExpandableNotificationRow row = mHelper.createRow();
|
||||||
|
row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
|
||||||
|
mBlockingHelperManager.setNotificationShadeExpanded(1f);
|
||||||
|
|
||||||
|
assertTrue(mBlockingHelperManager.perhapsShowBlockingHelper(row, mMenuRow));
|
||||||
|
|
||||||
|
verify(mGutsManager).openGuts(row, 0, 0, mMenuItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPerhapsShowBlockingHelper_shownForLargeGroup() throws Exception {
|
||||||
|
ExpandableNotificationRow groupRow = mHelper.createGroup(10);
|
||||||
|
groupRow.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
|
||||||
|
mBlockingHelperManager.setNotificationShadeExpanded(1f);
|
||||||
|
|
||||||
|
assertTrue(mBlockingHelperManager.perhapsShowBlockingHelper(groupRow, mMenuRow));
|
||||||
|
|
||||||
|
verify(mGutsManager).openGuts(groupRow, 0, 0, mMenuItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPerhapsShowBlockingHelper_shownForOnlyChildNotification()
|
||||||
|
throws Exception {
|
||||||
|
ExpandableNotificationRow groupRow = mHelper.createGroup(1);
|
||||||
|
// Explicitly get the children container & call getViewAtPosition on it instead of the row
|
||||||
|
// as other factors such as view expansion may cause us to get the parent row back instead
|
||||||
|
// of the child row.
|
||||||
|
ExpandableNotificationRow childRow = groupRow.getChildrenContainer().getViewAtPosition(0);
|
||||||
|
childRow.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
|
||||||
|
mBlockingHelperManager.setNotificationShadeExpanded(1f);
|
||||||
|
|
||||||
|
assertTrue(mBlockingHelperManager.perhapsShowBlockingHelper(childRow, mMenuRow));
|
||||||
|
|
||||||
|
verify(mGutsManager).openGuts(childRow, 0, 0, mMenuItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPerhapsShowBlockingHelper_notShownDueToNeutralUserSentiment() throws Exception {
|
||||||
|
ExpandableNotificationRow row = mHelper.createRow();
|
||||||
|
row.getEntry().userSentiment = USER_SENTIMENT_NEUTRAL;
|
||||||
|
mBlockingHelperManager.setNotificationShadeExpanded(1f);
|
||||||
|
|
||||||
|
assertFalse(mBlockingHelperManager.perhapsShowBlockingHelper(row, mMenuRow));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPerhapsShowBlockingHelper_notShownDueToPositiveUserSentiment()
|
||||||
|
throws Exception {
|
||||||
|
ExpandableNotificationRow row = mHelper.createRow();
|
||||||
|
row.getEntry().userSentiment = USER_SENTIMENT_POSITIVE;
|
||||||
|
mBlockingHelperManager.setNotificationShadeExpanded(1f);
|
||||||
|
|
||||||
|
assertFalse(mBlockingHelperManager.perhapsShowBlockingHelper(row, mMenuRow));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPerhapsShowBlockingHelper_notShownDueToShadeVisibility() throws Exception {
|
||||||
|
ExpandableNotificationRow row = mHelper.createRow();
|
||||||
|
row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
|
||||||
|
// Hide the shade
|
||||||
|
mBlockingHelperManager.setNotificationShadeExpanded(0f);
|
||||||
|
|
||||||
|
assertFalse(mBlockingHelperManager.perhapsShowBlockingHelper(row, mMenuRow));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPerhapsShowBlockingHelper_notShownAsNotificationIsInMultipleChildGroup()
|
||||||
|
throws Exception {
|
||||||
|
ExpandableNotificationRow groupRow = mHelper.createGroup(2);
|
||||||
|
// Explicitly get the children container & call getViewAtPosition on it instead of the row
|
||||||
|
// as other factors such as view expansion may cause us to get the parent row back instead
|
||||||
|
// of the child row.
|
||||||
|
ExpandableNotificationRow childRow = groupRow.getChildrenContainer().getViewAtPosition(0);
|
||||||
|
childRow.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
|
||||||
|
mBlockingHelperManager.setNotificationShadeExpanded(1f);
|
||||||
|
|
||||||
|
assertFalse(mBlockingHelperManager.perhapsShowBlockingHelper(childRow, mMenuRow));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBlockingHelperShowAndDismiss() throws Exception{
|
||||||
|
ExpandableNotificationRow row = spy(mHelper.createRow());
|
||||||
|
row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
|
||||||
|
when(row.isAttachedToWindow()).thenReturn(true);
|
||||||
|
mBlockingHelperManager.setNotificationShadeExpanded(1f);
|
||||||
|
|
||||||
|
// Show check
|
||||||
|
assertTrue(mBlockingHelperManager.perhapsShowBlockingHelper(row, mMenuRow));
|
||||||
|
|
||||||
|
verify(mGutsManager).openGuts(row, 0, 0, mMenuItem);
|
||||||
|
|
||||||
|
// Dismiss check
|
||||||
|
assertTrue(mBlockingHelperManager.dismissCurrentBlockingHelper());
|
||||||
|
assertTrue(mBlockingHelperManager.isBlockingHelperRowNull());
|
||||||
|
|
||||||
|
verify(mEntryManager).updateNotifications();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,6 +20,7 @@ import static android.app.AppOpsManager.OP_CAMERA;
|
|||||||
import static android.app.AppOpsManager.OP_RECORD_AUDIO;
|
import static android.app.AppOpsManager.OP_RECORD_AUDIO;
|
||||||
import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
|
import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
|
||||||
|
|
||||||
|
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
|
||||||
import static junit.framework.Assert.assertNotNull;
|
import static junit.framework.Assert.assertNotNull;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
@@ -27,6 +28,8 @@ import static org.junit.Assert.fail;
|
|||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||||
import static org.mockito.ArgumentMatchers.anyInt;
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.ArgumentMatchers.isNull;
|
||||||
import static org.mockito.Mockito.doNothing;
|
import static org.mockito.Mockito.doNothing;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
@@ -34,31 +37,24 @@ import static org.mockito.Mockito.spy;
|
|||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.times;
|
import static org.mockito.Mockito.times;
|
||||||
|
|
||||||
|
import android.app.INotificationManager;
|
||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
import android.app.NotificationChannel;
|
import android.app.NotificationChannel;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
|
||||||
import android.os.UserHandle;
|
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.service.notification.StatusBarNotification;
|
import android.service.notification.StatusBarNotification;
|
||||||
import android.support.test.filters.SmallTest;
|
import android.support.test.filters.SmallTest;
|
||||||
import android.testing.AndroidTestingRunner;
|
import android.testing.AndroidTestingRunner;
|
||||||
import android.testing.TestableLooper;
|
import android.testing.TestableLooper;
|
||||||
import android.util.ArraySet;
|
import android.util.ArraySet;
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import com.android.systemui.R;
|
|
||||||
import com.android.systemui.SysuiTestCase;
|
import com.android.systemui.SysuiTestCase;
|
||||||
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
|
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
|
||||||
import com.android.systemui.statusbar.NotificationData;
|
|
||||||
import com.android.systemui.statusbar.NotificationGuts;
|
|
||||||
import com.android.systemui.statusbar.NotificationTestHelper;
|
|
||||||
import com.android.systemui.statusbar.notification.NotificationInflater;
|
|
||||||
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
|
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
@@ -70,15 +66,18 @@ import org.mockito.Mock;
|
|||||||
import org.mockito.junit.MockitoRule;
|
import org.mockito.junit.MockitoRule;
|
||||||
import org.mockito.junit.MockitoJUnit;
|
import org.mockito.junit.MockitoJUnit;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link NotificationGutsManager}.
|
||||||
|
*/
|
||||||
@SmallTest
|
@SmallTest
|
||||||
@RunWith(AndroidTestingRunner.class)
|
@RunWith(AndroidTestingRunner.class)
|
||||||
@TestableLooper.RunWithLooper
|
@TestableLooper.RunWithLooper
|
||||||
public class NotificationGutsManagerTest extends SysuiTestCase {
|
public class NotificationGutsManagerTest extends SysuiTestCase {
|
||||||
private static final String TEST_CHANNEL_ID = "NotificationManagerServiceTestChannelId";
|
private static final String TEST_CHANNEL_ID = "NotificationManagerServiceTestChannelId";
|
||||||
|
|
||||||
private final String mPackageName = mContext.getPackageName();
|
|
||||||
private final int mUid = Binder.getCallingUid();
|
|
||||||
|
|
||||||
private NotificationChannel mTestNotificationChannel = new NotificationChannel(
|
private NotificationChannel mTestNotificationChannel = new NotificationChannel(
|
||||||
TEST_CHANNEL_ID, TEST_CHANNEL_ID, NotificationManager.IMPORTANCE_DEFAULT);
|
TEST_CHANNEL_ID, TEST_CHANNEL_ID, NotificationManager.IMPORTANCE_DEFAULT);
|
||||||
private TestableLooper mTestableLooper;
|
private TestableLooper mTestableLooper;
|
||||||
@@ -87,7 +86,6 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
|
|||||||
private NotificationGutsManager mGutsManager;
|
private NotificationGutsManager mGutsManager;
|
||||||
|
|
||||||
@Rule public MockitoRule mockito = MockitoJUnit.rule();
|
@Rule public MockitoRule mockito = MockitoJUnit.rule();
|
||||||
@Mock private NotificationLockscreenUserManager mLockscreenUserManager;
|
|
||||||
@Mock private NotificationPresenter mPresenter;
|
@Mock private NotificationPresenter mPresenter;
|
||||||
@Mock private NotificationEntryManager mEntryManager;
|
@Mock private NotificationEntryManager mEntryManager;
|
||||||
@Mock private NotificationStackScrollLayout mStackScroller;
|
@Mock private NotificationStackScrollLayout mStackScroller;
|
||||||
@@ -118,7 +116,12 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Test doesn't support animation since the guts view is not attached.
|
// Test doesn't support animation since the guts view is not attached.
|
||||||
doNothing().when(guts).openControls(anyInt(), anyInt(), anyBoolean(), any(Runnable.class));
|
doNothing().when(guts).openControls(
|
||||||
|
eq(true) /* shouldDoCircularReveal */,
|
||||||
|
anyInt(),
|
||||||
|
anyInt(),
|
||||||
|
anyBoolean(),
|
||||||
|
any(Runnable.class));
|
||||||
|
|
||||||
ExpandableNotificationRow realRow = createTestNotificationRow();
|
ExpandableNotificationRow realRow = createTestNotificationRow();
|
||||||
NotificationMenuRowPlugin.MenuItem menuItem = createTestMenuItem(realRow);
|
NotificationMenuRowPlugin.MenuItem menuItem = createTestMenuItem(realRow);
|
||||||
@@ -130,7 +133,12 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
|
|||||||
mGutsManager.openGuts(row, 0, 0, menuItem);
|
mGutsManager.openGuts(row, 0, 0, menuItem);
|
||||||
assertEquals(View.INVISIBLE, guts.getVisibility());
|
assertEquals(View.INVISIBLE, guts.getVisibility());
|
||||||
mTestableLooper.processAllMessages();
|
mTestableLooper.processAllMessages();
|
||||||
verify(guts).openControls(anyInt(), anyInt(), anyBoolean(), any(Runnable.class));
|
verify(guts).openControls(
|
||||||
|
eq(true),
|
||||||
|
anyInt(),
|
||||||
|
anyInt(),
|
||||||
|
anyBoolean(),
|
||||||
|
any(Runnable.class));
|
||||||
|
|
||||||
assertEquals(View.VISIBLE, guts.getVisibility());
|
assertEquals(View.VISIBLE, guts.getVisibility());
|
||||||
mGutsManager.closeAndSaveGuts(false, false, false, 0, 0, false);
|
mGutsManager.closeAndSaveGuts(false, false, false, 0, 0, false);
|
||||||
@@ -148,7 +156,12 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Test doesn't support animation since the guts view is not attached.
|
// Test doesn't support animation since the guts view is not attached.
|
||||||
doNothing().when(guts).openControls(anyInt(), anyInt(), anyBoolean(), any(Runnable.class));
|
doNothing().when(guts).openControls(
|
||||||
|
eq(true) /* shouldDoCircularReveal */,
|
||||||
|
anyInt(),
|
||||||
|
anyInt(),
|
||||||
|
anyBoolean(),
|
||||||
|
any(Runnable.class));
|
||||||
|
|
||||||
ExpandableNotificationRow realRow = createTestNotificationRow();
|
ExpandableNotificationRow realRow = createTestNotificationRow();
|
||||||
NotificationMenuRowPlugin.MenuItem menuItem = createTestMenuItem(realRow);
|
NotificationMenuRowPlugin.MenuItem menuItem = createTestMenuItem(realRow);
|
||||||
@@ -160,7 +173,12 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
|
|||||||
|
|
||||||
mGutsManager.openGuts(row, 0, 0, menuItem);
|
mGutsManager.openGuts(row, 0, 0, menuItem);
|
||||||
mTestableLooper.processAllMessages();
|
mTestableLooper.processAllMessages();
|
||||||
verify(guts).openControls(anyInt(), anyInt(), anyBoolean(), any(Runnable.class));
|
verify(guts).openControls(
|
||||||
|
eq(true),
|
||||||
|
anyInt(),
|
||||||
|
anyInt(),
|
||||||
|
anyBoolean(),
|
||||||
|
any(Runnable.class));
|
||||||
|
|
||||||
row.onDensityOrFontScaleChanged();
|
row.onDensityOrFontScaleChanged();
|
||||||
mGutsManager.onDensityOrFontScaleChanged(row);
|
mGutsManager.onDensityOrFontScaleChanged(row);
|
||||||
@@ -247,6 +265,56 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
|
|||||||
assertEquals(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, captor.getValue().getAction());
|
assertEquals(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, captor.getValue().getAction());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInitializeNotificationInfoView_showBlockingHelper() throws Exception {
|
||||||
|
NotificationInfo notificationInfoView = mock(NotificationInfo.class);
|
||||||
|
ExpandableNotificationRow row = mHelper.createRow();
|
||||||
|
row.setBlockingHelperShowing(true);
|
||||||
|
row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
|
||||||
|
StatusBarNotification statusBarNotification = row.getStatusBarNotification();
|
||||||
|
|
||||||
|
mGutsManager.initializeNotificationInfo(row, notificationInfoView);
|
||||||
|
|
||||||
|
verify(notificationInfoView).bindNotification(
|
||||||
|
any(PackageManager.class),
|
||||||
|
any(INotificationManager.class),
|
||||||
|
eq(statusBarNotification.getPackageName()),
|
||||||
|
isNull(),
|
||||||
|
anyInt(),
|
||||||
|
eq(statusBarNotification),
|
||||||
|
any(NotificationInfo.CheckSaveListener.class),
|
||||||
|
any(NotificationInfo.OnSettingsClickListener.class),
|
||||||
|
any(NotificationInfo.OnAppSettingsClickListener.class),
|
||||||
|
any(),
|
||||||
|
eq(true) /* isForBlockingHelper */,
|
||||||
|
eq(true) /* isUserSentimentNegative */);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInitializeNotificationInfoView_dontShowBlockingHelper() throws Exception {
|
||||||
|
NotificationInfo notificationInfoView = mock(NotificationInfo.class);
|
||||||
|
ExpandableNotificationRow row = mHelper.createRow();
|
||||||
|
row.setBlockingHelperShowing(false);
|
||||||
|
row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
|
||||||
|
StatusBarNotification statusBarNotification = row.getStatusBarNotification();
|
||||||
|
|
||||||
|
mGutsManager.initializeNotificationInfo(row, notificationInfoView);
|
||||||
|
|
||||||
|
verify(notificationInfoView).bindNotification(
|
||||||
|
any(PackageManager.class),
|
||||||
|
any(INotificationManager.class),
|
||||||
|
eq(statusBarNotification.getPackageName()),
|
||||||
|
isNull(),
|
||||||
|
anyInt(),
|
||||||
|
eq(statusBarNotification),
|
||||||
|
any(NotificationInfo.CheckSaveListener.class),
|
||||||
|
any(NotificationInfo.OnSettingsClickListener.class),
|
||||||
|
any(NotificationInfo.OnAppSettingsClickListener.class),
|
||||||
|
any(),
|
||||||
|
eq(false) /* isForBlockingHelper */,
|
||||||
|
eq(true) /* isUserSentimentNegative */);
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Utility methods:
|
// Utility methods:
|
||||||
|
|
||||||
|
|||||||
@@ -67,9 +67,13 @@ import com.android.systemui.R;
|
|||||||
import com.android.systemui.SysuiTestCase;
|
import com.android.systemui.SysuiTestCase;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.mockito.ArgumentCaptor;
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.MockitoJUnit;
|
||||||
|
import org.mockito.junit.MockitoRule;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -87,14 +91,21 @@ public class NotificationInfoTest extends SysuiTestCase {
|
|||||||
private static final String TEST_CHANNEL_NAME = "TEST CHANNEL NAME";
|
private static final String TEST_CHANNEL_NAME = "TEST CHANNEL NAME";
|
||||||
|
|
||||||
private NotificationInfo mNotificationInfo;
|
private NotificationInfo mNotificationInfo;
|
||||||
private final INotificationManager mMockINotificationManager = mock(INotificationManager.class);
|
|
||||||
private final PackageManager mMockPackageManager = mock(PackageManager.class);
|
|
||||||
private NotificationChannel mNotificationChannel;
|
private NotificationChannel mNotificationChannel;
|
||||||
private NotificationChannel mDefaultNotificationChannel;
|
private NotificationChannel mDefaultNotificationChannel;
|
||||||
private StatusBarNotification mSbn;
|
private StatusBarNotification mSbn;
|
||||||
|
|
||||||
|
@Rule public MockitoRule mockito = MockitoJUnit.rule();
|
||||||
|
@Mock private INotificationManager mMockINotificationManager;
|
||||||
|
@Mock private PackageManager mMockPackageManager;
|
||||||
|
@Mock private NotificationBlockingHelperManager mBlockingHelperManager;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
|
mDependency.injectTestDependency(
|
||||||
|
NotificationBlockingHelperManager.class,
|
||||||
|
mBlockingHelperManager);
|
||||||
|
|
||||||
// Inflate the layout
|
// Inflate the layout
|
||||||
final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
|
final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
|
||||||
mNotificationInfo = (NotificationInfo) layoutInflater.inflate(R.layout.notification_info,
|
mNotificationInfo = (NotificationInfo) layoutInflater.inflate(R.layout.notification_info,
|
||||||
@@ -311,7 +322,7 @@ public class NotificationInfoTest extends SysuiTestCase {
|
|||||||
public void testbindNotification_BlockingHelper() throws Exception {
|
public void testbindNotification_BlockingHelper() throws Exception {
|
||||||
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
|
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
|
||||||
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null,
|
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null,
|
||||||
null, null, true);
|
null, null, false, true);
|
||||||
final TextView view = mNotificationInfo.findViewById(R.id.block_prompt);
|
final TextView view = mNotificationInfo.findViewById(R.id.block_prompt);
|
||||||
assertEquals(View.VISIBLE, view.getVisibility());
|
assertEquals(View.VISIBLE, view.getVisibility());
|
||||||
assertEquals(mContext.getString(R.string.inline_blocking_helper), view.getText());
|
assertEquals(mContext.getString(R.string.inline_blocking_helper), view.getText());
|
||||||
@@ -385,6 +396,27 @@ public class NotificationInfoTest extends SysuiTestCase {
|
|||||||
assertEquals(IMPORTANCE_UNSPECIFIED, mNotificationChannel.getImportance());
|
assertEquals(IMPORTANCE_UNSPECIFIED, mNotificationChannel.getImportance());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCloseControls_blockingHelperDismissedIfShown() throws Exception {
|
||||||
|
mNotificationInfo.bindNotification(
|
||||||
|
mMockPackageManager,
|
||||||
|
mMockINotificationManager,
|
||||||
|
TEST_PACKAGE_NAME,
|
||||||
|
mNotificationChannel,
|
||||||
|
1 /* numChannels */,
|
||||||
|
mSbn,
|
||||||
|
null /* checkSaveListener */,
|
||||||
|
null /* onSettingsClick */,
|
||||||
|
null /* onAppSettingsClick */,
|
||||||
|
null /* nonBlockablePkgs */,
|
||||||
|
true /* isForBlockingHelper */,
|
||||||
|
false /* isUserSentimentNegative */);
|
||||||
|
|
||||||
|
mNotificationInfo.closeControls(mNotificationInfo);
|
||||||
|
|
||||||
|
verify(mBlockingHelperManager).dismissCurrentBlockingHelper();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNonBlockableAppDoesNotBecomeBlocked() throws Exception {
|
public void testNonBlockableAppDoesNotBecomeBlocked() throws Exception {
|
||||||
mNotificationChannel.setImportance(IMPORTANCE_LOW);
|
mNotificationChannel.setImportance(IMPORTANCE_LOW);
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.android.systemui.statusbar;
|
package com.android.systemui.statusbar;
|
||||||
|
|
||||||
|
import android.annotation.Nullable;
|
||||||
import android.app.ActivityManager;
|
import android.app.ActivityManager;
|
||||||
import android.app.Instrumentation;
|
import android.app.Instrumentation;
|
||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
@@ -23,33 +24,32 @@ import android.content.Context;
|
|||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.service.notification.StatusBarNotification;
|
import android.service.notification.StatusBarNotification;
|
||||||
import android.support.test.InstrumentationRegistry;
|
import android.support.test.InstrumentationRegistry;
|
||||||
|
import android.text.TextUtils;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.widget.FrameLayout;
|
|
||||||
import android.widget.RemoteViews;
|
import android.widget.RemoteViews;
|
||||||
|
|
||||||
import com.android.systemui.R;
|
import com.android.systemui.R;
|
||||||
import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
|
|
||||||
import com.android.systemui.statusbar.notification.AboveShelfObserver;
|
|
||||||
import com.android.systemui.statusbar.notification.InflationException;
|
|
||||||
import com.android.systemui.statusbar.notification.NotificationInflaterTest;
|
import com.android.systemui.statusbar.notification.NotificationInflaterTest;
|
||||||
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
|
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
|
||||||
import com.android.systemui.statusbar.phone.NotificationGroupManager;
|
import com.android.systemui.statusbar.phone.NotificationGroupManager;
|
||||||
import com.android.systemui.statusbar.policy.HeadsUpManager;
|
import com.android.systemui.statusbar.policy.HeadsUpManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A helper class to create {@link ExpandableNotificationRow}
|
* A helper class to create {@link ExpandableNotificationRow} (for both individual and group
|
||||||
|
* notifications).
|
||||||
*/
|
*/
|
||||||
public class NotificationTestHelper {
|
public class NotificationTestHelper {
|
||||||
|
|
||||||
|
static final String PKG = "com.android.systemui";
|
||||||
|
static final int UID = 1000;
|
||||||
|
private static final String GROUP_KEY = "gruKey";
|
||||||
|
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
private final Instrumentation mInstrumentation;
|
private final Instrumentation mInstrumentation;
|
||||||
private int mId;
|
private int mId;
|
||||||
private final NotificationGroupManager mGroupManager = new NotificationGroupManager();
|
private final NotificationGroupManager mGroupManager = new NotificationGroupManager();
|
||||||
private ExpandableNotificationRow mRow;
|
private ExpandableNotificationRow mRow;
|
||||||
private InflationException mException;
|
|
||||||
private HeadsUpManager mHeadsUpManager;
|
private HeadsUpManager mHeadsUpManager;
|
||||||
protected static final String PKG = "com.android.systemui";
|
|
||||||
protected static final int UID = 1000;
|
|
||||||
|
|
||||||
public NotificationTestHelper(Context context) {
|
public NotificationTestHelper(Context context) {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
@@ -57,57 +57,125 @@ public class NotificationTestHelper {
|
|||||||
mHeadsUpManager = new HeadsUpManagerPhone(mContext, null, mGroupManager, null, null);
|
mHeadsUpManager = new HeadsUpManagerPhone(mContext, null, mGroupManager, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ExpandableNotificationRow createRow() throws Exception {
|
||||||
|
return createRow(PKG, UID);
|
||||||
|
}
|
||||||
|
|
||||||
public ExpandableNotificationRow createRow(String pkg, int uid) throws Exception {
|
public ExpandableNotificationRow createRow(String pkg, int uid) throws Exception {
|
||||||
|
return createRow(pkg, uid, false /* isGroupSummary */, null /* groupKey */);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExpandableNotificationRow createRow(Notification notification) throws Exception {
|
||||||
|
return generateRow(notification, PKG, UID, false /* isGroupRow */);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an {@link ExpandableNotificationRow} group with the given number of child
|
||||||
|
* notifications.
|
||||||
|
*/
|
||||||
|
public ExpandableNotificationRow createGroup(int numChildren) throws Exception {
|
||||||
|
ExpandableNotificationRow row = createGroupSummary(GROUP_KEY);
|
||||||
|
for (int i = 0; i < numChildren; i++) {
|
||||||
|
ExpandableNotificationRow childRow = createGroupChild(GROUP_KEY);
|
||||||
|
row.addChildNotification(childRow);
|
||||||
|
}
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns a group notification with 2 child notifications. */
|
||||||
|
public ExpandableNotificationRow createGroup() throws Exception {
|
||||||
|
return createGroup(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExpandableNotificationRow createGroupSummary(String groupkey) throws Exception {
|
||||||
|
return createRow(PKG, UID, true /* isGroupSummary */, groupkey);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExpandableNotificationRow createGroupChild(String groupkey) throws Exception {
|
||||||
|
return createRow(PKG, UID, false /* isGroupSummary */, groupkey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a notification row with the given details.
|
||||||
|
*
|
||||||
|
* @param pkg package used for creating a {@link StatusBarNotification}
|
||||||
|
* @param uid uid used for creating a {@link StatusBarNotification}
|
||||||
|
* @param isGroupSummary whether the notification row is a group summary
|
||||||
|
* @param groupKey the group key for the notification group used across notifications
|
||||||
|
* @return a row with that's either a standalone notification or a group notification if the
|
||||||
|
* groupKey is non-null
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
private ExpandableNotificationRow createRow(
|
||||||
|
String pkg,
|
||||||
|
int uid,
|
||||||
|
boolean isGroupSummary,
|
||||||
|
@Nullable String groupKey)
|
||||||
|
throws Exception {
|
||||||
Notification publicVersion = new Notification.Builder(mContext).setSmallIcon(
|
Notification publicVersion = new Notification.Builder(mContext).setSmallIcon(
|
||||||
R.drawable.ic_person)
|
R.drawable.ic_person)
|
||||||
.setCustomContentView(new RemoteViews(mContext.getPackageName(),
|
.setCustomContentView(new RemoteViews(mContext.getPackageName(),
|
||||||
R.layout.custom_view_dark))
|
R.layout.custom_view_dark))
|
||||||
.build();
|
.build();
|
||||||
Notification notification = new Notification.Builder(mContext).setSmallIcon(
|
Notification.Builder notificationBuilder =
|
||||||
R.drawable.ic_person)
|
new Notification.Builder(mContext)
|
||||||
.setContentTitle("Title")
|
.setSmallIcon(R.drawable.ic_person)
|
||||||
.setContentText("Text")
|
.setContentTitle("Title")
|
||||||
.setPublicVersion(publicVersion)
|
.setContentText("Text")
|
||||||
.build();
|
.setPublicVersion(publicVersion);
|
||||||
return createRow(notification, pkg, uid);
|
|
||||||
|
// Group notification setup
|
||||||
|
if (isGroupSummary) {
|
||||||
|
notificationBuilder.setGroupSummary(true);
|
||||||
|
}
|
||||||
|
if (!TextUtils.isEmpty(groupKey)) {
|
||||||
|
notificationBuilder.setGroup(groupKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
return generateRow(notificationBuilder.build(), pkg, uid, !TextUtils.isEmpty(groupKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ExpandableNotificationRow createRow() throws Exception {
|
private ExpandableNotificationRow generateRow(
|
||||||
return createRow(PKG, UID);
|
Notification notification,
|
||||||
}
|
String pkg,
|
||||||
|
int uid,
|
||||||
public ExpandableNotificationRow createRow(Notification notification) throws Exception {
|
boolean isGroupRow)
|
||||||
return createRow(notification, PKG, UID);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ExpandableNotificationRow createRow(Notification notification, String pkg, int uid)
|
|
||||||
throws Exception {
|
throws Exception {
|
||||||
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
|
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
|
||||||
mContext.LAYOUT_INFLATER_SERVICE);
|
mContext.LAYOUT_INFLATER_SERVICE);
|
||||||
mInstrumentation.runOnMainSync(() -> {
|
mInstrumentation.runOnMainSync(() ->
|
||||||
mRow = (ExpandableNotificationRow) inflater.inflate(
|
mRow = (ExpandableNotificationRow) inflater.inflate(
|
||||||
R.layout.status_bar_notification_row,
|
R.layout.status_bar_notification_row,
|
||||||
null, false);
|
null /* root */,
|
||||||
});
|
false /* attachToRoot */)
|
||||||
|
);
|
||||||
ExpandableNotificationRow row = mRow;
|
ExpandableNotificationRow row = mRow;
|
||||||
row.setGroupManager(mGroupManager);
|
row.setGroupManager(mGroupManager);
|
||||||
row.setHeadsUpManager(mHeadsUpManager);
|
row.setHeadsUpManager(mHeadsUpManager);
|
||||||
row.setAboveShelfChangedListener(aboveShelf -> {});
|
row.setAboveShelfChangedListener(aboveShelf -> {});
|
||||||
UserHandle mUser = UserHandle.of(ActivityManager.getCurrentUser());
|
UserHandle mUser = UserHandle.of(ActivityManager.getCurrentUser());
|
||||||
StatusBarNotification sbn = new StatusBarNotification(pkg, pkg, mId++, null, uid,
|
StatusBarNotification sbn = new StatusBarNotification(
|
||||||
2000, notification, mUser, null, System.currentTimeMillis());
|
pkg,
|
||||||
|
pkg,
|
||||||
|
mId++,
|
||||||
|
null /* tag */,
|
||||||
|
uid,
|
||||||
|
2000 /* initialPid */,
|
||||||
|
notification,
|
||||||
|
mUser,
|
||||||
|
null /* overrideGroupKey */,
|
||||||
|
System.currentTimeMillis());
|
||||||
NotificationData.Entry entry = new NotificationData.Entry(sbn);
|
NotificationData.Entry entry = new NotificationData.Entry(sbn);
|
||||||
entry.row = row;
|
entry.row = row;
|
||||||
entry.createIcons(mContext, sbn);
|
entry.createIcons(mContext, sbn);
|
||||||
NotificationInflaterTest.runThenWaitForInflation(() -> row.updateNotification(entry),
|
NotificationInflaterTest.runThenWaitForInflation(
|
||||||
|
() -> row.updateNotification(entry),
|
||||||
row.getNotificationInflater());
|
row.getNotificationInflater());
|
||||||
return row;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ExpandableNotificationRow createGroup() throws Exception {
|
// This would be done as part of onAsyncInflationFinished, but we skip large amounts of
|
||||||
ExpandableNotificationRow row = createRow();
|
// the callback chain, so we need to make up for not adding it to the group manager
|
||||||
row.addChildNotification(createRow());
|
// here.
|
||||||
row.addChildNotification(createRow());
|
mGroupManager.onEntryAdded(entry);
|
||||||
return row;
|
return row;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,12 @@
|
|||||||
|
|
||||||
package com.android.systemui.statusbar.stack;
|
package com.android.systemui.statusbar.stack;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||||
|
import static org.mockito.Mockito.doNothing;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.reset;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
@@ -25,33 +30,73 @@ import android.support.test.filters.SmallTest;
|
|||||||
import android.support.test.runner.AndroidJUnit4;
|
import android.support.test.runner.AndroidJUnit4;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
|
import com.android.systemui.ExpandHelper;
|
||||||
import com.android.systemui.R;
|
import com.android.systemui.R;
|
||||||
import com.android.systemui.SysuiTestCase;
|
import com.android.systemui.SysuiTestCase;
|
||||||
|
import com.android.systemui.TestableDependency;
|
||||||
import com.android.systemui.statusbar.EmptyShadeView;
|
import com.android.systemui.statusbar.EmptyShadeView;
|
||||||
import com.android.systemui.statusbar.FooterView;
|
import com.android.systemui.statusbar.FooterView;
|
||||||
|
import com.android.systemui.statusbar.NotificationBlockingHelperManager;
|
||||||
|
import com.android.systemui.statusbar.NotificationShelf;
|
||||||
import com.android.systemui.statusbar.StatusBarState;
|
import com.android.systemui.statusbar.StatusBarState;
|
||||||
|
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
|
||||||
|
import com.android.systemui.statusbar.phone.NotificationGroupManager;
|
||||||
import com.android.systemui.statusbar.phone.ScrimController;
|
import com.android.systemui.statusbar.phone.ScrimController;
|
||||||
import com.android.systemui.statusbar.phone.StatusBar;
|
import com.android.systemui.statusbar.phone.StatusBar;
|
||||||
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.MockitoJUnit;
|
||||||
|
import org.mockito.junit.MockitoRule;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link NotificationStackScrollLayout}.
|
||||||
|
*/
|
||||||
@SmallTest
|
@SmallTest
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class NotificationStackScrollLayoutTest extends SysuiTestCase {
|
public class NotificationStackScrollLayoutTest extends SysuiTestCase {
|
||||||
|
|
||||||
private NotificationStackScrollLayout mStackScroller;
|
private NotificationStackScrollLayout mStackScroller;
|
||||||
private StatusBar mBar;
|
|
||||||
|
@Rule public MockitoRule mockito = MockitoJUnit.rule();
|
||||||
|
@Mock private StatusBar mBar;
|
||||||
|
@Mock private HeadsUpManagerPhone mHeadsUpManager;
|
||||||
|
@Mock private NotificationBlockingHelperManager mBlockingHelperManager;
|
||||||
|
@Mock private NotificationGroupManager mGroupManager;
|
||||||
|
@Mock private ExpandHelper mExpandHelper;
|
||||||
|
@Mock private EmptyShadeView mEmptyShadeView;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
@UiThreadTest
|
@UiThreadTest
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
|
// Inject dependencies before initializing the layout
|
||||||
|
mDependency.injectTestDependency(
|
||||||
|
NotificationBlockingHelperManager.class,
|
||||||
|
mBlockingHelperManager);
|
||||||
|
|
||||||
|
NotificationShelf notificationShelf = spy(new NotificationShelf(getContext(), null));
|
||||||
mStackScroller = new NotificationStackScrollLayout(getContext());
|
mStackScroller = new NotificationStackScrollLayout(getContext());
|
||||||
mBar = mock(StatusBar.class);
|
mStackScroller.setShelf(notificationShelf);
|
||||||
mStackScroller.setStatusBar(mBar);
|
mStackScroller.setStatusBar(mBar);
|
||||||
mStackScroller.setScrimController(mock(ScrimController.class));
|
mStackScroller.setScrimController(mock(ScrimController.class));
|
||||||
|
mStackScroller.setHeadsUpManager(mHeadsUpManager);
|
||||||
|
mStackScroller.setGroupManager(mGroupManager);
|
||||||
|
mStackScroller.setEmptyShadeView(mEmptyShadeView);
|
||||||
|
|
||||||
|
// Stub out functionality that isn't necessary to test.
|
||||||
|
doNothing().when(mBar)
|
||||||
|
.executeRunnableDismissingKeyguard(any(Runnable.class),
|
||||||
|
any(Runnable.class),
|
||||||
|
anyBoolean(),
|
||||||
|
anyBoolean(),
|
||||||
|
anyBoolean());
|
||||||
|
doNothing().when(mGroupManager).collapseAllGroups();
|
||||||
|
doNothing().when(mExpandHelper).cancelImmediately();
|
||||||
|
doNothing().when(notificationShelf).setAnimationsEnabled(anyBoolean());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -75,26 +120,34 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void updateEmptyView_dndSuppressing() {
|
public void updateEmptyView_dndSuppressing() {
|
||||||
EmptyShadeView view = mock(EmptyShadeView.class);
|
when(mEmptyShadeView.willBeGone()).thenReturn(true);
|
||||||
mStackScroller.setEmptyShadeView(view);
|
|
||||||
when(view.willBeGone()).thenReturn(true);
|
|
||||||
when(mBar.areNotificationsHidden()).thenReturn(true);
|
when(mBar.areNotificationsHidden()).thenReturn(true);
|
||||||
|
|
||||||
mStackScroller.updateEmptyShadeView(true);
|
mStackScroller.updateEmptyShadeView(true);
|
||||||
|
|
||||||
verify(view).setText(R.string.dnd_suppressing_shade_text);
|
verify(mEmptyShadeView).setText(R.string.dnd_suppressing_shade_text);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void updateEmptyView_dndNotSuppressing() {
|
public void updateEmptyView_dndNotSuppressing() {
|
||||||
EmptyShadeView view = mock(EmptyShadeView.class);
|
mStackScroller.setEmptyShadeView(mEmptyShadeView);
|
||||||
mStackScroller.setEmptyShadeView(view);
|
when(mEmptyShadeView.willBeGone()).thenReturn(true);
|
||||||
when(view.willBeGone()).thenReturn(true);
|
|
||||||
when(mBar.areNotificationsHidden()).thenReturn(false);
|
when(mBar.areNotificationsHidden()).thenReturn(false);
|
||||||
|
|
||||||
mStackScroller.updateEmptyShadeView(true);
|
mStackScroller.updateEmptyShadeView(true);
|
||||||
|
|
||||||
verify(view).setText(R.string.empty_shade_text);
|
verify(mEmptyShadeView).setText(R.string.empty_shade_text);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@UiThreadTest
|
||||||
|
public void testSetExpandedHeight_blockingHelperManagerReceivedCallbacks() {
|
||||||
|
mStackScroller.setExpandedHeight(0f);
|
||||||
|
verify(mBlockingHelperManager).setNotificationShadeExpanded(0f);
|
||||||
|
reset(mBlockingHelperManager);
|
||||||
|
|
||||||
|
mStackScroller.setExpandedHeight(100f);
|
||||||
|
verify(mBlockingHelperManager).setNotificationShadeExpanded(100f);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user