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