Merge "Remove app ops indicators from notifications" into rvc-qpr-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
2659836775
@@ -5151,18 +5151,10 @@ public class Notification implements Parcelable
|
|||||||
bindHeaderChronometerAndTime(contentView, p);
|
bindHeaderChronometerAndTime(contentView, p);
|
||||||
bindProfileBadge(contentView, p);
|
bindProfileBadge(contentView, p);
|
||||||
bindAlertedIcon(contentView, p);
|
bindAlertedIcon(contentView, p);
|
||||||
bindActivePermissions(contentView, p);
|
|
||||||
bindExpandButton(contentView, p);
|
bindExpandButton(contentView, p);
|
||||||
mN.mUsesStandardHeader = true;
|
mN.mUsesStandardHeader = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void bindActivePermissions(RemoteViews contentView, StandardTemplateParams p) {
|
|
||||||
int color = getNeutralColor(p);
|
|
||||||
contentView.setDrawableTint(R.id.camera, false, color, PorterDuff.Mode.SRC_ATOP);
|
|
||||||
contentView.setDrawableTint(R.id.mic, false, color, PorterDuff.Mode.SRC_ATOP);
|
|
||||||
contentView.setDrawableTint(R.id.overlay, false, color, PorterDuff.Mode.SRC_ATOP);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void bindExpandButton(RemoteViews contentView, StandardTemplateParams p) {
|
private void bindExpandButton(RemoteViews contentView, StandardTemplateParams p) {
|
||||||
int color = isColorized(p) ? getPrimaryTextColor(p) : getSecondaryTextColor(p);
|
int color = isColorized(p) ? getPrimaryTextColor(p) : getSecondaryTextColor(p);
|
||||||
contentView.setDrawableTint(R.id.expand_button, false, color,
|
contentView.setDrawableTint(R.id.expand_button, false, color,
|
||||||
|
|||||||
@@ -52,13 +52,11 @@ public class NotificationHeaderView extends ViewGroup {
|
|||||||
private View mHeaderText;
|
private View mHeaderText;
|
||||||
private View mSecondaryHeaderText;
|
private View mSecondaryHeaderText;
|
||||||
private OnClickListener mExpandClickListener;
|
private OnClickListener mExpandClickListener;
|
||||||
private OnClickListener mAppOpsListener;
|
|
||||||
private HeaderTouchListener mTouchListener = new HeaderTouchListener();
|
private HeaderTouchListener mTouchListener = new HeaderTouchListener();
|
||||||
private LinearLayout mTransferChip;
|
private LinearLayout mTransferChip;
|
||||||
private NotificationExpandButton mExpandButton;
|
private NotificationExpandButton mExpandButton;
|
||||||
private CachingIconView mIcon;
|
private CachingIconView mIcon;
|
||||||
private View mProfileBadge;
|
private View mProfileBadge;
|
||||||
private View mAppOps;
|
|
||||||
private boolean mExpanded;
|
private boolean mExpanded;
|
||||||
private boolean mShowExpandButtonAtEnd;
|
private boolean mShowExpandButtonAtEnd;
|
||||||
private boolean mShowWorkBadgeAtEnd;
|
private boolean mShowWorkBadgeAtEnd;
|
||||||
@@ -115,7 +113,6 @@ public class NotificationHeaderView extends ViewGroup {
|
|||||||
mExpandButton = findViewById(com.android.internal.R.id.expand_button);
|
mExpandButton = findViewById(com.android.internal.R.id.expand_button);
|
||||||
mIcon = findViewById(com.android.internal.R.id.icon);
|
mIcon = findViewById(com.android.internal.R.id.icon);
|
||||||
mProfileBadge = findViewById(com.android.internal.R.id.profile_badge);
|
mProfileBadge = findViewById(com.android.internal.R.id.profile_badge);
|
||||||
mAppOps = findViewById(com.android.internal.R.id.app_ops);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -143,7 +140,6 @@ public class NotificationHeaderView extends ViewGroup {
|
|||||||
// Icons that should go at the end
|
// Icons that should go at the end
|
||||||
if ((child == mExpandButton && mShowExpandButtonAtEnd)
|
if ((child == mExpandButton && mShowExpandButtonAtEnd)
|
||||||
|| child == mProfileBadge
|
|| child == mProfileBadge
|
||||||
|| child == mAppOps
|
|
||||||
|| child == mTransferChip) {
|
|| child == mTransferChip) {
|
||||||
iconWidth += lp.leftMargin + lp.rightMargin + child.getMeasuredWidth();
|
iconWidth += lp.leftMargin + lp.rightMargin + child.getMeasuredWidth();
|
||||||
} else {
|
} else {
|
||||||
@@ -208,7 +204,6 @@ public class NotificationHeaderView extends ViewGroup {
|
|||||||
// Icons that should go at the end
|
// Icons that should go at the end
|
||||||
if ((child == mExpandButton && mShowExpandButtonAtEnd)
|
if ((child == mExpandButton && mShowExpandButtonAtEnd)
|
||||||
|| child == mProfileBadge
|
|| child == mProfileBadge
|
||||||
|| child == mAppOps
|
|
||||||
|| child == mTransferChip) {
|
|| child == mTransferChip) {
|
||||||
if (end == getMeasuredWidth()) {
|
if (end == getMeasuredWidth()) {
|
||||||
layoutRight = end - mContentEndMargin;
|
layoutRight = end - mContentEndMargin;
|
||||||
@@ -277,22 +272,10 @@ public class NotificationHeaderView extends ViewGroup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateTouchListener() {
|
private void updateTouchListener() {
|
||||||
if (mExpandClickListener == null && mAppOpsListener == null) {
|
|
||||||
setOnTouchListener(null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setOnTouchListener(mTouchListener);
|
setOnTouchListener(mTouchListener);
|
||||||
mTouchListener.bindTouchRects();
|
mTouchListener.bindTouchRects();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets onclick listener for app ops icons.
|
|
||||||
*/
|
|
||||||
public void setAppOpsOnClickListener(OnClickListener l) {
|
|
||||||
mAppOpsListener = l;
|
|
||||||
updateTouchListener();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setOnClickListener(@Nullable OnClickListener l) {
|
public void setOnClickListener(@Nullable OnClickListener l) {
|
||||||
mExpandClickListener = l;
|
mExpandClickListener = l;
|
||||||
@@ -380,7 +363,6 @@ public class NotificationHeaderView extends ViewGroup {
|
|||||||
|
|
||||||
private final ArrayList<Rect> mTouchRects = new ArrayList<>();
|
private final ArrayList<Rect> mTouchRects = new ArrayList<>();
|
||||||
private Rect mExpandButtonRect;
|
private Rect mExpandButtonRect;
|
||||||
private Rect mAppOpsRect;
|
|
||||||
private int mTouchSlop;
|
private int mTouchSlop;
|
||||||
private boolean mTrackGesture;
|
private boolean mTrackGesture;
|
||||||
private float mDownX;
|
private float mDownX;
|
||||||
@@ -393,8 +375,6 @@ public class NotificationHeaderView extends ViewGroup {
|
|||||||
mTouchRects.clear();
|
mTouchRects.clear();
|
||||||
addRectAroundView(mIcon);
|
addRectAroundView(mIcon);
|
||||||
mExpandButtonRect = addRectAroundView(mExpandButton);
|
mExpandButtonRect = addRectAroundView(mExpandButton);
|
||||||
mAppOpsRect = addRectAroundView(mAppOps);
|
|
||||||
setTouchDelegate(new TouchDelegate(mAppOpsRect, mAppOps));
|
|
||||||
addWidthRect();
|
addWidthRect();
|
||||||
mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
|
mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
|
||||||
}
|
}
|
||||||
@@ -455,11 +435,6 @@ public class NotificationHeaderView extends ViewGroup {
|
|||||||
break;
|
break;
|
||||||
case MotionEvent.ACTION_UP:
|
case MotionEvent.ACTION_UP:
|
||||||
if (mTrackGesture) {
|
if (mTrackGesture) {
|
||||||
if (mAppOps.isVisibleToUser() && (mAppOpsRect.contains((int) x, (int) y)
|
|
||||||
|| mAppOpsRect.contains((int) mDownX, (int) mDownY))) {
|
|
||||||
mAppOps.performClick();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
mExpandButton.performClick();
|
mExpandButton.performClick();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -167,8 +167,6 @@ public class ConversationLayout extends FrameLayout
|
|||||||
private int mFacePileProtectionWidthExpanded;
|
private int mFacePileProtectionWidthExpanded;
|
||||||
private boolean mImportantConversation;
|
private boolean mImportantConversation;
|
||||||
private TextView mUnreadBadge;
|
private TextView mUnreadBadge;
|
||||||
private ViewGroup mAppOps;
|
|
||||||
private Rect mAppOpsTouchRect = new Rect();
|
|
||||||
private float mMinTouchSize;
|
private float mMinTouchSize;
|
||||||
private Icon mConversationIcon;
|
private Icon mConversationIcon;
|
||||||
private Icon mShortcutIcon;
|
private Icon mShortcutIcon;
|
||||||
@@ -210,7 +208,6 @@ public class ConversationLayout extends FrameLayout
|
|||||||
mConversationIconView = findViewById(R.id.conversation_icon);
|
mConversationIconView = findViewById(R.id.conversation_icon);
|
||||||
mConversationIconContainer = findViewById(R.id.conversation_icon_container);
|
mConversationIconContainer = findViewById(R.id.conversation_icon_container);
|
||||||
mIcon = findViewById(R.id.icon);
|
mIcon = findViewById(R.id.icon);
|
||||||
mAppOps = findViewById(com.android.internal.R.id.app_ops);
|
|
||||||
mMinTouchSize = 48 * getResources().getDisplayMetrics().density;
|
mMinTouchSize = 48 * getResources().getDisplayMetrics().density;
|
||||||
mImportanceRingView = findViewById(R.id.conversation_icon_badge_ring);
|
mImportanceRingView = findViewById(R.id.conversation_icon_badge_ring);
|
||||||
mConversationIconBadge = findViewById(R.id.conversation_icon_badge);
|
mConversationIconBadge = findViewById(R.id.conversation_icon_badge);
|
||||||
@@ -1166,47 +1163,6 @@ public class ConversationLayout extends FrameLayout
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (mAppOps.getWidth() > 0) {
|
|
||||||
|
|
||||||
// Let's increase the touch size of the app ops view if it's here
|
|
||||||
mAppOpsTouchRect.set(
|
|
||||||
mAppOps.getLeft(),
|
|
||||||
mAppOps.getTop(),
|
|
||||||
mAppOps.getRight(),
|
|
||||||
mAppOps.getBottom());
|
|
||||||
for (int i = 0; i < mAppOps.getChildCount(); i++) {
|
|
||||||
View child = mAppOps.getChildAt(i);
|
|
||||||
if (child.getVisibility() == GONE) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Make sure each child has at least a minTouchSize touch target around it
|
|
||||||
float childTouchLeft = child.getLeft() + child.getWidth() / 2.0f
|
|
||||||
- mMinTouchSize / 2.0f;
|
|
||||||
float childTouchRight = childTouchLeft + mMinTouchSize;
|
|
||||||
mAppOpsTouchRect.left = (int) Math.min(mAppOpsTouchRect.left,
|
|
||||||
mAppOps.getLeft() + childTouchLeft);
|
|
||||||
mAppOpsTouchRect.right = (int) Math.max(mAppOpsTouchRect.right,
|
|
||||||
mAppOps.getLeft() + childTouchRight);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Increase the height
|
|
||||||
int heightIncrease = 0;
|
|
||||||
if (mAppOpsTouchRect.height() < mMinTouchSize) {
|
|
||||||
heightIncrease = (int) Math.ceil((mMinTouchSize - mAppOpsTouchRect.height())
|
|
||||||
/ 2.0f);
|
|
||||||
}
|
|
||||||
mAppOpsTouchRect.inset(0, -heightIncrease);
|
|
||||||
|
|
||||||
// Let's adjust the hitrect since app ops isn't a direct child
|
|
||||||
ViewGroup viewGroup = (ViewGroup) mAppOps.getParent();
|
|
||||||
while (viewGroup != this) {
|
|
||||||
mAppOpsTouchRect.offset(viewGroup.getLeft(), viewGroup.getTop());
|
|
||||||
viewGroup = (ViewGroup) viewGroup.getParent();
|
|
||||||
}
|
|
||||||
//
|
|
||||||
// Extend the size of the app opps to be at least 48dp
|
|
||||||
setTouchDelegate(new TouchDelegate(mAppOpsTouchRect, mAppOps));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public MessagingLinearLayout getMessagingLinearLayout() {
|
public MessagingLinearLayout getMessagingLinearLayout() {
|
||||||
|
|||||||
@@ -146,43 +146,6 @@
|
|||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
android:contentDescription="@string/notification_work_profile_content_description"
|
android:contentDescription="@string/notification_work_profile_content_description"
|
||||||
/>
|
/>
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/app_ops"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_marginStart="6dp"
|
|
||||||
android:background="?android:selectableItemBackgroundBorderless"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/camera"
|
|
||||||
android:layout_width="?attr/notificationHeaderIconSize"
|
|
||||||
android:layout_height="?attr/notificationHeaderIconSize"
|
|
||||||
android:src="@drawable/ic_camera"
|
|
||||||
android:visibility="gone"
|
|
||||||
android:focusable="false"
|
|
||||||
android:contentDescription="@string/notification_appops_camera_active"
|
|
||||||
/>
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/mic"
|
|
||||||
android:layout_width="?attr/notificationHeaderIconSize"
|
|
||||||
android:layout_height="?attr/notificationHeaderIconSize"
|
|
||||||
android:src="@drawable/ic_mic"
|
|
||||||
android:layout_marginStart="4dp"
|
|
||||||
android:visibility="gone"
|
|
||||||
android:focusable="false"
|
|
||||||
android:contentDescription="@string/notification_appops_microphone_active"
|
|
||||||
/>
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/overlay"
|
|
||||||
android:layout_width="?attr/notificationHeaderIconSize"
|
|
||||||
android:layout_height="?attr/notificationHeaderIconSize"
|
|
||||||
android:src="@drawable/ic_alert_window_layer"
|
|
||||||
android:layout_marginStart="4dp"
|
|
||||||
android:visibility="gone"
|
|
||||||
android:focusable="false"
|
|
||||||
android:contentDescription="@string/notification_appops_overlay_active"
|
|
||||||
/>
|
|
||||||
</LinearLayout>
|
|
||||||
<include
|
<include
|
||||||
layout="@layout/notification_material_media_transfer_action"
|
layout="@layout/notification_material_media_transfer_action"
|
||||||
android:id="@+id/media_seamless"
|
android:id="@+id/media_seamless"
|
||||||
|
|||||||
@@ -74,11 +74,6 @@ public interface NotificationMenuRowPlugin extends Plugin {
|
|||||||
*/
|
*/
|
||||||
public MenuItem getLongpressMenuItem(Context context);
|
public MenuItem getLongpressMenuItem(Context context);
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the {@link MenuItem} to display when app ops icons are pressed.
|
|
||||||
*/
|
|
||||||
public MenuItem getAppOpsMenuItem(Context context);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the {@link MenuItem} to display when snooze item is pressed.
|
* @return the {@link MenuItem} to display when snooze item is pressed.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -39,21 +39,15 @@ import javax.inject.Singleton;
|
|||||||
*/
|
*/
|
||||||
@Singleton
|
@Singleton
|
||||||
public class ForegroundServiceController {
|
public class ForegroundServiceController {
|
||||||
public static final int[] APP_OPS = new int[] {AppOpsManager.OP_CAMERA,
|
public static final int[] APP_OPS = new int[] {AppOpsManager.OP_SYSTEM_ALERT_WINDOW};
|
||||||
AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
|
|
||||||
AppOpsManager.OP_RECORD_AUDIO,
|
|
||||||
AppOpsManager.OP_COARSE_LOCATION,
|
|
||||||
AppOpsManager.OP_FINE_LOCATION};
|
|
||||||
|
|
||||||
private final SparseArray<ForegroundServicesUserState> mUserServices = new SparseArray<>();
|
private final SparseArray<ForegroundServicesUserState> mUserServices = new SparseArray<>();
|
||||||
private final Object mMutex = new Object();
|
private final Object mMutex = new Object();
|
||||||
private final NotificationEntryManager mEntryManager;
|
|
||||||
private final Handler mMainHandler;
|
private final Handler mMainHandler;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public ForegroundServiceController(NotificationEntryManager entryManager,
|
public ForegroundServiceController(AppOpsController appOpsController,
|
||||||
AppOpsController appOpsController, @Main Handler mainHandler) {
|
@Main Handler mainHandler) {
|
||||||
mEntryManager = entryManager;
|
|
||||||
mMainHandler = mainHandler;
|
mMainHandler = mainHandler;
|
||||||
appOpsController.addCallback(APP_OPS, (code, uid, packageName, active) -> {
|
appOpsController.addCallback(APP_OPS, (code, uid, packageName, active) -> {
|
||||||
mMainHandler.post(() -> {
|
mMainHandler.post(() -> {
|
||||||
@@ -86,19 +80,6 @@ public class ForegroundServiceController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the keys for notifications from this package using the standard template,
|
|
||||||
* if they exist.
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
public ArraySet<String> getStandardLayoutKeys(int userId, String pkg) {
|
|
||||||
synchronized (mMutex) {
|
|
||||||
final ForegroundServicesUserState services = mUserServices.get(userId);
|
|
||||||
if (services == null) return null;
|
|
||||||
return services.getStandardLayoutKeys(pkg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets active app ops for this user and package
|
* Gets active app ops for this user and package
|
||||||
*/
|
*/
|
||||||
@@ -140,31 +121,6 @@ public class ForegroundServiceController {
|
|||||||
userServices.removeOp(packageName, appOpCode);
|
userServices.removeOp(packageName, appOpCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: (b/145659174) remove when moving to NewNotifPipeline. Replaced by
|
|
||||||
// AppOpsCoordinator
|
|
||||||
// Update appOps if there are associated pending or visible notifications
|
|
||||||
final Set<String> notificationKeys = getStandardLayoutKeys(userId, packageName);
|
|
||||||
if (notificationKeys != null) {
|
|
||||||
boolean changed = false;
|
|
||||||
for (String key : notificationKeys) {
|
|
||||||
final NotificationEntry entry = mEntryManager.getPendingOrActiveNotif(key);
|
|
||||||
if (entry != null
|
|
||||||
&& uid == entry.getSbn().getUid()
|
|
||||||
&& packageName.equals(entry.getSbn().getPackageName())) {
|
|
||||||
synchronized (entry.mActiveAppOps) {
|
|
||||||
if (active) {
|
|
||||||
changed |= entry.mActiveAppOps.add(appOpCode);
|
|
||||||
} else {
|
|
||||||
changed |= entry.mActiveAppOps.remove(appOpCode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (changed) {
|
|
||||||
mEntryManager.updateNotifications("appOpChanged pkg=" + packageName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -172,24 +172,8 @@ public class ForegroundServiceNotificationListener {
|
|||||||
sbn.getPackageName(), sbn.getKey());
|
sbn.getPackageName(), sbn.getKey());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tagAppOps(entry);
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
true /* create if not found */);
|
true /* create if not found */);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: (b/145659174) remove when moving to NewNotifPipeline. Replaced by
|
|
||||||
// AppOpsCoordinator
|
|
||||||
private void tagAppOps(NotificationEntry entry) {
|
|
||||||
final StatusBarNotification sbn = entry.getSbn();
|
|
||||||
ArraySet<Integer> activeOps = mForegroundServiceController.getAppOps(
|
|
||||||
sbn.getUserId(),
|
|
||||||
sbn.getPackageName());
|
|
||||||
synchronized (entry.mActiveAppOps) {
|
|
||||||
entry.mActiveAppOps.clear();
|
|
||||||
if (activeOps != null) {
|
|
||||||
entry.mActiveAppOps.addAll(activeOps);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -486,7 +486,6 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
row.showAppOpsIcons(entry.mActiveAppOps);
|
|
||||||
row.setLastAudiblyAlertedMs(entry.getLastAudiblyAlertedMs());
|
row.setLastAudiblyAlertedMs(entry.getLastAudiblyAlertedMs());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -76,14 +76,9 @@ public class AppOpsCoordinator implements Coordinator {
|
|||||||
// extend the lifetime of foreground notification services to show for at least 5 seconds
|
// extend the lifetime of foreground notification services to show for at least 5 seconds
|
||||||
mNotifPipeline.addNotificationLifetimeExtender(mForegroundLifetimeExtender);
|
mNotifPipeline.addNotificationLifetimeExtender(mForegroundLifetimeExtender);
|
||||||
|
|
||||||
// listen for new notifications to add appOps
|
|
||||||
mNotifPipeline.addCollectionListener(mNotifCollectionListener);
|
|
||||||
|
|
||||||
// filter out foreground service notifications that aren't necessary anymore
|
// filter out foreground service notifications that aren't necessary anymore
|
||||||
mNotifPipeline.addPreGroupFilter(mNotifFilter);
|
mNotifPipeline.addPreGroupFilter(mNotifFilter);
|
||||||
|
|
||||||
// when appOps change, update any relevant notifications to update appOps for
|
|
||||||
mAppOpsController.addCallback(ForegroundServiceController.APP_OPS, this::onAppOpsChanged);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -174,82 +169,4 @@ public class AppOpsCoordinator implements Coordinator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds appOps to incoming and updating notifications
|
|
||||||
*/
|
|
||||||
private NotifCollectionListener mNotifCollectionListener = new NotifCollectionListener() {
|
|
||||||
@Override
|
|
||||||
public void onEntryAdded(NotificationEntry entry) {
|
|
||||||
tagAppOps(entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onEntryUpdated(NotificationEntry entry) {
|
|
||||||
tagAppOps(entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void tagAppOps(NotificationEntry entry) {
|
|
||||||
final StatusBarNotification sbn = entry.getSbn();
|
|
||||||
// note: requires that the ForegroundServiceController is updating their appOps first
|
|
||||||
ArraySet<Integer> activeOps =
|
|
||||||
mForegroundServiceController.getAppOps(
|
|
||||||
sbn.getUser().getIdentifier(),
|
|
||||||
sbn.getPackageName());
|
|
||||||
|
|
||||||
entry.mActiveAppOps.clear();
|
|
||||||
if (activeOps != null) {
|
|
||||||
entry.mActiveAppOps.addAll(activeOps);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private void onAppOpsChanged(int code, int uid, String packageName, boolean active) {
|
|
||||||
mMainExecutor.execute(() -> handleAppOpsChanged(code, uid, packageName, active));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the appOp for the posted notification associated with the current foreground service
|
|
||||||
*
|
|
||||||
* @param code code for appOp to add/remove
|
|
||||||
* @param uid of user the notification is sent to
|
|
||||||
* @param packageName package that created the notification
|
|
||||||
* @param active whether the appOpCode is active or not
|
|
||||||
*/
|
|
||||||
private void handleAppOpsChanged(int code, int uid, String packageName, boolean active) {
|
|
||||||
Assert.isMainThread();
|
|
||||||
|
|
||||||
int userId = UserHandle.getUserId(uid);
|
|
||||||
|
|
||||||
// Update appOps of the app's posted notifications with standard layouts
|
|
||||||
final ArraySet<String> notifKeys =
|
|
||||||
mForegroundServiceController.getStandardLayoutKeys(userId, packageName);
|
|
||||||
if (notifKeys != null) {
|
|
||||||
boolean changed = false;
|
|
||||||
for (int i = 0; i < notifKeys.size(); i++) {
|
|
||||||
final NotificationEntry entry = findNotificationEntryWithKey(notifKeys.valueAt(i));
|
|
||||||
if (entry != null
|
|
||||||
&& uid == entry.getSbn().getUid()
|
|
||||||
&& packageName.equals(entry.getSbn().getPackageName())) {
|
|
||||||
if (active) {
|
|
||||||
changed |= entry.mActiveAppOps.add(code);
|
|
||||||
} else {
|
|
||||||
changed |= entry.mActiveAppOps.remove(code);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (changed) {
|
|
||||||
mNotifFilter.invalidateList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private NotificationEntry findNotificationEntryWithKey(String key) {
|
|
||||||
for (NotificationEntry entry : mNotifPipeline.getAllNotifs()) {
|
|
||||||
if (entry.getKey().equals(key)) {
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,214 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.row;
|
|
||||||
|
|
||||||
import android.app.AppOpsManager;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.pm.ApplicationInfo;
|
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.service.notification.StatusBarNotification;
|
|
||||||
import android.util.ArraySet;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.accessibility.AccessibilityEvent;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import com.android.internal.logging.MetricsLogger;
|
|
||||||
import com.android.internal.logging.UiEventLogger;
|
|
||||||
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
|
||||||
import com.android.systemui.R;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The guts of a notification revealed when performing a long press.
|
|
||||||
*/
|
|
||||||
public class AppOpsInfo extends LinearLayout implements NotificationGuts.GutsContent {
|
|
||||||
private static final String TAG = "AppOpsGuts";
|
|
||||||
|
|
||||||
private PackageManager mPm;
|
|
||||||
|
|
||||||
private String mPkg;
|
|
||||||
private String mAppName;
|
|
||||||
private int mAppUid;
|
|
||||||
private StatusBarNotification mSbn;
|
|
||||||
private ArraySet<Integer> mAppOps;
|
|
||||||
private MetricsLogger mMetricsLogger;
|
|
||||||
private OnSettingsClickListener mOnSettingsClickListener;
|
|
||||||
private NotificationGuts mGutsContainer;
|
|
||||||
private UiEventLogger mUiEventLogger;
|
|
||||||
|
|
||||||
private OnClickListener mOnOk = v -> {
|
|
||||||
mGutsContainer.closeControls(v, false);
|
|
||||||
};
|
|
||||||
|
|
||||||
public AppOpsInfo(Context context, AttributeSet attrs) {
|
|
||||||
super(context, attrs);
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface OnSettingsClickListener {
|
|
||||||
void onClick(View v, String pkg, int uid, ArraySet<Integer> ops);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void bindGuts(final PackageManager pm,
|
|
||||||
final OnSettingsClickListener onSettingsClick,
|
|
||||||
final StatusBarNotification sbn,
|
|
||||||
final UiEventLogger uiEventLogger,
|
|
||||||
ArraySet<Integer> activeOps) {
|
|
||||||
mPkg = sbn.getPackageName();
|
|
||||||
mSbn = sbn;
|
|
||||||
mPm = pm;
|
|
||||||
mAppName = mPkg;
|
|
||||||
mOnSettingsClickListener = onSettingsClick;
|
|
||||||
mAppOps = activeOps;
|
|
||||||
mUiEventLogger = uiEventLogger;
|
|
||||||
|
|
||||||
bindHeader();
|
|
||||||
bindPrompt();
|
|
||||||
bindButtons();
|
|
||||||
|
|
||||||
logUiEvent(NotificationAppOpsEvent.NOTIFICATION_APP_OPS_OPEN);
|
|
||||||
mMetricsLogger = new MetricsLogger();
|
|
||||||
mMetricsLogger.visibility(MetricsEvent.APP_OPS_GUTS, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void bindHeader() {
|
|
||||||
// Package name
|
|
||||||
Drawable pkgicon = null;
|
|
||||||
ApplicationInfo info;
|
|
||||||
try {
|
|
||||||
info = mPm.getApplicationInfo(mPkg,
|
|
||||||
PackageManager.MATCH_UNINSTALLED_PACKAGES
|
|
||||||
| PackageManager.MATCH_DISABLED_COMPONENTS
|
|
||||||
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE
|
|
||||||
| PackageManager.MATCH_DIRECT_BOOT_AWARE);
|
|
||||||
if (info != null) {
|
|
||||||
mAppUid = mSbn.getUid();
|
|
||||||
mAppName = String.valueOf(mPm.getApplicationLabel(info));
|
|
||||||
pkgicon = mPm.getApplicationIcon(info);
|
|
||||||
}
|
|
||||||
} catch (PackageManager.NameNotFoundException e) {
|
|
||||||
// app is gone, just show package name and generic icon
|
|
||||||
pkgicon = mPm.getDefaultActivityIcon();
|
|
||||||
}
|
|
||||||
((ImageView) findViewById(R.id.pkgicon)).setImageDrawable(pkgicon);
|
|
||||||
((TextView) findViewById(R.id.pkgname)).setText(mAppName);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void bindPrompt() {
|
|
||||||
final TextView prompt = findViewById(R.id.prompt);
|
|
||||||
prompt.setText(getPrompt());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void bindButtons() {
|
|
||||||
View settings = findViewById(R.id.settings);
|
|
||||||
settings.setOnClickListener((View view) -> {
|
|
||||||
mOnSettingsClickListener.onClick(view, mPkg, mAppUid, mAppOps);
|
|
||||||
});
|
|
||||||
TextView ok = findViewById(R.id.ok);
|
|
||||||
ok.setOnClickListener(mOnOk);
|
|
||||||
ok.setAccessibilityDelegate(mGutsContainer.getAccessibilityDelegate());
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getPrompt() {
|
|
||||||
if (mAppOps == null || mAppOps.size() == 0) {
|
|
||||||
return "";
|
|
||||||
} else if (mAppOps.size() == 1) {
|
|
||||||
if (mAppOps.contains(AppOpsManager.OP_CAMERA)) {
|
|
||||||
return mContext.getString(R.string.appops_camera);
|
|
||||||
} else if (mAppOps.contains(AppOpsManager.OP_RECORD_AUDIO)) {
|
|
||||||
return mContext.getString(R.string.appops_microphone);
|
|
||||||
} else {
|
|
||||||
return mContext.getString(R.string.appops_overlay);
|
|
||||||
}
|
|
||||||
} else if (mAppOps.size() == 2) {
|
|
||||||
if (mAppOps.contains(AppOpsManager.OP_CAMERA)) {
|
|
||||||
if (mAppOps.contains(AppOpsManager.OP_RECORD_AUDIO)) {
|
|
||||||
return mContext.getString(R.string.appops_camera_mic);
|
|
||||||
} else {
|
|
||||||
return mContext.getString(R.string.appops_camera_overlay);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return mContext.getString(R.string.appops_mic_overlay);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return mContext.getString(R.string.appops_camera_mic_overlay);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
|
|
||||||
super.onInitializeAccessibilityEvent(event);
|
|
||||||
if (mGutsContainer != null &&
|
|
||||||
event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
|
|
||||||
if (mGutsContainer.isExposed()) {
|
|
||||||
event.getText().add(mContext.getString(
|
|
||||||
R.string.notification_channel_controls_opened_accessibility, mAppName));
|
|
||||||
} else {
|
|
||||||
event.getText().add(mContext.getString(
|
|
||||||
R.string.notification_channel_controls_closed_accessibility, mAppName));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setGutsParent(NotificationGuts guts) {
|
|
||||||
mGutsContainer = guts;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean willBeRemoved() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean shouldBeSaved() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean needsFalsingProtection() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public View getContentView() {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean handleCloseControls(boolean save, boolean force) {
|
|
||||||
logUiEvent(NotificationAppOpsEvent.NOTIFICATION_APP_OPS_CLOSE);
|
|
||||||
if (mMetricsLogger != null) {
|
|
||||||
mMetricsLogger.visibility(MetricsEvent.APP_OPS_GUTS, false);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getActualHeight() {
|
|
||||||
return getHeight();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void logUiEvent(NotificationAppOpsEvent event) {
|
|
||||||
if (mSbn != null) {
|
|
||||||
mUiEventLogger.logWithInstanceId(event,
|
|
||||||
mSbn.getUid(), mSbn.getPackageName(), mSbn.getInstanceId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -239,7 +239,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
|||||||
private boolean mShowNoBackground;
|
private boolean mShowNoBackground;
|
||||||
private ExpandableNotificationRow mNotificationParent;
|
private ExpandableNotificationRow mNotificationParent;
|
||||||
private OnExpandClickListener mOnExpandClickListener;
|
private OnExpandClickListener mOnExpandClickListener;
|
||||||
private View.OnClickListener mOnAppOpsClickListener;
|
|
||||||
|
|
||||||
// Listener will be called when receiving a long click event.
|
// Listener will be called when receiving a long click event.
|
||||||
// Use #setLongPressPosition to optionally assign positional data with the long press.
|
// Use #setLongPressPosition to optionally assign positional data with the long press.
|
||||||
@@ -1143,7 +1142,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
|||||||
items.add(NotificationMenuRow.createPartialConversationItem(mContext));
|
items.add(NotificationMenuRow.createPartialConversationItem(mContext));
|
||||||
items.add(NotificationMenuRow.createInfoItem(mContext));
|
items.add(NotificationMenuRow.createInfoItem(mContext));
|
||||||
items.add(NotificationMenuRow.createSnoozeItem(mContext));
|
items.add(NotificationMenuRow.createSnoozeItem(mContext));
|
||||||
items.add(NotificationMenuRow.createAppOpsItem(mContext));
|
|
||||||
mMenuRow.setMenuItems(items);
|
mMenuRow.setMenuItems(items);
|
||||||
}
|
}
|
||||||
if (existed) {
|
if (existed) {
|
||||||
@@ -1609,7 +1607,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
|||||||
RowContentBindStage rowContentBindStage,
|
RowContentBindStage rowContentBindStage,
|
||||||
OnExpandClickListener onExpandClickListener,
|
OnExpandClickListener onExpandClickListener,
|
||||||
NotificationMediaManager notificationMediaManager,
|
NotificationMediaManager notificationMediaManager,
|
||||||
OnAppOpsClickListener onAppOpsClickListener,
|
|
||||||
FalsingManager falsingManager,
|
FalsingManager falsingManager,
|
||||||
StatusBarStateController statusBarStateController,
|
StatusBarStateController statusBarStateController,
|
||||||
PeopleNotificationIdentifier peopleNotificationIdentifier) {
|
PeopleNotificationIdentifier peopleNotificationIdentifier) {
|
||||||
@@ -1629,7 +1626,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
|||||||
mRowContentBindStage = rowContentBindStage;
|
mRowContentBindStage = rowContentBindStage;
|
||||||
mOnExpandClickListener = onExpandClickListener;
|
mOnExpandClickListener = onExpandClickListener;
|
||||||
mMediaManager = notificationMediaManager;
|
mMediaManager = notificationMediaManager;
|
||||||
setAppOpsOnClickListener(onAppOpsClickListener);
|
|
||||||
mFalsingManager = falsingManager;
|
mFalsingManager = falsingManager;
|
||||||
mStatusbarStateController = statusBarStateController;
|
mStatusbarStateController = statusBarStateController;
|
||||||
mPeopleNotificationIdentifier = peopleNotificationIdentifier;
|
mPeopleNotificationIdentifier = peopleNotificationIdentifier;
|
||||||
@@ -1686,14 +1682,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
|||||||
requestLayout();
|
requestLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void showAppOpsIcons(ArraySet<Integer> activeOps) {
|
|
||||||
if (mIsSummaryWithChildren) {
|
|
||||||
mChildrenContainer.showAppOpsIcons(activeOps);
|
|
||||||
}
|
|
||||||
mPrivateLayout.showAppOpsIcons(activeOps);
|
|
||||||
mPublicLayout.showAppOpsIcons(activeOps);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Sets the last time the notification being displayed audibly alerted the user. */
|
/** Sets the last time the notification being displayed audibly alerted the user. */
|
||||||
public void setLastAudiblyAlertedMs(long lastAudiblyAlertedMs) {
|
public void setLastAudiblyAlertedMs(long lastAudiblyAlertedMs) {
|
||||||
if (NotificationUtils.useNewInterruptionModel(mContext)) {
|
if (NotificationUtils.useNewInterruptionModel(mContext)) {
|
||||||
@@ -1722,24 +1710,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
|||||||
mPublicLayout.setRecentlyAudiblyAlerted(audiblyAlertedRecently);
|
mPublicLayout.setRecentlyAudiblyAlerted(audiblyAlertedRecently);
|
||||||
}
|
}
|
||||||
|
|
||||||
public View.OnClickListener getAppOpsOnClickListener() {
|
|
||||||
return mOnAppOpsClickListener;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setAppOpsOnClickListener(ExpandableNotificationRow.OnAppOpsClickListener l) {
|
|
||||||
mOnAppOpsClickListener = v -> {
|
|
||||||
createMenu();
|
|
||||||
NotificationMenuRowPlugin provider = getProvider();
|
|
||||||
if (provider == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
MenuItem menuItem = provider.getAppOpsMenuItem(mContext);
|
|
||||||
if (menuItem != null) {
|
|
||||||
l.onClick(this, v.getWidth() / 2, v.getHeight() / 2, menuItem);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onFinishInflate() {
|
protected void onFinishInflate() {
|
||||||
super.onFinishInflate();
|
super.onFinishInflate();
|
||||||
|
|||||||
@@ -63,7 +63,6 @@ public class ExpandableNotificationRowController {
|
|||||||
|
|
||||||
private final ExpandableNotificationRow.ExpansionLogger mExpansionLogger =
|
private final ExpandableNotificationRow.ExpansionLogger mExpansionLogger =
|
||||||
this::logNotificationExpansion;
|
this::logNotificationExpansion;
|
||||||
private final ExpandableNotificationRow.OnAppOpsClickListener mOnAppOpsClickListener;
|
|
||||||
private final NotificationGutsManager mNotificationGutsManager;
|
private final NotificationGutsManager mNotificationGutsManager;
|
||||||
private Runnable mOnDismissRunnable;
|
private Runnable mOnDismissRunnable;
|
||||||
private final FalsingManager mFalsingManager;
|
private final FalsingManager mFalsingManager;
|
||||||
@@ -101,7 +100,6 @@ public class ExpandableNotificationRowController {
|
|||||||
mStatusBarStateController = statusBarStateController;
|
mStatusBarStateController = statusBarStateController;
|
||||||
mNotificationGutsManager = notificationGutsManager;
|
mNotificationGutsManager = notificationGutsManager;
|
||||||
mOnDismissRunnable = onDismissRunnable;
|
mOnDismissRunnable = onDismissRunnable;
|
||||||
mOnAppOpsClickListener = mNotificationGutsManager::openGuts;
|
|
||||||
mAllowLongPress = allowLongPress;
|
mAllowLongPress = allowLongPress;
|
||||||
mFalsingManager = falsingManager;
|
mFalsingManager = falsingManager;
|
||||||
mPeopleNotificationIdentifier = peopleNotificationIdentifier;
|
mPeopleNotificationIdentifier = peopleNotificationIdentifier;
|
||||||
@@ -122,7 +120,6 @@ public class ExpandableNotificationRowController {
|
|||||||
mRowContentBindStage,
|
mRowContentBindStage,
|
||||||
mOnExpandClickListener,
|
mOnExpandClickListener,
|
||||||
mMediaManager,
|
mMediaManager,
|
||||||
mOnAppOpsClickListener,
|
|
||||||
mFalsingManager,
|
mFalsingManager,
|
||||||
mStatusBarStateController,
|
mStatusBarStateController,
|
||||||
mPeopleNotificationIdentifier
|
mPeopleNotificationIdentifier
|
||||||
|
|||||||
@@ -1567,18 +1567,6 @@ public class NotificationContentView extends FrameLayout {
|
|||||||
return header;
|
return header;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void showAppOpsIcons(ArraySet<Integer> activeOps) {
|
|
||||||
if (mContractedChild != null) {
|
|
||||||
mContractedWrapper.showAppOpsIcons(activeOps);
|
|
||||||
}
|
|
||||||
if (mExpandedChild != null) {
|
|
||||||
mExpandedWrapper.showAppOpsIcons(activeOps);
|
|
||||||
}
|
|
||||||
if (mHeadsUpChild != null) {
|
|
||||||
mHeadsUpWrapper.showAppOpsIcons(activeOps);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Sets whether the notification being displayed audibly alerted the user. */
|
/** Sets whether the notification being displayed audibly alerted the user. */
|
||||||
public void setRecentlyAudiblyAlerted(boolean audiblyAlerted) {
|
public void setRecentlyAudiblyAlerted(boolean audiblyAlerted) {
|
||||||
if (mContractedChild != null) {
|
if (mContractedChild != null) {
|
||||||
|
|||||||
@@ -264,8 +264,6 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
|
|||||||
try {
|
try {
|
||||||
if (gutsView instanceof NotificationSnooze) {
|
if (gutsView instanceof NotificationSnooze) {
|
||||||
initializeSnoozeView(row, (NotificationSnooze) gutsView);
|
initializeSnoozeView(row, (NotificationSnooze) gutsView);
|
||||||
} else if (gutsView instanceof AppOpsInfo) {
|
|
||||||
initializeAppOpsInfo(row, (AppOpsInfo) gutsView);
|
|
||||||
} else if (gutsView instanceof NotificationInfo) {
|
} else if (gutsView instanceof NotificationInfo) {
|
||||||
initializeNotificationInfo(row, (NotificationInfo) gutsView);
|
initializeNotificationInfo(row, (NotificationInfo) gutsView);
|
||||||
} else if (gutsView instanceof NotificationConversationInfo) {
|
} else if (gutsView instanceof NotificationConversationInfo) {
|
||||||
@@ -302,36 +300,6 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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.getEntry().getSbn();
|
|
||||||
UserHandle userHandle = sbn.getUser();
|
|
||||||
PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
|
|
||||||
userHandle.getIdentifier());
|
|
||||||
|
|
||||||
AppOpsInfo.OnSettingsClickListener onSettingsClick =
|
|
||||||
(View v, String pkg, int uid, ArraySet<Integer> ops) -> {
|
|
||||||
mUiEventLogger.logWithInstanceId(
|
|
||||||
NotificationAppOpsEvent.NOTIFICATION_APP_OPS_SETTINGS_CLICK,
|
|
||||||
sbn.getUid(), sbn.getPackageName(), sbn.getInstanceId());
|
|
||||||
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, mUiEventLogger,
|
|
||||||
row.getEntry().mActiveAppOps);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets up the {@link NotificationInfo} inside the notification row's guts.
|
* Sets up the {@link NotificationInfo} inside the notification row's guts.
|
||||||
* @param row view to set up the guts for
|
* @param row view to set up the guts for
|
||||||
|
|||||||
@@ -76,7 +76,6 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
|
|||||||
private Context mContext;
|
private Context mContext;
|
||||||
private FrameLayout mMenuContainer;
|
private FrameLayout mMenuContainer;
|
||||||
private NotificationMenuItem mInfoItem;
|
private NotificationMenuItem mInfoItem;
|
||||||
private MenuItem mAppOpsItem;
|
|
||||||
private MenuItem mSnoozeItem;
|
private MenuItem mSnoozeItem;
|
||||||
private ArrayList<MenuItem> mLeftMenuItems;
|
private ArrayList<MenuItem> mLeftMenuItems;
|
||||||
private ArrayList<MenuItem> mRightMenuItems;
|
private ArrayList<MenuItem> mRightMenuItems;
|
||||||
@@ -137,11 +136,6 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
|
|||||||
return mInfoItem;
|
return mInfoItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public MenuItem getAppOpsMenuItem(Context context) {
|
|
||||||
return mAppOpsItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MenuItem getSnoozeMenuItem(Context context) {
|
public MenuItem getSnoozeMenuItem(Context context) {
|
||||||
return mSnoozeItem;
|
return mSnoozeItem;
|
||||||
@@ -264,7 +258,6 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
|
|||||||
// Only show snooze for non-foreground notifications, and if the setting is on
|
// Only show snooze for non-foreground notifications, and if the setting is on
|
||||||
mSnoozeItem = createSnoozeItem(mContext);
|
mSnoozeItem = createSnoozeItem(mContext);
|
||||||
}
|
}
|
||||||
mAppOpsItem = createAppOpsItem(mContext);
|
|
||||||
NotificationEntry entry = mParent.getEntry();
|
NotificationEntry entry = mParent.getEntry();
|
||||||
int personNotifType = mPeopleNotificationIdentifier
|
int personNotifType = mPeopleNotificationIdentifier
|
||||||
.getPeopleNotificationType(entry.getSbn(), entry.getRanking());
|
.getPeopleNotificationType(entry.getSbn(), entry.getRanking());
|
||||||
@@ -280,7 +273,6 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
|
|||||||
mRightMenuItems.add(mSnoozeItem);
|
mRightMenuItems.add(mSnoozeItem);
|
||||||
}
|
}
|
||||||
mRightMenuItems.add(mInfoItem);
|
mRightMenuItems.add(mInfoItem);
|
||||||
mRightMenuItems.add(mAppOpsItem);
|
|
||||||
mLeftMenuItems.addAll(mRightMenuItems);
|
mLeftMenuItems.addAll(mRightMenuItems);
|
||||||
|
|
||||||
populateMenuViews();
|
populateMenuViews();
|
||||||
@@ -688,14 +680,6 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
|
|||||||
R.drawable.ic_settings);
|
R.drawable.ic_settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
static MenuItem createAppOpsItem(Context context) {
|
|
||||||
AppOpsInfo appOpsContent = (AppOpsInfo) LayoutInflater.from(context).inflate(
|
|
||||||
R.layout.app_ops_info, null, false);
|
|
||||||
MenuItem info = new NotificationMenuItem(context, null, appOpsContent,
|
|
||||||
-1 /*don't show in slow swipe menu */);
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addMenuView(MenuItem item, ViewGroup parent) {
|
private void addMenuView(MenuItem item, ViewGroup parent) {
|
||||||
View menuView = item.getMenuView();
|
View menuView = item.getMenuView();
|
||||||
if (menuView != null) {
|
if (menuView != null) {
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ package com.android.systemui.statusbar.notification.row.wrapper;
|
|||||||
|
|
||||||
import static com.android.systemui.statusbar.notification.TransformState.TRANSFORM_Y;
|
import static com.android.systemui.statusbar.notification.TransformState.TRANSFORM_Y;
|
||||||
|
|
||||||
import android.app.AppOpsManager;
|
|
||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.util.ArraySet;
|
import android.util.ArraySet;
|
||||||
@@ -64,10 +63,6 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
|
|||||||
private TextView mHeaderText;
|
private TextView mHeaderText;
|
||||||
private TextView mAppNameText;
|
private TextView mAppNameText;
|
||||||
private ImageView mWorkProfileImage;
|
private ImageView mWorkProfileImage;
|
||||||
private View mCameraIcon;
|
|
||||||
private View mMicIcon;
|
|
||||||
private View mOverlayIcon;
|
|
||||||
private View mAppOps;
|
|
||||||
private View mAudiblyAlertedIcon;
|
private View mAudiblyAlertedIcon;
|
||||||
private FrameLayout mIconContainer;
|
private FrameLayout mIconContainer;
|
||||||
|
|
||||||
@@ -108,7 +103,6 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
|
|||||||
}
|
}
|
||||||
}, TRANSFORMING_VIEW_TITLE);
|
}, TRANSFORMING_VIEW_TITLE);
|
||||||
resolveHeaderViews();
|
resolveHeaderViews();
|
||||||
addAppOpsOnClickListener(row);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void resolveHeaderViews() {
|
protected void resolveHeaderViews() {
|
||||||
@@ -119,10 +113,6 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
|
|||||||
mExpandButton = mView.findViewById(com.android.internal.R.id.expand_button);
|
mExpandButton = mView.findViewById(com.android.internal.R.id.expand_button);
|
||||||
mWorkProfileImage = mView.findViewById(com.android.internal.R.id.profile_badge);
|
mWorkProfileImage = mView.findViewById(com.android.internal.R.id.profile_badge);
|
||||||
mNotificationHeader = mView.findViewById(com.android.internal.R.id.notification_header);
|
mNotificationHeader = mView.findViewById(com.android.internal.R.id.notification_header);
|
||||||
mCameraIcon = mView.findViewById(com.android.internal.R.id.camera);
|
|
||||||
mMicIcon = mView.findViewById(com.android.internal.R.id.mic);
|
|
||||||
mOverlayIcon = mView.findViewById(com.android.internal.R.id.overlay);
|
|
||||||
mAppOps = mView.findViewById(com.android.internal.R.id.app_ops);
|
|
||||||
mAudiblyAlertedIcon = mView.findViewById(com.android.internal.R.id.alerted_icon);
|
mAudiblyAlertedIcon = mView.findViewById(com.android.internal.R.id.alerted_icon);
|
||||||
if (mNotificationHeader != null) {
|
if (mNotificationHeader != null) {
|
||||||
mNotificationHeader.setShowExpandButtonAtEnd(mShowExpandButtonAtEnd);
|
mNotificationHeader.setShowExpandButtonAtEnd(mShowExpandButtonAtEnd);
|
||||||
@@ -130,38 +120,6 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addAppOpsOnClickListener(ExpandableNotificationRow row) {
|
|
||||||
View.OnClickListener listener = row.getAppOpsOnClickListener();
|
|
||||||
if (mNotificationHeader != null) {
|
|
||||||
mNotificationHeader.setAppOpsOnClickListener(listener);
|
|
||||||
}
|
|
||||||
if (mAppOps != null) {
|
|
||||||
mAppOps.setOnClickListener(listener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shows or hides 'app op in use' icons based on app usage.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void showAppOpsIcons(ArraySet<Integer> appOps) {
|
|
||||||
if (appOps == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (mOverlayIcon != null) {
|
|
||||||
mOverlayIcon.setVisibility(appOps.contains(AppOpsManager.OP_SYSTEM_ALERT_WINDOW)
|
|
||||||
? View.VISIBLE : View.GONE);
|
|
||||||
}
|
|
||||||
if (mCameraIcon != null) {
|
|
||||||
mCameraIcon.setVisibility(appOps.contains(AppOpsManager.OP_CAMERA)
|
|
||||||
? View.VISIBLE : View.GONE);
|
|
||||||
}
|
|
||||||
if (mMicIcon != null) {
|
|
||||||
mMicIcon.setVisibility(appOps.contains(AppOpsManager.OP_RECORD_AUDIO)
|
|
||||||
? View.VISIBLE : View.GONE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onContentUpdated(ExpandableNotificationRow row) {
|
public void onContentUpdated(ExpandableNotificationRow row) {
|
||||||
super.onContentUpdated(row);
|
super.onContentUpdated(row);
|
||||||
@@ -285,15 +243,6 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
|
|||||||
mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TITLE,
|
mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TITLE,
|
||||||
mHeaderText);
|
mHeaderText);
|
||||||
}
|
}
|
||||||
if (mCameraIcon != null) {
|
|
||||||
mTransformationHelper.addViewTransformingToSimilar(mCameraIcon);
|
|
||||||
}
|
|
||||||
if (mMicIcon != null) {
|
|
||||||
mTransformationHelper.addViewTransformingToSimilar(mMicIcon);
|
|
||||||
}
|
|
||||||
if (mOverlayIcon != null) {
|
|
||||||
mTransformationHelper.addViewTransformingToSimilar(mOverlayIcon);
|
|
||||||
}
|
|
||||||
if (mAudiblyAlertedIcon != null) {
|
if (mAudiblyAlertedIcon != null) {
|
||||||
mTransformationHelper.addViewTransformingToSimilar(mAudiblyAlertedIcon);
|
mTransformationHelper.addViewTransformingToSimilar(mAudiblyAlertedIcon);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,14 +96,6 @@ public abstract class NotificationViewWrapper implements TransformableView {
|
|||||||
public void onContentUpdated(ExpandableNotificationRow row) {
|
public void onContentUpdated(ExpandableNotificationRow row) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Show a set of app opp icons in the layout.
|
|
||||||
*
|
|
||||||
* @param appOps which app ops to show
|
|
||||||
*/
|
|
||||||
public void showAppOpsIcons(ArraySet<Integer> appOps) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onReinflated() {
|
public void onReinflated() {
|
||||||
if (shouldClearBackgroundOnReapply()) {
|
if (shouldClearBackgroundOnReapply()) {
|
||||||
mBackgroundColor = 0;
|
mBackgroundColor = 0;
|
||||||
|
|||||||
@@ -1302,20 +1302,6 @@ public class NotificationChildrenContainer extends ViewGroup {
|
|||||||
mCurrentHeaderTranslation = (int) ((1.0f - headerVisibleAmount) * mTranslationForHeader);
|
mCurrentHeaderTranslation = (int) ((1.0f - headerVisibleAmount) * mTranslationForHeader);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Show a set of app opp icons in the layout.
|
|
||||||
*
|
|
||||||
* @param appOps which app ops to show
|
|
||||||
*/
|
|
||||||
public void showAppOpsIcons(ArraySet<Integer> appOps) {
|
|
||||||
if (mNotificationHeaderWrapper != null) {
|
|
||||||
mNotificationHeaderWrapper.showAppOpsIcons(appOps);
|
|
||||||
}
|
|
||||||
if (mNotificationHeaderWrapperLowPriority != null) {
|
|
||||||
mNotificationHeaderWrapperLowPriority.showAppOpsIcons(appOps);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRecentlyAudiblyAlerted(boolean audiblyAlertedRecently) {
|
public void setRecentlyAudiblyAlerted(boolean audiblyAlertedRecently) {
|
||||||
if (mNotificationHeaderWrapper != null) {
|
if (mNotificationHeaderWrapper != null) {
|
||||||
mNotificationHeaderWrapper.setRecentlyAudiblyAlerted(audiblyAlertedRecently);
|
mNotificationHeaderWrapper.setRecentlyAudiblyAlerted(audiblyAlertedRecently);
|
||||||
|
|||||||
@@ -82,8 +82,7 @@ public class ForegroundServiceControllerTest extends SysuiTestCase {
|
|||||||
allowTestableLooperAsMainThread();
|
allowTestableLooperAsMainThread();
|
||||||
|
|
||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
mFsc = new ForegroundServiceController(
|
mFsc = new ForegroundServiceController(mAppOpsController, mMainHandler);
|
||||||
mEntryManager, mAppOpsController, mMainHandler);
|
|
||||||
mListener = new ForegroundServiceNotificationListener(
|
mListener = new ForegroundServiceNotificationListener(
|
||||||
mContext, mFsc, mEntryManager, mNotifPipeline,
|
mContext, mFsc, mEntryManager, mNotifPipeline,
|
||||||
mock(ForegroundServiceLifetimeExtender.class), mClock);
|
mock(ForegroundServiceLifetimeExtender.class), mClock);
|
||||||
@@ -114,85 +113,6 @@ public class ForegroundServiceControllerTest extends SysuiTestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAppOps_appOpChangedBeforeNotificationExists() {
|
|
||||||
// GIVEN app op exists, but notification doesn't exist in NEM yet
|
|
||||||
NotificationEntry entry = createFgEntry();
|
|
||||||
mFsc.onAppOpChanged(
|
|
||||||
AppOpsManager.OP_CAMERA,
|
|
||||||
entry.getSbn().getUid(),
|
|
||||||
entry.getSbn().getPackageName(),
|
|
||||||
true);
|
|
||||||
assertFalse(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA));
|
|
||||||
|
|
||||||
// WHEN the notification is added
|
|
||||||
mEntryListener.onPendingEntryAdded(entry);
|
|
||||||
|
|
||||||
// THEN the app op is added to the entry
|
|
||||||
Assert.assertTrue(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAppOps_appOpAddedToForegroundNotif() {
|
|
||||||
// GIVEN a notification associated with a foreground service
|
|
||||||
NotificationEntry entry = addFgEntry();
|
|
||||||
when(mEntryManager.getPendingOrActiveNotif(entry.getKey())).thenReturn(entry);
|
|
||||||
|
|
||||||
// WHEN we are notified of a new app op for this notification
|
|
||||||
mFsc.onAppOpChanged(
|
|
||||||
AppOpsManager.OP_CAMERA,
|
|
||||||
entry.getSbn().getUid(),
|
|
||||||
entry.getSbn().getPackageName(),
|
|
||||||
true);
|
|
||||||
|
|
||||||
// THEN the app op is added to the entry
|
|
||||||
Assert.assertTrue(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA));
|
|
||||||
|
|
||||||
// THEN notification views are updated since the notification is visible
|
|
||||||
verify(mEntryManager, times(1)).updateNotifications(anyString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAppOpsAlreadyAdded() {
|
|
||||||
// GIVEN a foreground service associated notification that already has the correct app op
|
|
||||||
NotificationEntry entry = addFgEntry();
|
|
||||||
entry.mActiveAppOps.add(AppOpsManager.OP_CAMERA);
|
|
||||||
when(mEntryManager.getPendingOrActiveNotif(entry.getKey())).thenReturn(entry);
|
|
||||||
|
|
||||||
// WHEN we are notified of the same app op for this notification
|
|
||||||
mFsc.onAppOpChanged(
|
|
||||||
AppOpsManager.OP_CAMERA,
|
|
||||||
entry.getSbn().getUid(),
|
|
||||||
entry.getSbn().getPackageName(),
|
|
||||||
true);
|
|
||||||
|
|
||||||
// THEN the app op still exists in the notification entry
|
|
||||||
Assert.assertTrue(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA));
|
|
||||||
|
|
||||||
// THEN notification views aren't updated since nothing changed
|
|
||||||
verify(mEntryManager, never()).updateNotifications(anyString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAppOps_appOpNotAddedToUnrelatedNotif() {
|
|
||||||
// GIVEN no notification entries correspond to the newly updated appOp
|
|
||||||
NotificationEntry entry = addFgEntry();
|
|
||||||
when(mEntryManager.getPendingOrActiveNotif(entry.getKey())).thenReturn(null);
|
|
||||||
|
|
||||||
// WHEN a new app op is detected
|
|
||||||
mFsc.onAppOpChanged(
|
|
||||||
AppOpsManager.OP_CAMERA,
|
|
||||||
entry.getSbn().getUid(),
|
|
||||||
entry.getSbn().getPackageName(),
|
|
||||||
true);
|
|
||||||
|
|
||||||
// THEN we won't see appOps on the entry
|
|
||||||
Assert.assertFalse(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA));
|
|
||||||
|
|
||||||
// THEN notification views aren't updated since nothing changed
|
|
||||||
verify(mEntryManager, never()).updateNotifications(anyString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAppOpsCRUD() {
|
public void testAppOpsCRUD() {
|
||||||
// no crash on remove that doesn't exist
|
// no crash on remove that doesn't exist
|
||||||
|
|||||||
@@ -210,19 +210,6 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase {
|
|||||||
assertEquals(View.VISIBLE, entry1.getRow().getVisibility());
|
assertEquals(View.VISIBLE, entry1.getRow().getVisibility());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testUpdateNotificationViews_appOps() throws Exception {
|
|
||||||
NotificationEntry entry0 = createEntry();
|
|
||||||
entry0.setRow(spy(entry0.getRow()));
|
|
||||||
when(mEntryManager.getVisibleNotifications()).thenReturn(
|
|
||||||
Lists.newArrayList(entry0));
|
|
||||||
mListContainer.addContainerView(entry0.getRow());
|
|
||||||
|
|
||||||
mViewHierarchyManager.updateNotificationViews();
|
|
||||||
|
|
||||||
verify(entry0.getRow(), times(1)).showAppOpsIcons(any());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReentrantCallsToOnDynamicPrivacyChangedPostForLater() {
|
public void testReentrantCallsToOnDynamicPrivacyChangedPostForLater() {
|
||||||
// GIVEN a ListContainer that will make a re-entrant call to updateNotificationViews()
|
// GIVEN a ListContainer that will make a re-entrant call to updateNotificationViews()
|
||||||
|
|||||||
@@ -72,8 +72,6 @@ public class AppOpsCoordinatorTest extends SysuiTestCase {
|
|||||||
private Notification mNotification;
|
private Notification mNotification;
|
||||||
private AppOpsCoordinator mAppOpsCoordinator;
|
private AppOpsCoordinator mAppOpsCoordinator;
|
||||||
private NotifFilter mForegroundFilter;
|
private NotifFilter mForegroundFilter;
|
||||||
private NotifCollectionListener mNotifCollectionListener;
|
|
||||||
private AppOpsController.Callback mAppOpsCallback;
|
|
||||||
private NotifLifetimeExtender mForegroundNotifLifetimeExtender;
|
private NotifLifetimeExtender mForegroundNotifLifetimeExtender;
|
||||||
|
|
||||||
private FakeSystemClock mClock = new FakeSystemClock();
|
private FakeSystemClock mClock = new FakeSystemClock();
|
||||||
@@ -110,18 +108,6 @@ public class AppOpsCoordinatorTest extends SysuiTestCase {
|
|||||||
lifetimeExtenderCaptor.capture());
|
lifetimeExtenderCaptor.capture());
|
||||||
mForegroundNotifLifetimeExtender = lifetimeExtenderCaptor.getValue();
|
mForegroundNotifLifetimeExtender = lifetimeExtenderCaptor.getValue();
|
||||||
|
|
||||||
// capture notifCollectionListener
|
|
||||||
ArgumentCaptor<NotifCollectionListener> notifCollectionCaptor =
|
|
||||||
ArgumentCaptor.forClass(NotifCollectionListener.class);
|
|
||||||
verify(mNotifPipeline, times(1)).addCollectionListener(
|
|
||||||
notifCollectionCaptor.capture());
|
|
||||||
mNotifCollectionListener = notifCollectionCaptor.getValue();
|
|
||||||
|
|
||||||
// capture app ops callback
|
|
||||||
ArgumentCaptor<AppOpsController.Callback> appOpsCaptor =
|
|
||||||
ArgumentCaptor.forClass(AppOpsController.Callback.class);
|
|
||||||
verify(mAppOpsController).addCallback(any(int[].class), appOpsCaptor.capture());
|
|
||||||
mAppOpsCallback = appOpsCaptor.getValue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -215,134 +201,4 @@ public class AppOpsCoordinatorTest extends SysuiTestCase {
|
|||||||
assertFalse(mForegroundNotifLifetimeExtender
|
assertFalse(mForegroundNotifLifetimeExtender
|
||||||
.shouldExtendLifetime(mEntry, NotificationListenerService.REASON_CLICK));
|
.shouldExtendLifetime(mEntry, NotificationListenerService.REASON_CLICK));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAppOpsUpdateOnlyAppliedToRelevantNotificationWithStandardLayout() {
|
|
||||||
// GIVEN three current notifications, two with the same key but from different users
|
|
||||||
NotificationEntry entry1 = new NotificationEntryBuilder()
|
|
||||||
.setUser(new UserHandle(NOTIF_USER_ID))
|
|
||||||
.setPkg(TEST_PKG)
|
|
||||||
.setId(1)
|
|
||||||
.build();
|
|
||||||
NotificationEntry entry2 = new NotificationEntryBuilder()
|
|
||||||
.setUser(new UserHandle(NOTIF_USER_ID))
|
|
||||||
.setPkg(TEST_PKG)
|
|
||||||
.setId(2)
|
|
||||||
.build();
|
|
||||||
NotificationEntry entry3_diffUser = new NotificationEntryBuilder()
|
|
||||||
.setUser(new UserHandle(NOTIF_USER_ID + 1))
|
|
||||||
.setPkg(TEST_PKG)
|
|
||||||
.setId(2)
|
|
||||||
.build();
|
|
||||||
when(mNotifPipeline.getAllNotifs()).thenReturn(List.of(entry1, entry2, entry3_diffUser));
|
|
||||||
|
|
||||||
// GIVEN that only entry2 has a standard layout
|
|
||||||
when(mForegroundServiceController.getStandardLayoutKeys(NOTIF_USER_ID, TEST_PKG))
|
|
||||||
.thenReturn(new ArraySet<>(List.of(entry2.getKey())));
|
|
||||||
|
|
||||||
// WHEN a new app ops code comes in
|
|
||||||
mAppOpsCallback.onActiveStateChanged(47, NOTIF_USER_ID, TEST_PKG, true);
|
|
||||||
mExecutor.runAllReady();
|
|
||||||
|
|
||||||
// THEN entry2's app ops are updated, but no one else's are
|
|
||||||
assertEquals(
|
|
||||||
new ArraySet<>(),
|
|
||||||
entry1.mActiveAppOps);
|
|
||||||
assertEquals(
|
|
||||||
new ArraySet<>(List.of(47)),
|
|
||||||
entry2.mActiveAppOps);
|
|
||||||
assertEquals(
|
|
||||||
new ArraySet<>(),
|
|
||||||
entry3_diffUser.mActiveAppOps);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAppOpsUpdateAppliedToAllNotificationsWithStandardLayouts() {
|
|
||||||
// GIVEN three notifications with standard layouts
|
|
||||||
NotificationEntry entry1 = new NotificationEntryBuilder()
|
|
||||||
.setUser(new UserHandle(NOTIF_USER_ID))
|
|
||||||
.setPkg(TEST_PKG)
|
|
||||||
.setId(1)
|
|
||||||
.build();
|
|
||||||
NotificationEntry entry2 = new NotificationEntryBuilder()
|
|
||||||
.setUser(new UserHandle(NOTIF_USER_ID))
|
|
||||||
.setPkg(TEST_PKG)
|
|
||||||
.setId(2)
|
|
||||||
.build();
|
|
||||||
NotificationEntry entry3 = new NotificationEntryBuilder()
|
|
||||||
.setUser(new UserHandle(NOTIF_USER_ID))
|
|
||||||
.setPkg(TEST_PKG)
|
|
||||||
.setId(3)
|
|
||||||
.build();
|
|
||||||
when(mNotifPipeline.getAllNotifs()).thenReturn(List.of(entry1, entry2, entry3));
|
|
||||||
when(mForegroundServiceController.getStandardLayoutKeys(NOTIF_USER_ID, TEST_PKG))
|
|
||||||
.thenReturn(new ArraySet<>(List.of(entry1.getKey(), entry2.getKey(),
|
|
||||||
entry3.getKey())));
|
|
||||||
|
|
||||||
// WHEN a new app ops code comes in
|
|
||||||
mAppOpsCallback.onActiveStateChanged(47, NOTIF_USER_ID, TEST_PKG, true);
|
|
||||||
mExecutor.runAllReady();
|
|
||||||
|
|
||||||
// THEN all entries get updated
|
|
||||||
assertEquals(
|
|
||||||
new ArraySet<>(List.of(47)),
|
|
||||||
entry1.mActiveAppOps);
|
|
||||||
assertEquals(
|
|
||||||
new ArraySet<>(List.of(47)),
|
|
||||||
entry2.mActiveAppOps);
|
|
||||||
assertEquals(
|
|
||||||
new ArraySet<>(List.of(47)),
|
|
||||||
entry3.mActiveAppOps);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAppOpsAreRemoved() {
|
|
||||||
// GIVEN One notification which is associated with app ops
|
|
||||||
NotificationEntry entry = new NotificationEntryBuilder()
|
|
||||||
.setUser(new UserHandle(NOTIF_USER_ID))
|
|
||||||
.setPkg(TEST_PKG)
|
|
||||||
.setId(2)
|
|
||||||
.build();
|
|
||||||
when(mNotifPipeline.getAllNotifs()).thenReturn(List.of(entry));
|
|
||||||
when(mForegroundServiceController.getStandardLayoutKeys(0, TEST_PKG))
|
|
||||||
.thenReturn(new ArraySet<>(List.of(entry.getKey())));
|
|
||||||
|
|
||||||
// GIVEN that the notification's app ops are already [47, 33]
|
|
||||||
mAppOpsCallback.onActiveStateChanged(47, NOTIF_USER_ID, TEST_PKG, true);
|
|
||||||
mAppOpsCallback.onActiveStateChanged(33, NOTIF_USER_ID, TEST_PKG, true);
|
|
||||||
mExecutor.runAllReady();
|
|
||||||
assertEquals(
|
|
||||||
new ArraySet<>(List.of(47, 33)),
|
|
||||||
entry.mActiveAppOps);
|
|
||||||
|
|
||||||
// WHEN one of the app ops is removed
|
|
||||||
mAppOpsCallback.onActiveStateChanged(47, NOTIF_USER_ID, TEST_PKG, false);
|
|
||||||
mExecutor.runAllReady();
|
|
||||||
|
|
||||||
// THEN the entry's active app ops are updated as well
|
|
||||||
assertEquals(
|
|
||||||
new ArraySet<>(List.of(33)),
|
|
||||||
entry.mActiveAppOps);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testNullAppOps() {
|
|
||||||
// GIVEN one notification with app ops
|
|
||||||
NotificationEntry entry = new NotificationEntryBuilder()
|
|
||||||
.setUser(new UserHandle(NOTIF_USER_ID))
|
|
||||||
.setPkg(TEST_PKG)
|
|
||||||
.setId(2)
|
|
||||||
.build();
|
|
||||||
entry.mActiveAppOps.clear();
|
|
||||||
entry.mActiveAppOps.addAll(List.of(47, 33));
|
|
||||||
|
|
||||||
// WHEN the notification is updated and the foreground service controller returns null for
|
|
||||||
// this notification
|
|
||||||
when(mForegroundServiceController.getAppOps(entry.getSbn().getUser().getIdentifier(),
|
|
||||||
entry.getSbn().getPackageName())).thenReturn(null);
|
|
||||||
mNotifCollectionListener.onEntryUpdated(entry);
|
|
||||||
|
|
||||||
// THEN the entry's active app ops is updated to empty
|
|
||||||
assertTrue(entry.mActiveAppOps.isEmpty());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,230 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.row;
|
|
||||||
|
|
||||||
import static android.app.AppOpsManager.OP_CAMERA;
|
|
||||||
import static android.app.AppOpsManager.OP_RECORD_AUDIO;
|
|
||||||
import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
|
|
||||||
|
|
||||||
import static junit.framework.Assert.assertEquals;
|
|
||||||
import static junit.framework.Assert.assertTrue;
|
|
||||||
|
|
||||||
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.eq;
|
|
||||||
import static org.mockito.Mockito.mock;
|
|
||||||
import static org.mockito.Mockito.times;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
import android.app.Notification;
|
|
||||||
import android.content.pm.ApplicationInfo;
|
|
||||||
import android.content.pm.PackageInfo;
|
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.os.UserHandle;
|
|
||||||
import android.service.notification.StatusBarNotification;
|
|
||||||
import android.test.suitebuilder.annotation.SmallTest;
|
|
||||||
import android.testing.AndroidTestingRunner;
|
|
||||||
import android.testing.UiThreadTest;
|
|
||||||
import android.util.ArraySet;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import com.android.internal.logging.testing.UiEventLoggerFake;
|
|
||||||
import com.android.systemui.R;
|
|
||||||
import com.android.systemui.SysuiTestCase;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
|
|
||||||
import java.util.concurrent.CountDownLatch;
|
|
||||||
|
|
||||||
@SmallTest
|
|
||||||
@RunWith(AndroidTestingRunner.class)
|
|
||||||
@UiThreadTest
|
|
||||||
public class AppOpsInfoTest extends SysuiTestCase {
|
|
||||||
private static final String TEST_PACKAGE_NAME = "test_package";
|
|
||||||
private static final int TEST_UID = 1;
|
|
||||||
|
|
||||||
private AppOpsInfo mAppOpsInfo;
|
|
||||||
private final PackageManager mMockPackageManager = mock(PackageManager.class);
|
|
||||||
private final NotificationGuts mGutsParent = mock(NotificationGuts.class);
|
|
||||||
private StatusBarNotification mSbn;
|
|
||||||
private UiEventLoggerFake mUiEventLogger = new UiEventLoggerFake();
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() throws Exception {
|
|
||||||
// Inflate the layout
|
|
||||||
final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
|
|
||||||
mAppOpsInfo = (AppOpsInfo) layoutInflater.inflate(R.layout.app_ops_info, null);
|
|
||||||
mAppOpsInfo.setGutsParent(mGutsParent);
|
|
||||||
|
|
||||||
// PackageManager must return a packageInfo and applicationInfo.
|
|
||||||
final PackageInfo packageInfo = new PackageInfo();
|
|
||||||
packageInfo.packageName = TEST_PACKAGE_NAME;
|
|
||||||
when(mMockPackageManager.getPackageInfo(eq(TEST_PACKAGE_NAME), anyInt()))
|
|
||||||
.thenReturn(packageInfo);
|
|
||||||
final ApplicationInfo applicationInfo = new ApplicationInfo();
|
|
||||||
applicationInfo.uid = TEST_UID; // non-zero
|
|
||||||
when(mMockPackageManager.getApplicationInfo(anyString(), anyInt())).thenReturn(
|
|
||||||
applicationInfo);
|
|
||||||
|
|
||||||
mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0,
|
|
||||||
new Notification(), UserHandle.CURRENT, null, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testBindNotification_SetsTextApplicationName() {
|
|
||||||
when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name");
|
|
||||||
mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, new ArraySet<>());
|
|
||||||
final TextView textView = mAppOpsInfo.findViewById(R.id.pkgname);
|
|
||||||
assertTrue(textView.getText().toString().contains("App Name"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testBindNotification_SetsPackageIcon() {
|
|
||||||
final Drawable iconDrawable = mock(Drawable.class);
|
|
||||||
when(mMockPackageManager.getApplicationIcon(any(ApplicationInfo.class)))
|
|
||||||
.thenReturn(iconDrawable);
|
|
||||||
mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, new ArraySet<>());
|
|
||||||
final ImageView iconView = mAppOpsInfo.findViewById(R.id.pkgicon);
|
|
||||||
assertEquals(iconDrawable, iconView.getDrawable());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testBindNotification_SetsOnClickListenerForSettings() throws Exception {
|
|
||||||
ArraySet<Integer> expectedOps = new ArraySet<>();
|
|
||||||
expectedOps.add(OP_CAMERA);
|
|
||||||
final CountDownLatch latch = new CountDownLatch(1);
|
|
||||||
mAppOpsInfo.bindGuts(mMockPackageManager, (View v, String pkg, int uid,
|
|
||||||
ArraySet<Integer> ops) -> {
|
|
||||||
assertEquals(TEST_PACKAGE_NAME, pkg);
|
|
||||||
assertEquals(expectedOps, ops);
|
|
||||||
assertEquals(TEST_UID, uid);
|
|
||||||
latch.countDown();
|
|
||||||
}, mSbn, mUiEventLogger, expectedOps);
|
|
||||||
|
|
||||||
final View settingsButton = mAppOpsInfo.findViewById(R.id.settings);
|
|
||||||
settingsButton.performClick();
|
|
||||||
// Verify that listener was triggered.
|
|
||||||
assertEquals(0, latch.getCount());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testBindNotification_LogsOpen() throws Exception {
|
|
||||||
mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, new ArraySet<>());
|
|
||||||
assertEquals(1, mUiEventLogger.numLogs());
|
|
||||||
assertEquals(NotificationAppOpsEvent.NOTIFICATION_APP_OPS_OPEN.getId(),
|
|
||||||
mUiEventLogger.eventId(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testOk() {
|
|
||||||
ArraySet<Integer> expectedOps = new ArraySet<>();
|
|
||||||
expectedOps.add(OP_CAMERA);
|
|
||||||
final CountDownLatch latch = new CountDownLatch(1);
|
|
||||||
mAppOpsInfo.bindGuts(mMockPackageManager, (View v, String pkg, int uid,
|
|
||||||
ArraySet<Integer> ops) -> {
|
|
||||||
assertEquals(TEST_PACKAGE_NAME, pkg);
|
|
||||||
assertEquals(expectedOps, ops);
|
|
||||||
assertEquals(TEST_UID, uid);
|
|
||||||
latch.countDown();
|
|
||||||
}, mSbn, mUiEventLogger, expectedOps);
|
|
||||||
|
|
||||||
final View okButton = mAppOpsInfo.findViewById(R.id.ok);
|
|
||||||
okButton.performClick();
|
|
||||||
assertEquals(1, latch.getCount());
|
|
||||||
verify(mGutsParent, times(1)).closeControls(eq(okButton), anyBoolean());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testPrompt_camera() {
|
|
||||||
ArraySet<Integer> expectedOps = new ArraySet<>();
|
|
||||||
expectedOps.add(OP_CAMERA);
|
|
||||||
mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, expectedOps);
|
|
||||||
TextView prompt = mAppOpsInfo.findViewById(R.id.prompt);
|
|
||||||
assertEquals("This app is using the camera.", prompt.getText());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testPrompt_mic() {
|
|
||||||
ArraySet<Integer> expectedOps = new ArraySet<>();
|
|
||||||
expectedOps.add(OP_RECORD_AUDIO);
|
|
||||||
mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, expectedOps);
|
|
||||||
TextView prompt = mAppOpsInfo.findViewById(R.id.prompt);
|
|
||||||
assertEquals("This app is using the microphone.", prompt.getText());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testPrompt_overlay() {
|
|
||||||
ArraySet<Integer> expectedOps = new ArraySet<>();
|
|
||||||
expectedOps.add(OP_SYSTEM_ALERT_WINDOW);
|
|
||||||
mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, expectedOps);
|
|
||||||
TextView prompt = mAppOpsInfo.findViewById(R.id.prompt);
|
|
||||||
assertEquals("This app is displaying over other apps on your screen.", prompt.getText());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testPrompt_camera_mic() {
|
|
||||||
ArraySet<Integer> expectedOps = new ArraySet<>();
|
|
||||||
expectedOps.add(OP_CAMERA);
|
|
||||||
expectedOps.add(OP_RECORD_AUDIO);
|
|
||||||
mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, expectedOps);
|
|
||||||
TextView prompt = mAppOpsInfo.findViewById(R.id.prompt);
|
|
||||||
assertEquals("This app is using the microphone and camera.", prompt.getText());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testPrompt_camera_mic_overlay() {
|
|
||||||
ArraySet<Integer> expectedOps = new ArraySet<>();
|
|
||||||
expectedOps.add(OP_CAMERA);
|
|
||||||
expectedOps.add(OP_RECORD_AUDIO);
|
|
||||||
expectedOps.add(OP_SYSTEM_ALERT_WINDOW);
|
|
||||||
mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, expectedOps);
|
|
||||||
TextView prompt = mAppOpsInfo.findViewById(R.id.prompt);
|
|
||||||
assertEquals("This app is displaying over other apps on your screen and using"
|
|
||||||
+ " the microphone and camera.", prompt.getText());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testPrompt_camera_overlay() {
|
|
||||||
ArraySet<Integer> expectedOps = new ArraySet<>();
|
|
||||||
expectedOps.add(OP_CAMERA);
|
|
||||||
expectedOps.add(OP_SYSTEM_ALERT_WINDOW);
|
|
||||||
mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, expectedOps);
|
|
||||||
TextView prompt = mAppOpsInfo.findViewById(R.id.prompt);
|
|
||||||
assertEquals("This app is displaying over other apps on your screen and using"
|
|
||||||
+ " the camera.", prompt.getText());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testPrompt_mic_overlay() {
|
|
||||||
ArraySet<Integer> expectedOps = new ArraySet<>();
|
|
||||||
expectedOps.add(OP_RECORD_AUDIO);
|
|
||||||
expectedOps.add(OP_SYSTEM_ALERT_WINDOW);
|
|
||||||
mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, expectedOps);
|
|
||||||
TextView prompt = mAppOpsInfo.findViewById(R.id.prompt);
|
|
||||||
assertEquals("This app is displaying over other apps on your screen and using"
|
|
||||||
+ " the microphone.", prompt.getText());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -35,12 +35,10 @@ import static org.mockito.Mockito.times;
|
|||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import android.app.AppOpsManager;
|
|
||||||
import android.app.NotificationChannel;
|
import android.app.NotificationChannel;
|
||||||
import android.testing.AndroidTestingRunner;
|
import android.testing.AndroidTestingRunner;
|
||||||
import android.testing.TestableLooper;
|
import android.testing.TestableLooper;
|
||||||
import android.testing.TestableLooper.RunWithLooper;
|
import android.testing.TestableLooper.RunWithLooper;
|
||||||
import android.util.ArraySet;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import androidx.test.filters.SmallTest;
|
import androidx.test.filters.SmallTest;
|
||||||
@@ -212,46 +210,6 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
|
|||||||
any(NotificationMenuRowPlugin.MenuItem.class));
|
any(NotificationMenuRowPlugin.MenuItem.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testShowAppOps_noHeader() {
|
|
||||||
// public notification is custom layout - no header
|
|
||||||
mGroupRow.setSensitive(true, true);
|
|
||||||
mGroupRow.setAppOpsOnClickListener(null);
|
|
||||||
mGroupRow.showAppOpsIcons(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testShowAppOpsIcons_header() {
|
|
||||||
NotificationContentView publicLayout = mock(NotificationContentView.class);
|
|
||||||
mGroupRow.setPublicLayout(publicLayout);
|
|
||||||
NotificationContentView privateLayout = mock(NotificationContentView.class);
|
|
||||||
mGroupRow.setPrivateLayout(privateLayout);
|
|
||||||
NotificationChildrenContainer mockContainer = mock(NotificationChildrenContainer.class);
|
|
||||||
when(mockContainer.getNotificationChildCount()).thenReturn(1);
|
|
||||||
mGroupRow.setChildrenContainer(mockContainer);
|
|
||||||
|
|
||||||
ArraySet<Integer> ops = new ArraySet<>();
|
|
||||||
ops.add(AppOpsManager.OP_ANSWER_PHONE_CALLS);
|
|
||||||
mGroupRow.showAppOpsIcons(ops);
|
|
||||||
|
|
||||||
verify(mockContainer, times(1)).showAppOpsIcons(ops);
|
|
||||||
verify(privateLayout, times(1)).showAppOpsIcons(ops);
|
|
||||||
verify(publicLayout, times(1)).showAppOpsIcons(ops);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAppOpsOnClick() {
|
|
||||||
ExpandableNotificationRow.OnAppOpsClickListener l = mock(
|
|
||||||
ExpandableNotificationRow.OnAppOpsClickListener.class);
|
|
||||||
View view = mock(View.class);
|
|
||||||
|
|
||||||
mGroupRow.setAppOpsOnClickListener(l);
|
|
||||||
|
|
||||||
mGroupRow.getAppOpsOnClickListener().onClick(view);
|
|
||||||
verify(l, times(1)).onClick(any(), anyInt(), anyInt(), any());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testHeadsUpAnimatingAwayListener() {
|
public void testHeadsUpAnimatingAwayListener() {
|
||||||
mGroupRow.setHeadsUpAnimatingAway(true);
|
mGroupRow.setHeadsUpAnimatingAway(true);
|
||||||
|
|||||||
@@ -74,32 +74,6 @@ public class NotificationContentViewTest extends SysuiTestCase {
|
|||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
@UiThreadTest
|
|
||||||
public void testShowAppOpsIcons() {
|
|
||||||
View mockContracted = mock(NotificationHeaderView.class);
|
|
||||||
when(mockContracted.findViewById(com.android.internal.R.id.mic))
|
|
||||||
.thenReturn(mockContracted);
|
|
||||||
View mockExpanded = mock(NotificationHeaderView.class);
|
|
||||||
when(mockExpanded.findViewById(com.android.internal.R.id.mic))
|
|
||||||
.thenReturn(mockExpanded);
|
|
||||||
View mockHeadsUp = mock(NotificationHeaderView.class);
|
|
||||||
when(mockHeadsUp.findViewById(com.android.internal.R.id.mic))
|
|
||||||
.thenReturn(mockHeadsUp);
|
|
||||||
|
|
||||||
mView.setContractedChild(mockContracted);
|
|
||||||
mView.setExpandedChild(mockExpanded);
|
|
||||||
mView.setHeadsUpChild(mockHeadsUp);
|
|
||||||
|
|
||||||
ArraySet<Integer> ops = new ArraySet<>();
|
|
||||||
ops.add(AppOpsManager.OP_RECORD_AUDIO);
|
|
||||||
mView.showAppOpsIcons(ops);
|
|
||||||
|
|
||||||
verify(mockContracted, times(1)).setVisibility(View.VISIBLE);
|
|
||||||
verify(mockExpanded, times(1)).setVisibility(View.VISIBLE);
|
|
||||||
verify(mockHeadsUp, times(1)).setVisibility(View.VISIBLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@UiThreadTest
|
@UiThreadTest
|
||||||
public void testExpandButtonFocusIsCalled() {
|
public void testExpandButtonFocusIsCalled() {
|
||||||
|
|||||||
@@ -421,7 +421,6 @@ public class NotificationTestHelper {
|
|||||||
mBindStage,
|
mBindStage,
|
||||||
mock(OnExpandClickListener.class),
|
mock(OnExpandClickListener.class),
|
||||||
mock(NotificationMediaManager.class),
|
mock(NotificationMediaManager.class),
|
||||||
mock(ExpandableNotificationRow.OnAppOpsClickListener.class),
|
|
||||||
mock(FalsingManager.class),
|
mock(FalsingManager.class),
|
||||||
mStatusBarStateController,
|
mStatusBarStateController,
|
||||||
mPeopleNotificationIdentifier);
|
mPeopleNotificationIdentifier);
|
||||||
|
|||||||
Reference in New Issue
Block a user