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