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