1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package com.android.systemui.statusbar.notification.row; 18 19 import android.app.AppOpsManager; 20 import android.content.Context; 21 import android.content.pm.ApplicationInfo; 22 import android.content.pm.PackageManager; 23 import android.graphics.drawable.Drawable; 24 import android.service.notification.StatusBarNotification; 25 import android.util.ArraySet; 26 import android.util.AttributeSet; 27 import android.view.View; 28 import android.view.accessibility.AccessibilityEvent; 29 import android.widget.ImageView; 30 import android.widget.LinearLayout; 31 import android.widget.TextView; 32 33 import com.android.internal.logging.MetricsLogger; 34 import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 35 import com.android.systemui.R; 36 37 /** 38 * The guts of a notification revealed when performing a long press. 39 */ 40 public class AppOpsInfo extends LinearLayout implements NotificationGuts.GutsContent { 41 private static final String TAG = "AppOpsGuts"; 42 43 private PackageManager mPm; 44 45 private String mPkg; 46 private String mAppName; 47 private int mAppUid; 48 private StatusBarNotification mSbn; 49 private ArraySet<Integer> mAppOps; 50 private MetricsLogger mMetricsLogger; 51 private OnSettingsClickListener mOnSettingsClickListener; 52 private NotificationGuts mGutsContainer; 53 54 private OnClickListener mOnOk = v -> { 55 closeControls(v); 56 }; 57 AppOpsInfo(Context context, AttributeSet attrs)58 public AppOpsInfo(Context context, AttributeSet attrs) { 59 super(context, attrs); 60 } 61 62 public interface OnSettingsClickListener { onClick(View v, String pkg, int uid, ArraySet<Integer> ops)63 void onClick(View v, String pkg, int uid, ArraySet<Integer> ops); 64 } 65 bindGuts(final PackageManager pm, final OnSettingsClickListener onSettingsClick, final StatusBarNotification sbn, ArraySet<Integer> activeOps)66 public void bindGuts(final PackageManager pm, 67 final OnSettingsClickListener onSettingsClick, 68 final StatusBarNotification sbn, 69 ArraySet<Integer> activeOps) { 70 mPkg = sbn.getPackageName(); 71 mSbn = sbn; 72 mPm = pm; 73 mAppName = mPkg; 74 mOnSettingsClickListener = onSettingsClick; 75 mAppOps = activeOps; 76 77 bindHeader(); 78 bindPrompt(); 79 bindButtons(); 80 81 mMetricsLogger = new MetricsLogger(); 82 mMetricsLogger.visibility(MetricsEvent.APP_OPS_GUTS, true); 83 } 84 bindHeader()85 private void bindHeader() { 86 // Package name 87 Drawable pkgicon = null; 88 ApplicationInfo info; 89 try { 90 info = mPm.getApplicationInfo(mPkg, 91 PackageManager.MATCH_UNINSTALLED_PACKAGES 92 | PackageManager.MATCH_DISABLED_COMPONENTS 93 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE 94 | PackageManager.MATCH_DIRECT_BOOT_AWARE); 95 if (info != null) { 96 mAppUid = mSbn.getUid(); 97 mAppName = String.valueOf(mPm.getApplicationLabel(info)); 98 pkgicon = mPm.getApplicationIcon(info); 99 } 100 } catch (PackageManager.NameNotFoundException e) { 101 // app is gone, just show package name and generic icon 102 pkgicon = mPm.getDefaultActivityIcon(); 103 } 104 ((ImageView) findViewById(R.id.pkgicon)).setImageDrawable(pkgicon); 105 ((TextView) findViewById(R.id.pkgname)).setText(mAppName); 106 } 107 bindPrompt()108 private void bindPrompt() { 109 final TextView prompt = findViewById(R.id.prompt); 110 prompt.setText(getPrompt()); 111 } 112 bindButtons()113 private void bindButtons() { 114 View settings = findViewById(R.id.settings); 115 settings.setOnClickListener((View view) -> { 116 mOnSettingsClickListener.onClick(view, mPkg, mAppUid, mAppOps); 117 }); 118 TextView ok = findViewById(R.id.ok); 119 ok.setOnClickListener(mOnOk); 120 } 121 getPrompt()122 private String getPrompt() { 123 if (mAppOps == null || mAppOps.size() == 0) { 124 return ""; 125 } else if (mAppOps.size() == 1) { 126 if (mAppOps.contains(AppOpsManager.OP_CAMERA)) { 127 return mContext.getString(R.string.appops_camera); 128 } else if (mAppOps.contains(AppOpsManager.OP_RECORD_AUDIO)) { 129 return mContext.getString(R.string.appops_microphone); 130 } else { 131 return mContext.getString(R.string.appops_overlay); 132 } 133 } else if (mAppOps.size() == 2) { 134 if (mAppOps.contains(AppOpsManager.OP_CAMERA)) { 135 if (mAppOps.contains(AppOpsManager.OP_RECORD_AUDIO)) { 136 return mContext.getString(R.string.appops_camera_mic); 137 } else { 138 return mContext.getString(R.string.appops_camera_overlay); 139 } 140 } else { 141 return mContext.getString(R.string.appops_mic_overlay); 142 } 143 } else { 144 return mContext.getString(R.string.appops_camera_mic_overlay); 145 } 146 } 147 148 @Override onInitializeAccessibilityEvent(AccessibilityEvent event)149 public void onInitializeAccessibilityEvent(AccessibilityEvent event) { 150 super.onInitializeAccessibilityEvent(event); 151 if (mGutsContainer != null && 152 event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) { 153 if (mGutsContainer.isExposed()) { 154 event.getText().add(mContext.getString( 155 R.string.notification_channel_controls_opened_accessibility, mAppName)); 156 } else { 157 event.getText().add(mContext.getString( 158 R.string.notification_channel_controls_closed_accessibility, mAppName)); 159 } 160 } 161 } 162 closeControls(View v)163 private void closeControls(View v) { 164 mMetricsLogger.visibility(MetricsEvent.APP_OPS_GUTS, false); 165 int[] parentLoc = new int[2]; 166 int[] targetLoc = new int[2]; 167 mGutsContainer.getLocationOnScreen(parentLoc); 168 v.getLocationOnScreen(targetLoc); 169 final int centerX = v.getWidth() / 2; 170 final int centerY = v.getHeight() / 2; 171 final int x = targetLoc[0] - parentLoc[0] + centerX; 172 final int y = targetLoc[1] - parentLoc[1] + centerY; 173 mGutsContainer.closeControls(x, y, false, false); 174 } 175 176 @Override setGutsParent(NotificationGuts guts)177 public void setGutsParent(NotificationGuts guts) { 178 mGutsContainer = guts; 179 } 180 181 @Override willBeRemoved()182 public boolean willBeRemoved() { 183 return false; 184 } 185 186 @Override shouldBeSaved()187 public boolean shouldBeSaved() { 188 return false; 189 } 190 191 @Override getContentView()192 public View getContentView() { 193 return this; 194 } 195 196 @Override handleCloseControls(boolean save, boolean force)197 public boolean handleCloseControls(boolean save, boolean force) { 198 return false; 199 } 200 201 @Override getActualHeight()202 public int getActualHeight() { 203 return getHeight(); 204 } 205 } 206