1 /* 2 * Copyright (C) 2017 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 package com.android.settings.applications.appinfo; 17 18 import android.app.AlertDialog; 19 import android.app.AppOpsManager; 20 import android.content.Context; 21 import android.os.Bundle; 22 import android.support.v14.preference.SwitchPreference; 23 import android.support.v7.preference.Preference; 24 import android.support.v7.preference.Preference.OnPreferenceChangeListener; 25 26 import com.android.internal.annotations.VisibleForTesting; 27 import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 28 import com.android.settings.R; 29 import com.android.settings.applications.AppInfoWithHeader; 30 import com.android.settings.overlay.FeatureFactory; 31 32 import static android.app.AppOpsManager.MODE_ALLOWED; 33 import static android.app.AppOpsManager.MODE_ERRORED; 34 import static android.app.AppOpsManager.OP_PICTURE_IN_PICTURE; 35 36 public class PictureInPictureDetails extends AppInfoWithHeader 37 implements OnPreferenceChangeListener { 38 39 private static final String KEY_APP_OPS_SETTINGS_SWITCH = "app_ops_settings_switch"; 40 private static final String LOG_TAG = "PictureInPictureDetails"; 41 42 private SwitchPreference mSwitchPref; 43 44 @Override onCreate(Bundle savedInstanceState)45 public void onCreate(Bundle savedInstanceState) { 46 super.onCreate(savedInstanceState); 47 48 // find preferences 49 addPreferencesFromResource(R.xml.picture_in_picture_permissions_details); 50 mSwitchPref = (SwitchPreference) findPreference(KEY_APP_OPS_SETTINGS_SWITCH); 51 52 // set title/summary for all of them 53 mSwitchPref.setTitle(R.string.picture_in_picture_app_detail_switch); 54 55 // install event listeners 56 mSwitchPref.setOnPreferenceChangeListener(this); 57 } 58 59 @Override onPreferenceChange(Preference preference, Object newValue)60 public boolean onPreferenceChange(Preference preference, Object newValue) { 61 if (preference == mSwitchPref) { 62 logSpecialPermissionChange((Boolean) newValue, mPackageName); 63 setEnterPipStateForPackage(getActivity(), mPackageInfo.applicationInfo.uid, 64 mPackageName, (Boolean) newValue); 65 return true; 66 } 67 return false; 68 } 69 70 @Override refreshUi()71 protected boolean refreshUi() { 72 boolean isAllowed = getEnterPipStateForPackage(getActivity(), 73 mPackageInfo.applicationInfo.uid, mPackageName); 74 mSwitchPref.setChecked(isAllowed); 75 return true; 76 } 77 78 @Override createDialog(int id, int errorCode)79 protected AlertDialog createDialog(int id, int errorCode) { 80 return null; 81 } 82 83 @Override getMetricsCategory()84 public int getMetricsCategory() { 85 return MetricsEvent.SETTINGS_MANAGE_PICTURE_IN_PICTURE; 86 } 87 88 /** 89 * Sets whether the app associated with the given {@param packageName} is allowed to enter 90 * picture-in-picture. 91 */ setEnterPipStateForPackage(Context context, int uid, String packageName, boolean value)92 static void setEnterPipStateForPackage(Context context, int uid, String packageName, 93 boolean value) { 94 final AppOpsManager appOps = context.getSystemService(AppOpsManager.class); 95 final int newMode = value ? MODE_ALLOWED : MODE_ERRORED; 96 appOps.setMode(OP_PICTURE_IN_PICTURE, uid, packageName, newMode); 97 } 98 99 /** 100 * @return whether the app associated with the given {@param packageName} is allowed to enter 101 * picture-in-picture. 102 */ getEnterPipStateForPackage(Context context, int uid, String packageName)103 static boolean getEnterPipStateForPackage(Context context, int uid, String packageName) { 104 final AppOpsManager appOps = context.getSystemService(AppOpsManager.class); 105 return appOps.checkOpNoThrow(OP_PICTURE_IN_PICTURE, uid, packageName) == MODE_ALLOWED; 106 } 107 108 /** 109 * @return the summary for the current state of whether the app associated with the given 110 * {@param packageName} is allowed to enter picture-in-picture. 111 */ getPreferenceSummary(Context context, int uid, String packageName)112 public static int getPreferenceSummary(Context context, int uid, String packageName) { 113 final boolean enabled = PictureInPictureDetails.getEnterPipStateForPackage(context, uid, 114 packageName); 115 return enabled ? R.string.app_permission_summary_allowed 116 : R.string.app_permission_summary_not_allowed; 117 } 118 119 @VisibleForTesting logSpecialPermissionChange(boolean newState, String packageName)120 void logSpecialPermissionChange(boolean newState, String packageName) { 121 int logCategory = newState 122 ? MetricsEvent.APP_PICTURE_IN_PICTURE_ALLOW 123 : MetricsEvent.APP_PICTURE_IN_PICTURE_DENY; 124 FeatureFactory.getFactory(getContext()) 125 .getMetricsFeatureProvider().action(getContext(), logCategory, packageName); 126 } 127 } 128