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