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);
|
||||
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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -486,7 +486,6 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
|
||||
}
|
||||
}
|
||||
|
||||
row.showAppOpsIcons(entry.mActiveAppOps);
|
||||
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
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.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);
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -421,7 +421,6 @@ public class NotificationTestHelper {
|
||||
mBindStage,
|
||||
mock(OnExpandClickListener.class),
|
||||
mock(NotificationMediaManager.class),
|
||||
mock(ExpandableNotificationRow.OnAppOpsClickListener.class),
|
||||
mock(FalsingManager.class),
|
||||
mStatusBarStateController,
|
||||
mPeopleNotificationIdentifier);
|
||||
|
||||
Reference in New Issue
Block a user