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