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