• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.permissioncontroller.permission.ui.handheld;
18 
19 import static android.Manifest.permission_group.CAMERA;
20 import static android.Manifest.permission_group.MICROPHONE;
21 
22 import static com.android.permissioncontroller.Constants.EXTRA_SESSION_ID;
23 import static com.android.permissioncontroller.permission.debug.UtilsKt.getUsageDurationString;
24 import static com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_CALLER_NAME;
25 import static com.android.permissioncontroller.permission.ui.handheld.AppPermissionFragment.GRANT_CATEGORY;
26 import static com.android.permissioncontroller.permission.utils.KotlinUtilsKt.navigateSafe;
27 
28 import android.content.Context;
29 import android.content.Intent;
30 import android.graphics.drawable.Drawable;
31 import android.os.Bundle;
32 import android.os.UserHandle;
33 import android.text.TextUtils;
34 import android.view.LayoutInflater;
35 import android.view.View;
36 import android.view.ViewGroup;
37 import android.widget.ImageView;
38 import android.widget.TextView;
39 
40 import androidx.annotation.NonNull;
41 import androidx.annotation.Nullable;
42 import androidx.navigation.Navigation;
43 import androidx.preference.Preference;
44 import androidx.preference.PreferenceViewHolder;
45 
46 import com.android.permissioncontroller.permission.model.AppPermissionGroup;
47 import com.android.permissioncontroller.permission.model.AppPermissionUsage.GroupUsage;
48 import com.android.permissioncontroller.R;
49 import com.android.permissioncontroller.permission.ui.LocationProviderInterceptDialog;
50 import com.android.permissioncontroller.permission.utils.LocationUtils;
51 
52 import java.util.List;
53 
54 /**
55  * A preference that links to the screen where a permission can be toggled.
56  */
57 public class PermissionControlPreference extends Preference {
58     private final @NonNull Context mContext;
59     private @Nullable Drawable mWidgetIcon;
60     private @Nullable String mGranted;
61     private boolean mUseSmallerIcon;
62     private boolean mEllipsizeEnd;
63     private @Nullable List<Integer> mTitleIcons;
64     private @Nullable List<Integer> mSummaryIcons;
65     private @NonNull String mPackageName;
66     private @NonNull String mPermGroupName;
67     private @NonNull String mCaller;
68     private @NonNull long mSessionId;
69     private boolean mHasNavGraph;
70     private @NonNull UserHandle mUser;
71 
PermissionControlPreference(@onNull Context context, @NonNull AppPermissionGroup group, @NonNull String caller)72     public PermissionControlPreference(@NonNull Context context,
73             @NonNull AppPermissionGroup group, @NonNull String caller) {
74         this(context, group, caller, 0);
75     }
76 
PermissionControlPreference(@onNull Context context, @NonNull AppPermissionGroup group, @NonNull String caller, long sessionId)77     public PermissionControlPreference(@NonNull Context context,
78             @NonNull AppPermissionGroup group, @NonNull String caller, long sessionId) {
79         this(context, group.getApp().packageName, group.getName(), group.getUser(), caller,
80                 sessionId, null, false);
81     }
82 
PermissionControlPreference(@onNull Context context, @NonNull String packageName, @NonNull String permGroupName, @NonNull UserHandle user, @NonNull String caller, long sessionId, String granted, boolean hasNavGraph)83     public PermissionControlPreference(@NonNull Context context,
84             @NonNull String packageName, @NonNull String permGroupName, @NonNull UserHandle user,
85             @NonNull String caller, long sessionId, String granted, boolean hasNavGraph) {
86         super(context);
87         mContext = context;
88         mWidgetIcon = null;
89         mUseSmallerIcon = false;
90         mEllipsizeEnd = false;
91         mTitleIcons = null;
92         mSummaryIcons = null;
93         mPackageName = packageName;
94         mCaller = caller;
95         mPermGroupName = permGroupName;
96         mSessionId = sessionId;
97         mUser = user;
98         mGranted = granted;
99         mHasNavGraph = hasNavGraph;
100     }
101 
102     /**
103      * Sets this preference's right icon.
104      *
105      * Note that this must be called before preference layout to take effect.
106      *
107      * @param widgetIcon the icon to use.
108      */
setRightIcon(@onNull Drawable widgetIcon)109     public void setRightIcon(@NonNull Drawable widgetIcon) {
110         mWidgetIcon = widgetIcon;
111         setWidgetLayoutResource(R.layout.image_view);
112     }
113 
114     /**
115      * Sets this preference's left icon to be smaller than normal.
116      *
117      * Note that this must be called before preference layout to take effect.
118      */
useSmallerIcon()119     public void useSmallerIcon() {
120         mUseSmallerIcon = true;
121     }
122 
123     /**
124      * Sets this preference's title to use an ellipsis at the end.
125      *
126      * Note that this must be called before preference layout to take effect.
127      */
setEllipsizeEnd()128     public void setEllipsizeEnd() {
129         mEllipsizeEnd = true;
130     }
131 
132     /**
133      * Sets this preference's summary based on the group it represents, if applicable.
134      *
135      * @param group the permission group this preference represents.
136      */
setGroupSummary(@onNull AppPermissionGroup group)137     public void setGroupSummary(@NonNull AppPermissionGroup group) {
138         if (group.hasPermissionWithBackgroundMode() && group.areRuntimePermissionsGranted()) {
139             AppPermissionGroup backgroundGroup = group.getBackgroundPermissions();
140             if (backgroundGroup == null || !backgroundGroup.areRuntimePermissionsGranted()) {
141                 setSummary(R.string.permission_subtitle_only_in_foreground);
142                 return;
143             }
144         }
145         setSummary("");
146     }
147 
148     /**
149      * Sets this preference's summary based on its permission usage.
150      *
151      * @param groupUsage the usage information
152      * @param accessTimeStr the string representing the last access time
153      */
setUsageSummary(@onNull GroupUsage groupUsage, @NonNull String accessTimeStr)154     public void setUsageSummary(@NonNull GroupUsage groupUsage, @NonNull String accessTimeStr) {
155         long backgroundAccessCount = groupUsage.getBackgroundAccessCount();
156         long duration = 0;
157         String groupName = groupUsage.getGroup().getName();
158         if (groupName.equals(CAMERA) || groupName.equals(MICROPHONE)) {
159             duration = groupUsage.getAccessDuration();
160         }
161         if (backgroundAccessCount == 0) {
162             long numForegroundAccesses = groupUsage.getForegroundAccessCount();
163             if (duration == 0) {
164                 setSummary(mContext.getResources().getQuantityString(
165                         R.plurals.permission_usage_summary, (int) numForegroundAccesses,
166                         accessTimeStr, numForegroundAccesses));
167             } else {
168                 setSummary(mContext.getResources().getQuantityString(
169                         R.plurals.permission_usage_summary_duration, (int) numForegroundAccesses,
170                         accessTimeStr, numForegroundAccesses,
171                         getUsageDurationString(mContext, groupUsage)));
172             }
173         } else {
174             long numAccesses = groupUsage.getAccessCount();
175             if (duration == 0) {
176                 setSummary(mContext.getResources().getQuantityString(
177                         R.plurals.permission_usage_summary_background, (int) numAccesses,
178                         accessTimeStr, numAccesses, backgroundAccessCount));
179             } else {
180                 setSummary(mContext.getResources().getQuantityString(
181                         R.plurals.permission_usage_summary_background_duration, (int) numAccesses,
182                         accessTimeStr, numAccesses, backgroundAccessCount,
183                         getUsageDurationString(mContext, groupUsage)));
184             }
185         }
186     }
187 
188     /**
189      * Sets this preference to show the given icons to the left of its title.
190      *
191      * @param titleIcons the icons to show.
192      */
setTitleIcons(@onNull List<Integer> titleIcons)193     public void setTitleIcons(@NonNull List<Integer> titleIcons) {
194         mTitleIcons = titleIcons;
195         setLayoutResource(R.layout.preference_usage);
196     }
197 
198     @Override
onBindViewHolder(PreferenceViewHolder holder)199     public void onBindViewHolder(PreferenceViewHolder holder) {
200         if (mUseSmallerIcon) {
201             ImageView icon = ((ImageView) holder.findViewById(android.R.id.icon));
202             icon.setMaxWidth(
203                     mContext.getResources().getDimensionPixelSize(R.dimen.secondary_app_icon_size));
204             icon.setMaxHeight(
205                     mContext.getResources().getDimensionPixelSize(R.dimen.secondary_app_icon_size));
206         }
207 
208         super.onBindViewHolder(holder);
209 
210         if (mWidgetIcon != null) {
211             View widgetFrame = holder.findViewById(android.R.id.widget_frame);
212             ((ImageView) widgetFrame.findViewById(R.id.icon)).setImageDrawable(mWidgetIcon);
213         }
214 
215         if (mEllipsizeEnd) {
216             TextView title = (TextView) holder.findViewById(android.R.id.title);
217             title.setMaxLines(1);
218             title.setEllipsize(TextUtils.TruncateAt.END);
219         }
220 
221         setIcons(holder, mSummaryIcons, R.id.summary_widget_frame);
222         setIcons(holder, mTitleIcons, R.id.title_widget_frame);
223 
224         setOnPreferenceClickListener(pref -> {
225             if (LocationUtils.isLocationGroupAndProvider(
226                     mContext, mPermGroupName, mPackageName)) {
227                 Intent intent = new Intent(mContext, LocationProviderInterceptDialog.class);
228                 intent.putExtra(Intent.EXTRA_PACKAGE_NAME, mPackageName);
229                 mContext.startActivityAsUser(intent, mUser);
230             } else if (LocationUtils.isLocationGroupAndControllerExtraPackage(
231                     mContext, mPermGroupName, mPackageName)) {
232                 // Redirect to location controller extra package settings.
233                 LocationUtils.startLocationControllerExtraPackageSettings(mContext, mUser);
234             } else if (mHasNavGraph) {
235                 Bundle args = new Bundle();
236                 args.putString(Intent.EXTRA_PACKAGE_NAME, mPackageName);
237                 args.putString(Intent.EXTRA_PERMISSION_GROUP_NAME, mPermGroupName);
238                 args.putParcelable(Intent.EXTRA_USER, mUser);
239                 args.putString(EXTRA_CALLER_NAME, mCaller);
240                 args.putLong(EXTRA_SESSION_ID, mSessionId);
241                 args.putString(GRANT_CATEGORY, mGranted);
242                 navigateSafe(Navigation.findNavController(holder.itemView), R.id.perm_groups_to_app,
243                         args);
244             } else {
245                 // TODO ntmyren, yianyliu: Remove once Auto has been adapted to new permission model
246                 // see b/150229448
247                 Intent intent = new Intent(Intent.ACTION_MANAGE_APP_PERMISSION);
248                 intent.putExtra(Intent.EXTRA_PACKAGE_NAME, mPackageName);
249                 intent.putExtra(Intent.EXTRA_PERMISSION_GROUP_NAME, mPermGroupName);
250                 intent.putExtra(Intent.EXTRA_USER, mUser);
251                 intent.putExtra(EXTRA_CALLER_NAME, mCaller);
252                 intent.putExtra(EXTRA_SESSION_ID, mSessionId);
253                 mContext.startActivity(intent);
254             }
255             return true;
256         });
257     }
258 
setIcons(PreferenceViewHolder holder, @Nullable List<Integer> icons, int frameId)259     private void setIcons(PreferenceViewHolder holder, @Nullable List<Integer> icons, int frameId) {
260         ViewGroup frame = (ViewGroup) holder.findViewById(frameId);
261         if (icons != null && !icons.isEmpty()) {
262             frame.setVisibility(View.VISIBLE);
263             frame.removeAllViews();
264             LayoutInflater inflater = mContext.getSystemService(LayoutInflater.class);
265             int numIcons = icons.size();
266             for (int i = 0; i < numIcons; i++) {
267                 ViewGroup group = (ViewGroup) inflater.inflate(R.layout.title_summary_image_view,
268                         null);
269                 ImageView imageView = group.requireViewById(R.id.icon);
270                 imageView.setImageResource(icons.get(i));
271                 frame.addView(group);
272             }
273         } else if (frame != null) {
274             frame.setVisibility(View.GONE);
275         }
276     }
277 }
278