/* * 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; import android.app.AppOpsManager; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.graphics.drawable.Drawable; import android.os.RemoteException; 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.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 mAppOps; private MetricsLogger mMetricsLogger; private OnSettingsClickListener mOnSettingsClickListener; private NotificationGuts mGutsContainer; private OnClickListener mOnOk = v -> { closeControls(v); }; public AppOpsInfo(Context context, AttributeSet attrs) { super(context, attrs); } public interface OnSettingsClickListener { void onClick(View v, String pkg, int uid, ArraySet ops); } public void bindGuts(final PackageManager pm, final OnSettingsClickListener onSettingsClick, final StatusBarNotification sbn, ArraySet activeOps) { mPkg = sbn.getPackageName(); mSbn = sbn; mPm = pm; mAppName = mPkg; mOnSettingsClickListener = onSettingsClick; mAppOps = activeOps; bindHeader(); bindPrompt(); bindButtons(); 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); } 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)); } } } private void closeControls(View v) { mMetricsLogger.visibility(MetricsEvent.APP_OPS_GUTS, false); int[] parentLoc = new int[2]; int[] targetLoc = new int[2]; mGutsContainer.getLocationOnScreen(parentLoc); v.getLocationOnScreen(targetLoc); final int centerX = v.getWidth() / 2; final int centerY = v.getHeight() / 2; final int x = targetLoc[0] - parentLoc[0] + centerX; final int y = targetLoc[1] - parentLoc[1] + centerY; mGutsContainer.closeControls(x, y, false, false); } @Override public void setGutsParent(NotificationGuts guts) { mGutsContainer = guts; } @Override public boolean willBeRemoved() { return false; } @Override public boolean shouldBeSaved() { return false; } @Override public View getContentView() { return this; } @Override public boolean handleCloseControls(boolean save, boolean force) { return false; } @Override public int getActualHeight() { return getHeight(); } }