[Notif] Blocking helper basic metrics
Adding first pass of counters for basic actions taken with the blocking helper. This includes: - All notification dismissal events - Shown event - Dismissal via outside interaction - Any button taps to dismiss - Undo taps Test: Via manual testing & updated tests (+added two new) Bug: 74609669 Change-Id: I0adcfe03cfd10809ef7a2f13afbefcb420246a43
This commit is contained in:
@@ -76,6 +76,7 @@ import com.android.systemui.statusbar.NotificationGuts.GutsContent;
|
||||
import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
|
||||
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
|
||||
import com.android.systemui.statusbar.notification.HybridNotificationView;
|
||||
import com.android.systemui.statusbar.notification.NotificationCounters;
|
||||
import com.android.systemui.statusbar.notification.NotificationInflater;
|
||||
import com.android.systemui.statusbar.notification.NotificationUtils;
|
||||
import com.android.systemui.statusbar.notification.NotificationViewWrapper;
|
||||
@@ -1252,6 +1253,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
||||
Dependency.get(NotificationBlockingHelperManager.class);
|
||||
boolean isBlockingHelperShown = manager.perhapsShowBlockingHelper(this, mMenuRow);
|
||||
|
||||
Dependency.get(MetricsLogger.class).count(NotificationCounters.NOTIFICATION_DISMISSED, 1);
|
||||
|
||||
// Continue with dismiss since we don't want the blocking helper to be directly associated
|
||||
// with a certain notification.
|
||||
performDismiss(fromAccessibility);
|
||||
|
||||
@@ -30,6 +30,7 @@ import android.view.ViewAnimationUtils;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.systemui.Dependency;
|
||||
import com.android.systemui.Interpolators;
|
||||
import com.android.systemui.R;
|
||||
@@ -270,7 +271,8 @@ public class NotificationGuts extends FrameLayout {
|
||||
|
||||
|
||||
/** Animates out the guts view via either a fade or a circular reveal. */
|
||||
private void animateClose(int x, int y, boolean shouldDoCircularReveal) {
|
||||
@VisibleForTesting
|
||||
void animateClose(int x, int y, boolean shouldDoCircularReveal) {
|
||||
if (shouldDoCircularReveal) {
|
||||
// Circular reveal originating at (x, y)
|
||||
if (x == -1 || y == -1) {
|
||||
@@ -340,7 +342,8 @@ public class NotificationGuts extends FrameLayout {
|
||||
}
|
||||
}
|
||||
|
||||
private void setExposed(boolean exposed, boolean needsFalsingProtection) {
|
||||
@VisibleForTesting
|
||||
void setExposed(boolean exposed, boolean needsFalsingProtection) {
|
||||
final boolean wasExposed = mExposed;
|
||||
mExposed = exposed;
|
||||
mNeedsFalsingProtection = needsFalsingProtection;
|
||||
|
||||
@@ -54,17 +54,20 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
||||
import com.android.systemui.Dependency;
|
||||
import com.android.systemui.Interpolators;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.statusbar.notification.NotificationCounters;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The guts of a notification revealed when performing a long press.
|
||||
* The guts of a notification revealed when performing a long press. This also houses the blocking
|
||||
* helper affordance that allows a user to keep/stop notifications after swiping one away.
|
||||
*/
|
||||
public class NotificationInfo extends LinearLayout implements NotificationGuts.GutsContent {
|
||||
private static final String TAG = "InfoGuts";
|
||||
|
||||
private INotificationManager mINotificationManager;
|
||||
private PackageManager mPm;
|
||||
private MetricsLogger mMetricsLogger;
|
||||
|
||||
private String mPackageName;
|
||||
private String mAppName;
|
||||
@@ -84,17 +87,27 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
|
||||
private OnAppSettingsClickListener mAppSettingsClickListener;
|
||||
private NotificationGuts mGutsContainer;
|
||||
|
||||
/** Whether this view is being shown as part of the blocking helper */
|
||||
/** Whether this view is being shown as part of the blocking helper. */
|
||||
private boolean mIsForBlockingHelper;
|
||||
private boolean mNegativeUserSentiment;
|
||||
|
||||
private OnClickListener mOnKeepShowing = this::closeControls;
|
||||
/** Counter tag that describes how the user exit or quit out of this view. */
|
||||
private String mExitReasonCounter = NotificationCounters.BLOCKING_HELPER_DISMISSED;
|
||||
|
||||
private OnClickListener mOnKeepShowing = v -> {
|
||||
mExitReasonCounter = NotificationCounters.BLOCKING_HELPER_KEEP_SHOWING;
|
||||
closeControls(v);
|
||||
};
|
||||
|
||||
private OnClickListener mOnStopOrMinimizeNotifications = v -> {
|
||||
mExitReasonCounter = NotificationCounters.BLOCKING_HELPER_STOP_NOTIFICATIONS;
|
||||
swapContent(false);
|
||||
};
|
||||
|
||||
private OnClickListener mOnUndo = v -> {
|
||||
// Reset exit counter that we'll log and record an undo event separately (not an exit event)
|
||||
mExitReasonCounter = NotificationCounters.BLOCKING_HELPER_DISMISSED;
|
||||
logBlockingHelperCounter(NotificationCounters.BLOCKING_HELPER_UNDO);
|
||||
swapContent(true);
|
||||
};
|
||||
|
||||
@@ -151,6 +164,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
|
||||
boolean isUserSentimentNegative)
|
||||
throws RemoteException {
|
||||
mINotificationManager = iNotificationManager;
|
||||
mMetricsLogger = Dependency.get(MetricsLogger.class);
|
||||
mPackageName = pkg;
|
||||
mNumUniqueChannelsInRow = numUniqueChannelsInRow;
|
||||
mSbn = sbn;
|
||||
@@ -183,6 +197,8 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
|
||||
bindHeader();
|
||||
bindPrompt();
|
||||
bindButtons();
|
||||
|
||||
logBlockingHelperCounter(NotificationCounters.BLOCKING_HELPER_SHOWN);
|
||||
}
|
||||
|
||||
private void bindHeader() throws RemoteException {
|
||||
@@ -235,6 +251,8 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
|
||||
final int appUidF = mAppUid;
|
||||
settingsButton.setOnClickListener(
|
||||
(View view) -> {
|
||||
logBlockingHelperCounter(
|
||||
NotificationCounters.BLOCKING_HELPER_NOTIF_SETTINGS);
|
||||
mOnSettingsClickListener.onClick(view,
|
||||
mNumUniqueChannelsInRow > 1 ? null : mSingleNotificationChannel,
|
||||
appUidF);
|
||||
@@ -269,6 +287,13 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void logBlockingHelperCounter(String counterTag) {
|
||||
if (mIsForBlockingHelper) {
|
||||
mMetricsLogger.count(counterTag, 1);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasImportanceChanged() {
|
||||
return mSingleNotificationChannel != null && mStartingUserImportance != mChosenImportance;
|
||||
}
|
||||
@@ -437,25 +462,15 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
|
||||
*/
|
||||
@VisibleForTesting
|
||||
void closeControls(View v) {
|
||||
if (mIsForBlockingHelper) {
|
||||
NotificationBlockingHelperManager manager =
|
||||
Dependency.get(NotificationBlockingHelperManager.class);
|
||||
manager.dismissCurrentBlockingHelper();
|
||||
|
||||
// Since this won't get a callback via gutsContainer.closeControls, save the new
|
||||
// importance values immediately.
|
||||
saveImportance();
|
||||
} else {
|
||||
int[] parentLoc = new int[2];
|
||||
int[] targetLoc = new int[2];
|
||||
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 */);
|
||||
}
|
||||
int[] parentLoc = new int[2];
|
||||
int[] targetLoc = new int[2];
|
||||
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
|
||||
@@ -480,6 +495,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
|
||||
if (save) {
|
||||
saveImportance();
|
||||
}
|
||||
logBlockingHelperCounter(mExitReasonCounter);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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.notification;
|
||||
|
||||
/**
|
||||
* Constants for counter tags for Notification-related actions/views.
|
||||
*/
|
||||
public class NotificationCounters {
|
||||
/** Counter tag for notification dismissal. */
|
||||
public static final String NOTIFICATION_DISMISSED = "notification_dismissed";
|
||||
|
||||
/** Counter tag for when the blocking helper is shown to the user. */
|
||||
public static final String BLOCKING_HELPER_SHOWN = "blocking_helper_shown";
|
||||
/** Counter tag for when the blocking helper is dismissed via a miscellaneous interaction. */
|
||||
public static final String BLOCKING_HELPER_DISMISSED = "blocking_helper_dismissed";
|
||||
/** Counter tag for when the user hits 'stop notifications' in the blocking helper. */
|
||||
public static final String BLOCKING_HELPER_STOP_NOTIFICATIONS =
|
||||
"blocking_helper_stop_notifications";
|
||||
/** Counter tag for when the user hits 'keep showing' in the blocking helper. */
|
||||
public static final String BLOCKING_HELPER_KEEP_SHOWING =
|
||||
"blocking_helper_keep_showing";
|
||||
/**
|
||||
* Counter tag for when the user hits undo in context of the blocking helper - this can happen
|
||||
* multiple times per view.
|
||||
*/
|
||||
public static final String BLOCKING_HELPER_UNDO = "blocking_helper_undo";
|
||||
/** Counter tag for when the user hits the notification settings icon in the blocking helper. */
|
||||
public static final String BLOCKING_HELPER_NOTIF_SETTINGS =
|
||||
"blocking_helper_notif_settings";
|
||||
}
|
||||
@@ -33,9 +33,12 @@ import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.anyBoolean;
|
||||
import static org.mockito.Mockito.anyInt;
|
||||
import static org.mockito.Mockito.anyString;
|
||||
import static org.mockito.Mockito.doCallRealMethod;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -44,7 +47,6 @@ import android.app.INotificationManager;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationChannelGroup;
|
||||
import android.app.NotificationManager;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
@@ -52,7 +54,7 @@ import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Looper;
|
||||
import android.os.IBinder;
|
||||
import android.os.UserHandle;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
@@ -65,6 +67,7 @@ import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.internal.logging.MetricsLogger;
|
||||
import com.android.systemui.Dependency;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
@@ -100,7 +103,7 @@ public class NotificationInfoTest extends SysuiTestCase {
|
||||
private StatusBarNotification mSbn;
|
||||
|
||||
@Rule public MockitoRule mockito = MockitoJUnit.rule();
|
||||
private Looper mLooper;
|
||||
@Mock private MetricsLogger mMetricsLogger;
|
||||
@Mock private INotificationManager mMockINotificationManager;
|
||||
@Mock private PackageManager mMockPackageManager;
|
||||
@Mock private NotificationBlockingHelperManager mBlockingHelperManager;
|
||||
@@ -112,6 +115,7 @@ public class NotificationInfoTest extends SysuiTestCase {
|
||||
mBlockingHelperManager);
|
||||
mTestableLooper = TestableLooper.get(this);
|
||||
mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper());
|
||||
mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
|
||||
// Inflate the layout
|
||||
final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
|
||||
mNotificationInfo = (NotificationInfo) layoutInflater.inflate(R.layout.notification_info,
|
||||
@@ -300,6 +304,24 @@ public class NotificationInfoTest extends SysuiTestCase {
|
||||
assertEquals(View.VISIBLE, settingsButton.getVisibility());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLogBlockingHelperCounter_doesntLogForNormalGutsView() throws Exception {
|
||||
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
|
||||
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
|
||||
mNotificationInfo.logBlockingHelperCounter("HowCanNotifsBeRealIfAppsArent");
|
||||
verify(mMetricsLogger, times(0)).count(anyString(), anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLogBlockingHelperCounter_logsForBlockingHelper() throws Exception {
|
||||
// Bind notification logs an event, so this counts as one invocation for the metrics logger.
|
||||
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
|
||||
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false, true,
|
||||
true);
|
||||
mNotificationInfo.logBlockingHelperCounter("HowCanNotifsBeRealIfAppsArent");
|
||||
verify(mMetricsLogger, times(2)).count(anyString(), anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnClickListenerPassesNullChannelForBundle() throws Exception {
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
@@ -471,6 +493,13 @@ public class NotificationInfoTest extends SysuiTestCase {
|
||||
false /* isNonblockable */, true /* isForBlockingHelper */,
|
||||
true /* isUserSentimentNegative */);
|
||||
|
||||
NotificationGuts guts = spy(new NotificationGuts(mContext, null));
|
||||
when(guts.getWindowToken()).thenReturn(mock(IBinder.class));
|
||||
doNothing().when(guts).animateClose(anyInt(), anyInt(), anyBoolean());
|
||||
doNothing().when(guts).setExposed(anyBoolean(), anyBoolean());
|
||||
guts.setGutsContent(mNotificationInfo);
|
||||
mNotificationInfo.setGutsParent(guts);
|
||||
|
||||
mNotificationInfo.findViewById(R.id.keep).performClick();
|
||||
|
||||
verify(mBlockingHelperManager).dismissCurrentBlockingHelper();
|
||||
@@ -495,6 +524,9 @@ public class NotificationInfoTest extends SysuiTestCase {
|
||||
false /* isNonblockable */,
|
||||
true /* isForBlockingHelper */,
|
||||
false /* isUserSentimentNegative */);
|
||||
NotificationGuts guts = mock(NotificationGuts.class);
|
||||
doCallRealMethod().when(guts).closeControls(anyInt(), anyInt(), anyBoolean(), anyBoolean());
|
||||
mNotificationInfo.setGutsParent(guts);
|
||||
|
||||
mNotificationInfo.closeControls(mNotificationInfo);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user