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