Merge "Remove app ops indicators from notifications" into rvc-qpr-dev

This commit is contained in:
Julia Reynolds
2020-08-14 17:32:44 +00:00
committed by Android (Google) Code Review
25 changed files with 4 additions and 1183 deletions

View File

@@ -5151,18 +5151,10 @@ public class Notification implements Parcelable
bindHeaderChronometerAndTime(contentView, p);
bindProfileBadge(contentView, p);
bindAlertedIcon(contentView, p);
bindActivePermissions(contentView, p);
bindExpandButton(contentView, p);
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) {
int color = isColorized(p) ? getPrimaryTextColor(p) : getSecondaryTextColor(p);
contentView.setDrawableTint(R.id.expand_button, false, color,

View File

@@ -52,13 +52,11 @@ public class NotificationHeaderView extends ViewGroup {
private View mHeaderText;
private View mSecondaryHeaderText;
private OnClickListener mExpandClickListener;
private OnClickListener mAppOpsListener;
private HeaderTouchListener mTouchListener = new HeaderTouchListener();
private LinearLayout mTransferChip;
private NotificationExpandButton mExpandButton;
private CachingIconView mIcon;
private View mProfileBadge;
private View mAppOps;
private boolean mExpanded;
private boolean mShowExpandButtonAtEnd;
private boolean mShowWorkBadgeAtEnd;
@@ -115,7 +113,6 @@ public class NotificationHeaderView extends ViewGroup {
mExpandButton = findViewById(com.android.internal.R.id.expand_button);
mIcon = findViewById(com.android.internal.R.id.icon);
mProfileBadge = findViewById(com.android.internal.R.id.profile_badge);
mAppOps = findViewById(com.android.internal.R.id.app_ops);
}
@Override
@@ -143,7 +140,6 @@ public class NotificationHeaderView extends ViewGroup {
// Icons that should go at the end
if ((child == mExpandButton && mShowExpandButtonAtEnd)
|| child == mProfileBadge
|| child == mAppOps
|| child == mTransferChip) {
iconWidth += lp.leftMargin + lp.rightMargin + child.getMeasuredWidth();
} else {
@@ -208,7 +204,6 @@ public class NotificationHeaderView extends ViewGroup {
// Icons that should go at the end
if ((child == mExpandButton && mShowExpandButtonAtEnd)
|| child == mProfileBadge
|| child == mAppOps
|| child == mTransferChip) {
if (end == getMeasuredWidth()) {
layoutRight = end - mContentEndMargin;
@@ -277,22 +272,10 @@ public class NotificationHeaderView extends ViewGroup {
}
private void updateTouchListener() {
if (mExpandClickListener == null && mAppOpsListener == null) {
setOnTouchListener(null);
return;
}
setOnTouchListener(mTouchListener);
mTouchListener.bindTouchRects();
}
/**
* Sets onclick listener for app ops icons.
*/
public void setAppOpsOnClickListener(OnClickListener l) {
mAppOpsListener = l;
updateTouchListener();
}
@Override
public void setOnClickListener(@Nullable OnClickListener l) {
mExpandClickListener = l;
@@ -380,7 +363,6 @@ public class NotificationHeaderView extends ViewGroup {
private final ArrayList<Rect> mTouchRects = new ArrayList<>();
private Rect mExpandButtonRect;
private Rect mAppOpsRect;
private int mTouchSlop;
private boolean mTrackGesture;
private float mDownX;
@@ -393,8 +375,6 @@ public class NotificationHeaderView extends ViewGroup {
mTouchRects.clear();
addRectAroundView(mIcon);
mExpandButtonRect = addRectAroundView(mExpandButton);
mAppOpsRect = addRectAroundView(mAppOps);
setTouchDelegate(new TouchDelegate(mAppOpsRect, mAppOps));
addWidthRect();
mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
}
@@ -455,11 +435,6 @@ public class NotificationHeaderView extends ViewGroup {
break;
case MotionEvent.ACTION_UP:
if (mTrackGesture) {
if (mAppOps.isVisibleToUser() && (mAppOpsRect.contains((int) x, (int) y)
|| mAppOpsRect.contains((int) mDownX, (int) mDownY))) {
mAppOps.performClick();
return true;
}
mExpandButton.performClick();
}
break;

View File

@@ -167,8 +167,6 @@ public class ConversationLayout extends FrameLayout
private int mFacePileProtectionWidthExpanded;
private boolean mImportantConversation;
private TextView mUnreadBadge;
private ViewGroup mAppOps;
private Rect mAppOpsTouchRect = new Rect();
private float mMinTouchSize;
private Icon mConversationIcon;
private Icon mShortcutIcon;
@@ -210,7 +208,6 @@ public class ConversationLayout extends FrameLayout
mConversationIconView = findViewById(R.id.conversation_icon);
mConversationIconContainer = findViewById(R.id.conversation_icon_container);
mIcon = findViewById(R.id.icon);
mAppOps = findViewById(com.android.internal.R.id.app_ops);
mMinTouchSize = 48 * getResources().getDisplayMetrics().density;
mImportanceRingView = findViewById(R.id.conversation_icon_badge_ring);
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() {

View File

@@ -146,43 +146,6 @@
android:visibility="gone"
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
layout="@layout/notification_material_media_transfer_action"
android:id="@+id/media_seamless"

View File

@@ -74,11 +74,6 @@ public interface NotificationMenuRowPlugin extends Plugin {
*/
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.
*/

View File

@@ -39,21 +39,15 @@ import javax.inject.Singleton;
*/
@Singleton
public class ForegroundServiceController {
public static final int[] APP_OPS = new int[] {AppOpsManager.OP_CAMERA,
AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
AppOpsManager.OP_RECORD_AUDIO,
AppOpsManager.OP_COARSE_LOCATION,
AppOpsManager.OP_FINE_LOCATION};
public static final int[] APP_OPS = new int[] {AppOpsManager.OP_SYSTEM_ALERT_WINDOW};
private final SparseArray<ForegroundServicesUserState> mUserServices = new SparseArray<>();
private final Object mMutex = new Object();
private final NotificationEntryManager mEntryManager;
private final Handler mMainHandler;
@Inject
public ForegroundServiceController(NotificationEntryManager entryManager,
AppOpsController appOpsController, @Main Handler mainHandler) {
mEntryManager = entryManager;
public ForegroundServiceController(AppOpsController appOpsController,
@Main Handler mainHandler) {
mMainHandler = mainHandler;
appOpsController.addCallback(APP_OPS, (code, uid, packageName, active) -> {
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
*/
@@ -140,31 +121,6 @@ public class ForegroundServiceController {
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);
}
}
}
/**

View File

@@ -172,24 +172,8 @@ public class ForegroundServiceNotificationListener {
sbn.getPackageName(), sbn.getKey());
}
}
tagAppOps(entry);
return true;
},
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);
}
}
}
}

View File

@@ -486,7 +486,6 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
}
}
row.showAppOpsIcons(entry.mActiveAppOps);
row.setLastAudiblyAlertedMs(entry.getLastAudiblyAlertedMs());
}

View File

@@ -76,14 +76,9 @@ public class AppOpsCoordinator implements Coordinator {
// extend the lifetime of foreground notification services to show for at least 5 seconds
mNotifPipeline.addNotificationLifetimeExtender(mForegroundLifetimeExtender);
// listen for new notifications to add appOps
mNotifPipeline.addCollectionListener(mNotifCollectionListener);
// filter out foreground service notifications that aren't necessary anymore
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;
}
}

View File

@@ -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());
}
}
}

View File

@@ -239,7 +239,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
private boolean mShowNoBackground;
private ExpandableNotificationRow mNotificationParent;
private OnExpandClickListener mOnExpandClickListener;
private View.OnClickListener mOnAppOpsClickListener;
// Listener will be called when receiving a long click event.
// 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.createInfoItem(mContext));
items.add(NotificationMenuRow.createSnoozeItem(mContext));
items.add(NotificationMenuRow.createAppOpsItem(mContext));
mMenuRow.setMenuItems(items);
}
if (existed) {
@@ -1609,7 +1607,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
RowContentBindStage rowContentBindStage,
OnExpandClickListener onExpandClickListener,
NotificationMediaManager notificationMediaManager,
OnAppOpsClickListener onAppOpsClickListener,
FalsingManager falsingManager,
StatusBarStateController statusBarStateController,
PeopleNotificationIdentifier peopleNotificationIdentifier) {
@@ -1629,7 +1626,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
mRowContentBindStage = rowContentBindStage;
mOnExpandClickListener = onExpandClickListener;
mMediaManager = notificationMediaManager;
setAppOpsOnClickListener(onAppOpsClickListener);
mFalsingManager = falsingManager;
mStatusbarStateController = statusBarStateController;
mPeopleNotificationIdentifier = peopleNotificationIdentifier;
@@ -1686,14 +1682,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
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. */
public void setLastAudiblyAlertedMs(long lastAudiblyAlertedMs) {
if (NotificationUtils.useNewInterruptionModel(mContext)) {
@@ -1722,24 +1710,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
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
protected void onFinishInflate() {
super.onFinishInflate();

View File

@@ -63,7 +63,6 @@ public class ExpandableNotificationRowController {
private final ExpandableNotificationRow.ExpansionLogger mExpansionLogger =
this::logNotificationExpansion;
private final ExpandableNotificationRow.OnAppOpsClickListener mOnAppOpsClickListener;
private final NotificationGutsManager mNotificationGutsManager;
private Runnable mOnDismissRunnable;
private final FalsingManager mFalsingManager;
@@ -101,7 +100,6 @@ public class ExpandableNotificationRowController {
mStatusBarStateController = statusBarStateController;
mNotificationGutsManager = notificationGutsManager;
mOnDismissRunnable = onDismissRunnable;
mOnAppOpsClickListener = mNotificationGutsManager::openGuts;
mAllowLongPress = allowLongPress;
mFalsingManager = falsingManager;
mPeopleNotificationIdentifier = peopleNotificationIdentifier;
@@ -122,7 +120,6 @@ public class ExpandableNotificationRowController {
mRowContentBindStage,
mOnExpandClickListener,
mMediaManager,
mOnAppOpsClickListener,
mFalsingManager,
mStatusBarStateController,
mPeopleNotificationIdentifier

View File

@@ -1567,18 +1567,6 @@ public class NotificationContentView extends FrameLayout {
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. */
public void setRecentlyAudiblyAlerted(boolean audiblyAlerted) {
if (mContractedChild != null) {

View File

@@ -264,8 +264,6 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
try {
if (gutsView instanceof NotificationSnooze) {
initializeSnoozeView(row, (NotificationSnooze) gutsView);
} else if (gutsView instanceof AppOpsInfo) {
initializeAppOpsInfo(row, (AppOpsInfo) gutsView);
} else if (gutsView instanceof NotificationInfo) {
initializeNotificationInfo(row, (NotificationInfo) gutsView);
} 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.
* @param row view to set up the guts for

View File

@@ -76,7 +76,6 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
private Context mContext;
private FrameLayout mMenuContainer;
private NotificationMenuItem mInfoItem;
private MenuItem mAppOpsItem;
private MenuItem mSnoozeItem;
private ArrayList<MenuItem> mLeftMenuItems;
private ArrayList<MenuItem> mRightMenuItems;
@@ -137,11 +136,6 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
return mInfoItem;
}
@Override
public MenuItem getAppOpsMenuItem(Context context) {
return mAppOpsItem;
}
@Override
public MenuItem getSnoozeMenuItem(Context context) {
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
mSnoozeItem = createSnoozeItem(mContext);
}
mAppOpsItem = createAppOpsItem(mContext);
NotificationEntry entry = mParent.getEntry();
int personNotifType = mPeopleNotificationIdentifier
.getPeopleNotificationType(entry.getSbn(), entry.getRanking());
@@ -280,7 +273,6 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
mRightMenuItems.add(mSnoozeItem);
}
mRightMenuItems.add(mInfoItem);
mRightMenuItems.add(mAppOpsItem);
mLeftMenuItems.addAll(mRightMenuItems);
populateMenuViews();
@@ -688,14 +680,6 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
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) {
View menuView = item.getMenuView();
if (menuView != null) {

View File

@@ -18,7 +18,6 @@ package com.android.systemui.statusbar.notification.row.wrapper;
import static com.android.systemui.statusbar.notification.TransformState.TRANSFORM_Y;
import android.app.AppOpsManager;
import android.app.Notification;
import android.content.Context;
import android.util.ArraySet;
@@ -64,10 +63,6 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
private TextView mHeaderText;
private TextView mAppNameText;
private ImageView mWorkProfileImage;
private View mCameraIcon;
private View mMicIcon;
private View mOverlayIcon;
private View mAppOps;
private View mAudiblyAlertedIcon;
private FrameLayout mIconContainer;
@@ -108,7 +103,6 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
}
}, TRANSFORMING_VIEW_TITLE);
resolveHeaderViews();
addAppOpsOnClickListener(row);
}
protected void resolveHeaderViews() {
@@ -119,10 +113,6 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
mExpandButton = mView.findViewById(com.android.internal.R.id.expand_button);
mWorkProfileImage = mView.findViewById(com.android.internal.R.id.profile_badge);
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);
if (mNotificationHeader != null) {
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
public void onContentUpdated(ExpandableNotificationRow row) {
super.onContentUpdated(row);
@@ -285,15 +243,6 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TITLE,
mHeaderText);
}
if (mCameraIcon != null) {
mTransformationHelper.addViewTransformingToSimilar(mCameraIcon);
}
if (mMicIcon != null) {
mTransformationHelper.addViewTransformingToSimilar(mMicIcon);
}
if (mOverlayIcon != null) {
mTransformationHelper.addViewTransformingToSimilar(mOverlayIcon);
}
if (mAudiblyAlertedIcon != null) {
mTransformationHelper.addViewTransformingToSimilar(mAudiblyAlertedIcon);
}

View File

@@ -96,14 +96,6 @@ public abstract class NotificationViewWrapper implements TransformableView {
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() {
if (shouldClearBackgroundOnReapply()) {
mBackgroundColor = 0;

View File

@@ -1302,20 +1302,6 @@ public class NotificationChildrenContainer extends ViewGroup {
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) {
if (mNotificationHeaderWrapper != null) {
mNotificationHeaderWrapper.setRecentlyAudiblyAlerted(audiblyAlertedRecently);

View File

@@ -82,8 +82,7 @@ public class ForegroundServiceControllerTest extends SysuiTestCase {
allowTestableLooperAsMainThread();
MockitoAnnotations.initMocks(this);
mFsc = new ForegroundServiceController(
mEntryManager, mAppOpsController, mMainHandler);
mFsc = new ForegroundServiceController(mAppOpsController, mMainHandler);
mListener = new ForegroundServiceNotificationListener(
mContext, mFsc, mEntryManager, mNotifPipeline,
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
public void testAppOpsCRUD() {
// no crash on remove that doesn't exist

View File

@@ -210,19 +210,6 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase {
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
public void testReentrantCallsToOnDynamicPrivacyChangedPostForLater() {
// GIVEN a ListContainer that will make a re-entrant call to updateNotificationViews()

View File

@@ -72,8 +72,6 @@ public class AppOpsCoordinatorTest extends SysuiTestCase {
private Notification mNotification;
private AppOpsCoordinator mAppOpsCoordinator;
private NotifFilter mForegroundFilter;
private NotifCollectionListener mNotifCollectionListener;
private AppOpsController.Callback mAppOpsCallback;
private NotifLifetimeExtender mForegroundNotifLifetimeExtender;
private FakeSystemClock mClock = new FakeSystemClock();
@@ -110,18 +108,6 @@ public class AppOpsCoordinatorTest extends SysuiTestCase {
lifetimeExtenderCaptor.capture());
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
@@ -215,134 +201,4 @@ public class AppOpsCoordinatorTest extends SysuiTestCase {
assertFalse(mForegroundNotifLifetimeExtender
.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());
}
}

View File

@@ -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());
}
}

View File

@@ -35,12 +35,10 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.AppOpsManager;
import android.app.NotificationChannel;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.util.ArraySet;
import android.view.View;
import androidx.test.filters.SmallTest;
@@ -212,46 +210,6 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
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
public void testHeadsUpAnimatingAwayListener() {
mGroupRow.setHeadsUpAnimatingAway(true);

View File

@@ -74,32 +74,6 @@ public class NotificationContentViewTest extends SysuiTestCase {
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
@UiThreadTest
public void testExpandButtonFocusIsCalled() {

View File

@@ -421,7 +421,6 @@ public class NotificationTestHelper {
mBindStage,
mock(OnExpandClickListener.class),
mock(NotificationMediaManager.class),
mock(ExpandableNotificationRow.OnAppOpsClickListener.class),
mock(FalsingManager.class),
mStatusBarStateController,
mPeopleNotificationIdentifier);