1 /* 2 * Copyright (C) 2017 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.packageinstaller.permission.ui.handheld; 18 19 import android.app.Activity; 20 import android.app.DialogFragment; 21 import android.content.Intent; 22 import android.content.IntentSender; 23 import android.content.pm.PackageInfo; 24 import android.graphics.drawable.Drawable; 25 import android.os.Bundle; 26 import android.os.RemoteCallback; 27 import android.preference.Preference; 28 import android.preference.PreferenceCategory; 29 import android.preference.PreferenceFragment; 30 import android.preference.PreferenceGroup; 31 import android.preference.PreferenceScreen; 32 import android.preference.SwitchPreference; 33 import android.preference.TwoStatePreference; 34 import android.text.Html; 35 import android.text.Spanned; 36 import android.view.View; 37 import android.widget.Button; 38 import android.widget.ImageView; 39 import android.widget.TextView; 40 import com.android.packageinstaller.R; 41 import com.android.packageinstaller.permission.model.AppPermissionGroup; 42 import com.android.packageinstaller.permission.model.AppPermissions; 43 import com.android.packageinstaller.permission.model.Permission; 44 import com.android.packageinstaller.permission.ui.ConfirmActionDialogFragment; 45 import com.android.packageinstaller.permission.ui.ManagePermissionsActivity; 46 import com.android.packageinstaller.permission.utils.ArrayUtils; 47 import com.android.packageinstaller.permission.utils.Utils; 48 49 import java.util.List; 50 51 public final class ReviewPermissionsFragment extends PreferenceFragment 52 implements View.OnClickListener, Preference.OnPreferenceChangeListener, 53 ConfirmActionDialogFragment.OnActionConfirmedListener { 54 55 public static final String EXTRA_PACKAGE_INFO = 56 "com.android.packageinstaller.permission.ui.extra.PACKAGE_INFO"; 57 58 private AppPermissions mAppPermissions; 59 60 private Button mContinueButton; 61 private Button mCancelButton; 62 private Button mMoreInfoButton; 63 64 private PreferenceCategory mNewPermissionsCategory; 65 66 private boolean mHasConfirmedRevoke; 67 68 public static ReviewPermissionsFragment newInstance(PackageInfo packageInfo) { 69 Bundle arguments = new Bundle(); 70 arguments.putParcelable(ReviewPermissionsFragment.EXTRA_PACKAGE_INFO, packageInfo); 71 ReviewPermissionsFragment instance = new ReviewPermissionsFragment(); 72 instance.setArguments(arguments); 73 instance.setRetainInstance(true); 74 return instance; 75 } 76 77 @Override 78 public void onCreate(Bundle savedInstanceState) { 79 super.onCreate(savedInstanceState); 80 81 Activity activity = getActivity(); 82 if (activity == null) { 83 return; 84 } 85 86 PackageInfo packageInfo = getArguments().getParcelable(EXTRA_PACKAGE_INFO); 87 if (packageInfo == null) { 88 activity.finish(); 89 return; 90 } 91 92 mAppPermissions = new AppPermissions(activity, packageInfo, null, false, 93 new Runnable() { 94 @Override 95 public void run() { 96 getActivity().finish(); 97 } 98 }); 99 100 if (mAppPermissions.getPermissionGroups().isEmpty()) { 101 activity.finish(); 102 return; 103 } 104 105 boolean reviewRequired = false; 106 for (AppPermissionGroup group : mAppPermissions.getPermissionGroups()) { 107 if (group.isReviewRequired()) { 108 reviewRequired = true; 109 break; 110 } 111 } 112 113 if (!reviewRequired) { 114 activity.finish(); 115 } 116 } 117 118 @Override 119 public void onViewCreated(View view, Bundle savedInstanceState) { 120 super.onViewCreated(view, savedInstanceState); 121 bindUi(); 122 } 123 124 @Override 125 public void onResume() { 126 super.onResume(); 127 mAppPermissions.refresh(); 128 loadPreferences(); 129 } 130 131 @Override 132 public void onClick(View view) { 133 Activity activity = getActivity(); 134 if (activity == null) { 135 return; 136 } 137 if (view == mContinueButton) { 138 confirmPermissionsReview(); 139 executeCallback(true); 140 } else if (view == mCancelButton) { 141 executeCallback(false); 142 activity.setResult(Activity.RESULT_CANCELED); 143 } else if (view == mMoreInfoButton) { 144 Intent intent = new Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS); 145 intent.putExtra(Intent.EXTRA_PACKAGE_NAME, 146 mAppPermissions.getPackageInfo().packageName); 147 intent.putExtra(ManagePermissionsActivity.EXTRA_ALL_PERMISSIONS, true); 148 getActivity().startActivity(intent); 149 } 150 activity.finish(); 151 } 152 153 @Override 154 public boolean onPreferenceChange(Preference preference, Object newValue) { 155 if (mHasConfirmedRevoke) { 156 return true; 157 } 158 if (preference instanceof SwitchPreference) { 159 SwitchPreference switchPreference = (SwitchPreference) preference; 160 if (switchPreference.isChecked()) { 161 showWarnRevokeDialog(switchPreference.getKey()); 162 } else { 163 return true; 164 } 165 } 166 return false; 167 } 168 169 @Override 170 public void onActionConfirmed(String action) { 171 Preference preference = getPreferenceManager().findPreference(action); 172 if (preference instanceof SwitchPreference) { 173 SwitchPreference switchPreference = (SwitchPreference) preference; 174 switchPreference.setChecked(false); 175 mHasConfirmedRevoke = true; 176 } 177 } 178 179 private void showWarnRevokeDialog(final String groupName) { 180 DialogFragment fragment = ConfirmActionDialogFragment.newInstance( 181 getString(R.string.old_sdk_deny_warning), groupName); 182 fragment.show(getFragmentManager(), fragment.getClass().getName()); 183 } 184 185 private void confirmPermissionsReview() { 186 PreferenceGroup preferenceGroup = mNewPermissionsCategory != null 187 ? mNewPermissionsCategory : getPreferenceScreen(); 188 189 final int preferenceCount = preferenceGroup.getPreferenceCount(); 190 for (int i = 0; i < preferenceCount; i++) { 191 Preference preference = preferenceGroup.getPreference(i); 192 if (preference instanceof TwoStatePreference) { 193 TwoStatePreference twoStatePreference = (TwoStatePreference) preference; 194 String groupName = preference.getKey(); 195 AppPermissionGroup group = mAppPermissions.getPermissionGroup(groupName); 196 if (twoStatePreference.isChecked()) { 197 String[] permissionsToGrant = null; 198 final int permissionCount = group.getPermissions().size(); 199 for (int j = 0; j < permissionCount; j++) { 200 final Permission permission = group.getPermissions().get(j); 201 if (permission.isReviewRequired()) { 202 permissionsToGrant = ArrayUtils.appendString( 203 permissionsToGrant, permission.getName()); 204 } 205 } 206 if (permissionsToGrant != null) { 207 group.grantRuntimePermissions(false, permissionsToGrant); 208 } 209 } else { 210 group.revokeRuntimePermissions(false); 211 } 212 group.resetReviewRequired(); 213 } 214 } 215 } 216 217 private void bindUi() { 218 Activity activity = getActivity(); 219 if (activity == null) { 220 return; 221 } 222 223 // Set icon 224 Drawable icon = mAppPermissions.getPackageInfo().applicationInfo.loadIcon( 225 activity.getPackageManager()); 226 ImageView iconView = (ImageView) activity.findViewById(R.id.app_icon); 227 iconView.setImageDrawable(icon); 228 229 // Set message 230 final int labelTemplateResId = isPackageUpdated() 231 ? R.string.permission_review_title_template_update 232 : R.string.permission_review_title_template_install; 233 Spanned message = Html.fromHtml(getString(labelTemplateResId, 234 mAppPermissions.getAppLabel()), 0); 235 236 // Set the permission message as the title so it can be announced. 237 activity.setTitle(message.toString()); 238 239 // Color the app name. 240 TextView permissionsMessageView = (TextView) activity.findViewById( 241 R.id.permissions_message); 242 permissionsMessageView.setText(message); 243 244 mContinueButton = (Button) getActivity().findViewById(R.id.continue_button); 245 mContinueButton.setOnClickListener(this); 246 247 mCancelButton = (Button) getActivity().findViewById(R.id.cancel_button); 248 mCancelButton.setOnClickListener(this); 249 250 mMoreInfoButton = (Button) getActivity().findViewById( 251 R.id.permission_more_info_button); 252 mMoreInfoButton.setOnClickListener(this); 253 } 254 255 private void loadPreferences() { 256 Activity activity = getActivity(); 257 if (activity == null) { 258 return; 259 } 260 261 PreferenceScreen screen = getPreferenceScreen(); 262 if (screen == null) { 263 screen = getPreferenceManager().createPreferenceScreen(getActivity()); 264 setPreferenceScreen(screen); 265 } else { 266 screen.removeAll(); 267 } 268 269 PreferenceGroup currentPermissionsCategory = null; 270 PreferenceGroup oldNewPermissionsCategory = mNewPermissionsCategory; 271 mNewPermissionsCategory = null; 272 273 final boolean isPackageUpdated = isPackageUpdated(); 274 275 for (AppPermissionGroup group : mAppPermissions.getPermissionGroups()) { 276 if (!Utils.shouldShowPermission(group, mAppPermissions.getPackageInfo().packageName) 277 || !Utils.OS_PKG.equals(group.getDeclaringPackage())) { 278 continue; 279 } 280 281 final SwitchPreference preference; 282 Preference cachedPreference = oldNewPermissionsCategory != null 283 ? oldNewPermissionsCategory.findPreference(group.getName()) : null; 284 if (cachedPreference instanceof SwitchPreference) { 285 preference = (SwitchPreference) cachedPreference; 286 } else { 287 preference = new SwitchPreference(getActivity()); 288 289 preference.setKey(group.getName()); 290 Drawable icon = Utils.loadDrawable(activity.getPackageManager(), 291 group.getIconPkg(), group.getIconResId()); 292 preference.setIcon(Utils.applyTint(getContext(), icon, 293 android.R.attr.colorControlNormal)); 294 preference.setTitle(group.getLabel()); 295 preference.setSummary(group.getDescription()); 296 preference.setPersistent(false); 297 298 preference.setOnPreferenceChangeListener(this); 299 } 300 301 preference.setChecked(group.areRuntimePermissionsGranted() 302 || group.isReviewRequired()); 303 304 // Mutable state 305 if (group.isPolicyFixed()) { 306 preference.setEnabled(false); 307 preference.setSummary(getString( 308 R.string.permission_summary_enforced_by_policy)); 309 } else { 310 preference.setEnabled(true); 311 } 312 313 if (group.isReviewRequired()) { 314 if (!isPackageUpdated) { 315 screen.addPreference(preference); 316 } else { 317 if (mNewPermissionsCategory == null) { 318 mNewPermissionsCategory = new PreferenceCategory(activity); 319 mNewPermissionsCategory.setTitle(R.string.new_permissions_category); 320 mNewPermissionsCategory.setOrder(1); 321 screen.addPreference(mNewPermissionsCategory); 322 } 323 mNewPermissionsCategory.addPreference(preference); 324 } 325 } else { 326 if (currentPermissionsCategory == null) { 327 currentPermissionsCategory = new PreferenceCategory(activity); 328 currentPermissionsCategory.setTitle(R.string.current_permissions_category); 329 currentPermissionsCategory.setOrder(2); 330 screen.addPreference(currentPermissionsCategory); 331 } 332 currentPermissionsCategory.addPreference(preference); 333 } 334 } 335 } 336 337 private boolean isPackageUpdated() { 338 List<AppPermissionGroup> groups = mAppPermissions.getPermissionGroups(); 339 final int groupCount = groups.size(); 340 for (int i = 0; i < groupCount; i++) { 341 AppPermissionGroup group = groups.get(i); 342 if (!group.isReviewRequired()) { 343 return true; 344 } 345 } 346 return false; 347 } 348 349 private void executeCallback(boolean success) { 350 Activity activity = getActivity(); 351 if (activity == null) { 352 return; 353 } 354 if (success) { 355 IntentSender intent = activity.getIntent().getParcelableExtra(Intent.EXTRA_INTENT); 356 if (intent != null) { 357 try { 358 int flagMask = 0; 359 int flagValues = 0; 360 if (activity.getIntent().getBooleanExtra( 361 Intent.EXTRA_RESULT_NEEDED, false)) { 362 flagMask = Intent.FLAG_ACTIVITY_FORWARD_RESULT; 363 flagValues = Intent.FLAG_ACTIVITY_FORWARD_RESULT; 364 } 365 activity.startIntentSenderForResult(intent, -1, null, 366 flagMask, flagValues, 0); 367 } catch (IntentSender.SendIntentException e) { 368 /* ignore */ 369 } 370 return; 371 } 372 } 373 RemoteCallback callback = activity.getIntent().getParcelableExtra( 374 Intent.EXTRA_REMOTE_CALLBACK); 375 if (callback != null) { 376 Bundle result = new Bundle(); 377 result.putBoolean(Intent.EXTRA_RETURN_RESULT, success); 378 callback.sendResult(result); 379 } 380 } 381 } 382