AppRestrictionsFragment.java revision f88e6e5ae6a5a31d47677dcbd9be2b26c6615136
1/* 2 * Copyright (C) 2013 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 17package com.android.settings.users; 18 19import android.app.Activity; 20import android.app.AlertDialog; 21import android.app.AppGlobals; 22import android.app.Dialog; 23import android.app.Fragment; 24import android.appwidget.AppWidgetManager; 25import android.content.BroadcastReceiver; 26import android.content.Context; 27import android.content.DialogInterface; 28import android.content.Intent; 29import android.content.IntentFilter; 30import android.content.RestrictionEntry; 31import android.content.pm.ApplicationInfo; 32import android.content.pm.IPackageManager; 33import android.content.pm.PackageInfo; 34import android.content.pm.PackageManager; 35import android.content.pm.PackageManager.NameNotFoundException; 36import android.content.pm.ResolveInfo; 37import android.content.pm.UserInfo; 38import android.content.res.Resources; 39import android.database.Cursor; 40import android.graphics.Bitmap; 41import android.graphics.BitmapFactory; 42import android.graphics.ColorFilter; 43import android.graphics.ColorMatrix; 44import android.graphics.ColorMatrixColorFilter; 45import android.graphics.drawable.Drawable; 46import android.net.Uri; 47import android.os.AsyncTask; 48import android.os.Bundle; 49import android.os.RemoteException; 50import android.os.ServiceManager; 51import android.os.UserHandle; 52import android.os.UserManager; 53import android.preference.CheckBoxPreference; 54import android.preference.ListPreference; 55import android.preference.MultiSelectListPreference; 56import android.preference.Preference; 57import android.preference.Preference.OnPreferenceChangeListener; 58import android.preference.Preference.OnPreferenceClickListener; 59import android.preference.PreferenceGroup; 60import android.preference.SwitchPreference; 61import android.provider.ContactsContract.DisplayPhoto; 62import android.provider.MediaStore; 63import android.text.TextUtils; 64import android.util.Log; 65import android.view.LayoutInflater; 66import android.view.View; 67import android.view.View.OnClickListener; 68import android.view.inputmethod.InputMethod; 69import android.view.inputmethod.InputMethodInfo; 70import android.view.inputmethod.InputMethodManager; 71import android.view.ViewGroup; 72import android.view.WindowManager; 73import android.widget.AdapterView; 74import android.widget.ArrayAdapter; 75import android.widget.CompoundButton; 76import android.widget.CompoundButton.OnCheckedChangeListener; 77import android.widget.EditText; 78import android.widget.ImageView; 79import android.widget.ListAdapter; 80import android.widget.ListPopupWindow; 81import android.widget.Switch; 82import android.widget.TextView; 83 84import com.android.settings.R; 85import com.android.settings.SettingsPreferenceFragment; 86 87import java.io.File; 88import java.util.ArrayList; 89import java.util.Collections; 90import java.util.Comparator; 91import java.util.HashMap; 92import java.util.HashSet; 93import java.util.List; 94import java.util.Map; 95import java.util.Set; 96import java.util.StringTokenizer; 97 98public class AppRestrictionsFragment extends SettingsPreferenceFragment implements 99 OnPreferenceChangeListener, OnClickListener, OnPreferenceClickListener { 100 101 private static final String TAG = AppRestrictionsFragment.class.getSimpleName(); 102 103 private static final boolean DEBUG = false; 104 105 private static final String PKG_PREFIX = "pkg_"; 106 private static final String KEY_USER_INFO = "user_info"; 107 108 private static final int DIALOG_ID_EDIT_USER_INFO = 1; 109 110 private PackageManager mPackageManager; 111 private UserManager mUserManager; 112 private UserHandle mUser; 113 114 private PreferenceGroup mAppList; 115 116 private static final int MAX_APP_RESTRICTIONS = 100; 117 118 private static final String DELIMITER = ";"; 119 120 /** Key for extra passed in from calling fragment for the userId of the user being edited */ 121 public static final String EXTRA_USER_ID = "user_id"; 122 123 /** Key for extra passed in from calling fragment to indicate if this is a newly created user */ 124 public static final String EXTRA_NEW_USER = "new_user"; 125 126 private static final String KEY_SAVED_PHOTO = "pending_photo"; 127 128 HashMap<String,Boolean> mSelectedPackages = new HashMap<String,Boolean>(); 129 private boolean mFirstTime = true; 130 private boolean mNewUser; 131 private boolean mAppListChanged; 132 133 private int mCustomRequestCode; 134 private HashMap<Integer, AppRestrictionsPreference> mCustomRequestMap = 135 new HashMap<Integer,AppRestrictionsPreference>(); 136 private View mHeaderView; 137 private ImageView mUserIconView; 138 private TextView mUserNameView; 139 140 private List<SelectableAppInfo> mVisibleApps; 141 private List<ApplicationInfo> mUserApps; 142 143 private Dialog mEditUserInfoDialog; 144 145 private EditUserPhotoController mEditUserPhotoController; 146 private Bitmap mSavedPhoto; 147 148 private BroadcastReceiver mUserBackgrounding = new BroadcastReceiver() { 149 @Override 150 public void onReceive(Context context, Intent intent) { 151 // Update the user's app selection right away without waiting for a pause 152 // onPause() might come in too late, causing apps to disappear after broadcasts 153 // have been scheduled during user startup. 154 if (mAppListChanged) { 155 if (DEBUG) Log.d(TAG, "User backgrounding, update app list"); 156 updateUserAppList(); 157 if (DEBUG) Log.d(TAG, "User backgrounding, done updating app list"); 158 } 159 } 160 }; 161 162 static class SelectableAppInfo { 163 String packageName; 164 CharSequence appName; 165 CharSequence activityName; 166 Drawable icon; 167 SelectableAppInfo masterEntry; 168 169 @Override 170 public String toString() { 171 return packageName + ": appName=" + appName + "; activityName=" + activityName 172 + "; icon=" + icon + "; masterEntry=" + masterEntry; 173 } 174 } 175 176 static class AppRestrictionsPreference extends SwitchPreference { 177 private boolean hasSettings; 178 private OnClickListener listener; 179 private ArrayList<RestrictionEntry> restrictions; 180 boolean panelOpen; 181 private boolean immutable; 182 List<Preference> childPreferences = new ArrayList<Preference>(); 183 private SelectableAppInfo appInfo; 184 private final ColorFilter grayscaleFilter; 185 186 AppRestrictionsPreference(Context context, OnClickListener listener) { 187 super(context); 188 setLayoutResource(R.layout.preference_app_restrictions); 189 this.listener = listener; 190 191 ColorMatrix colorMatrix = new ColorMatrix(); 192 colorMatrix.setSaturation(0f); 193 float[] matrix = colorMatrix.getArray(); 194 matrix[18] = 0.5f; 195 grayscaleFilter = new ColorMatrixColorFilter(colorMatrix); 196 } 197 198 private void setSettingsEnabled(boolean enable) { 199 hasSettings = enable; 200 } 201 202 @Override 203 public void setChecked(boolean checked) { 204 if (checked) { 205 getIcon().setColorFilter(null); 206 } else { 207 getIcon().setColorFilter(grayscaleFilter); 208 } 209 super.setChecked(checked); 210 } 211 212 void setRestrictions(ArrayList<RestrictionEntry> restrictions) { 213 this.restrictions = restrictions; 214 } 215 216 void setImmutable(boolean immutable) { 217 this.immutable = immutable; 218 } 219 220 boolean isImmutable() { 221 return immutable; 222 } 223 224 void setSelectableAppInfo(SelectableAppInfo appInfo) { 225 this.appInfo = appInfo; 226 } 227 228 RestrictionEntry getRestriction(String key) { 229 if (restrictions == null) return null; 230 for (RestrictionEntry entry : restrictions) { 231 if (entry.getKey().equals(key)) { 232 return entry; 233 } 234 } 235 return null; 236 } 237 238 ArrayList<RestrictionEntry> getRestrictions() { 239 return restrictions; 240 } 241 242 @Override 243 protected void onBindView(View view) { 244 super.onBindView(view); 245 246 View appRestrictionsSettings = view.findViewById(R.id.app_restrictions_settings); 247 appRestrictionsSettings.setVisibility(hasSettings ? View.VISIBLE : View.GONE); 248 view.findViewById(R.id.settings_divider).setVisibility( 249 hasSettings ? View.VISIBLE : View.GONE); 250 appRestrictionsSettings.setOnClickListener(listener); 251 appRestrictionsSettings.setTag(this); 252 253 View appRestrictionsPref = view.findViewById(R.id.app_restrictions_pref); 254 appRestrictionsPref.setOnClickListener(listener); 255 appRestrictionsPref.setTag(this); 256 257 ViewGroup widget = (ViewGroup) view.findViewById(android.R.id.widget_frame); 258 widget.setEnabled(!isImmutable()); 259 if (widget.getChildCount() > 0) { 260 final Switch switchView = (Switch) widget.getChildAt(0); 261 switchView.setEnabled(!isImmutable()); 262 switchView.setTag(this); 263 switchView.setOnCheckedChangeListener(new OnCheckedChangeListener() { 264 @Override 265 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 266 listener.onClick(switchView); 267 } 268 }); 269 } 270 } 271 } 272 273 @Override 274 public void onCreate(Bundle icicle) { 275 super.onCreate(icicle); 276 277 if (icicle != null) { 278 mUser = new UserHandle(icicle.getInt(EXTRA_USER_ID)); 279 mSavedPhoto = (Bitmap) icicle.getParcelable(KEY_SAVED_PHOTO); 280 } else { 281 Bundle args = getArguments(); 282 283 if (args.containsKey(EXTRA_USER_ID)) { 284 mUser = new UserHandle(args.getInt(EXTRA_USER_ID)); 285 } 286 mNewUser = args.getBoolean(EXTRA_NEW_USER, false); 287 } 288 mPackageManager = getActivity().getPackageManager(); 289 mUserManager = (UserManager) getActivity().getSystemService(Context.USER_SERVICE); 290 addPreferencesFromResource(R.xml.app_restrictions); 291 mAppList = getPreferenceScreen(); 292 setHasOptionsMenu(true); 293 } 294 295 @Override 296 public void onActivityCreated(Bundle savedInstanceState) { 297 if (mHeaderView == null) { 298 mHeaderView = LayoutInflater.from(getActivity()).inflate( 299 R.layout.user_info_header, null); 300 ((ViewGroup) getListView().getParent()).addView(mHeaderView, 0); 301 mHeaderView.setOnClickListener(this); 302 mUserIconView = (ImageView) mHeaderView.findViewById(android.R.id.icon); 303 mUserNameView = (TextView) mHeaderView.findViewById(android.R.id.title); 304 getListView().setFastScrollEnabled(true); 305 } 306 // This is going to bind the preferences. 307 super.onActivityCreated(savedInstanceState); 308 } 309 310 @Override 311 public void onSaveInstanceState(Bundle outState) { 312 super.onSaveInstanceState(outState); 313 outState.putInt(EXTRA_USER_ID, mUser.getIdentifier()); 314 if (mEditUserInfoDialog != null && mEditUserInfoDialog.isShowing() 315 && mEditUserPhotoController != null) { 316 outState.putParcelable(KEY_SAVED_PHOTO, 317 mEditUserPhotoController.getNewUserPhotoBitmap()); 318 } 319 } 320 321 public void onResume() { 322 super.onResume(); 323 getActivity().registerReceiver(mUserBackgrounding, 324 new IntentFilter(Intent.ACTION_USER_BACKGROUND)); 325 mAppListChanged = false; 326 new AppLoadingTask().execute((Void[]) null); 327 328 UserInfo info = mUserManager.getUserInfo(mUser.getIdentifier()); 329 ((TextView) mHeaderView.findViewById(android.R.id.title)).setText(info.name); 330 ((ImageView) mHeaderView.findViewById(android.R.id.icon)).setImageDrawable( 331 getCircularUserIcon()); 332 } 333 334 public void onPause() { 335 super.onPause(); 336 mNewUser = false; 337 getActivity().unregisterReceiver(mUserBackgrounding); 338 if (mAppListChanged) { 339 new Thread() { 340 public void run() { 341 updateUserAppList(); 342 } 343 }.start(); 344 } 345 } 346 347 private Drawable getCircularUserIcon() { 348 Bitmap userIcon = mUserManager.getUserIcon(mUser.getIdentifier()); 349 CircleFramedDrawable circularIcon = 350 CircleFramedDrawable.getInstance(this.getActivity(), userIcon); 351 return circularIcon; 352 } 353 354 private void updateUserAppList() { 355 IPackageManager ipm = IPackageManager.Stub.asInterface( 356 ServiceManager.getService("package")); 357 final int userId = mUser.getIdentifier(); 358 if (!mUserManager.getUserInfo(userId).isRestricted()) { 359 Log.e(TAG, "Cannot apply application restrictions on a regular user!"); 360 return; 361 } 362 for (Map.Entry<String,Boolean> entry : mSelectedPackages.entrySet()) { 363 String packageName = entry.getKey(); 364 if (entry.getValue()) { 365 // Enable selected apps 366 try { 367 ApplicationInfo info = ipm.getApplicationInfo(packageName, 0, userId); 368 if (info == null || info.enabled == false) { 369 ipm.installExistingPackageAsUser(packageName, mUser.getIdentifier()); 370 if (DEBUG) { 371 Log.d(TAG, "Installing " + packageName); 372 } 373 } 374 } catch (RemoteException re) { 375 } 376 } else { 377 // Blacklist all other apps, system or downloaded 378 try { 379 ApplicationInfo info = ipm.getApplicationInfo(packageName, 0, userId); 380 if (info != null) { 381 ipm.deletePackageAsUser(entry.getKey(), null, mUser.getIdentifier(), 382 PackageManager.DELETE_SYSTEM_APP); 383 if (DEBUG) { 384 Log.d(TAG, "Uninstalling " + packageName); 385 } 386 } 387 } catch (RemoteException re) { 388 } 389 } 390 } 391 } 392 393 private boolean isSystemPackage(String packageName) { 394 try { 395 final PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0); 396 if (pi.applicationInfo == null) return false; 397 final int flags = pi.applicationInfo.flags; 398 if ((flags & ApplicationInfo.FLAG_SYSTEM) != 0 399 || (flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) { 400 return true; 401 } 402 } catch (NameNotFoundException nnfe) { 403 // Missing package? 404 } 405 return false; 406 } 407 408 /** 409 * Find all pre-installed input methods that are marked as default 410 * and add them to an exclusion list so that they aren't 411 * presented to the user for toggling. 412 * Don't add non-default ones, as they may include other stuff that we 413 * don't need to auto-include. 414 * @param excludePackages the set of package names to append to 415 */ 416 private void addSystemImes(Set<String> excludePackages) { 417 final Context context = getActivity(); 418 if (context == null) return; 419 InputMethodManager imm = (InputMethodManager) 420 context.getSystemService(Context.INPUT_METHOD_SERVICE); 421 List<InputMethodInfo> imis = imm.getInputMethodList(); 422 for (InputMethodInfo imi : imis) { 423 try { 424 if (imi.isDefault(context) && isSystemPackage(imi.getPackageName())) { 425 excludePackages.add(imi.getPackageName()); 426 } 427 } catch (Resources.NotFoundException rnfe) { 428 // Not default 429 } 430 } 431 } 432 433 /** 434 * Add system apps that match an intent to the list, excluding any packages in the exclude list. 435 * @param visibleApps list of apps to append the new list to 436 * @param intent the intent to match 437 * @param excludePackages the set of package names to be excluded, since they're required 438 */ 439 private void addSystemApps(List<SelectableAppInfo> visibleApps, Intent intent, 440 Set<String> excludePackages) { 441 if (getActivity() == null) return; 442 final PackageManager pm = mPackageManager; 443 List<ResolveInfo> launchableApps = pm.queryIntentActivities(intent, 444 PackageManager.GET_DISABLED_COMPONENTS); 445 for (ResolveInfo app : launchableApps) { 446 if (app.activityInfo != null && app.activityInfo.applicationInfo != null) { 447 int flags = app.activityInfo.applicationInfo.flags; 448 if ((flags & ApplicationInfo.FLAG_SYSTEM) != 0 449 || (flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) { 450 // System app 451 // Skip excluded packages 452 if (excludePackages.contains(app.activityInfo.packageName)) continue; 453 454 SelectableAppInfo info = new SelectableAppInfo(); 455 info.packageName = app.activityInfo.packageName; 456 info.appName = app.activityInfo.applicationInfo.loadLabel(pm); 457 info.icon = app.activityInfo.loadIcon(pm); 458 info.activityName = app.activityInfo.loadLabel(pm); 459 if (info.activityName == null) info.activityName = info.appName; 460 461 visibleApps.add(info); 462 } 463 } 464 } 465 } 466 467 private class AppLoadingTask extends AsyncTask<Void, Void, Void> { 468 469 @Override 470 protected Void doInBackground(Void... params) { 471 fetchAndMergeApps(); 472 return null; 473 } 474 475 @Override 476 protected void onPostExecute(Void result) { 477 populateApps(); 478 } 479 480 @Override 481 protected void onPreExecute() { 482 } 483 } 484 485 private void fetchAndMergeApps() { 486 mAppList.setOrderingAsAdded(false); 487 mVisibleApps = new ArrayList<SelectableAppInfo>(); 488 final Context context = getActivity(); 489 if (context == null) return; 490 final PackageManager pm = mPackageManager; 491 IPackageManager ipm = AppGlobals.getPackageManager(); 492 493 final HashSet<String> excludePackages = new HashSet<String>(); 494 addSystemImes(excludePackages); 495 496 // Add launchers 497 Intent launcherIntent = new Intent(Intent.ACTION_MAIN); 498 launcherIntent.addCategory(Intent.CATEGORY_LAUNCHER); 499 addSystemApps(mVisibleApps, launcherIntent, excludePackages); 500 501 // Add widgets 502 Intent widgetIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); 503 addSystemApps(mVisibleApps, widgetIntent, excludePackages); 504 505 List<ApplicationInfo> installedApps = pm.getInstalledApplications(0); 506 for (ApplicationInfo app : installedApps) { 507 if ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0 508 && (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) == 0) { 509 // Downloaded app 510 SelectableAppInfo info = new SelectableAppInfo(); 511 info.packageName = app.packageName; 512 info.appName = app.loadLabel(pm); 513 info.activityName = info.appName; 514 info.icon = app.loadIcon(pm); 515 mVisibleApps.add(info); 516 } 517 } 518 519 mUserApps = null; 520 try { 521 mUserApps = ipm.getInstalledApplications( 522 0, mUser.getIdentifier()).getList(); 523 } catch (RemoteException re) { 524 } 525 526 if (mUserApps != null) { 527 for (ApplicationInfo app : mUserApps) { 528 if ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0 529 && (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) == 0) { 530 // Downloaded app 531 SelectableAppInfo info = new SelectableAppInfo(); 532 info.packageName = app.packageName; 533 info.appName = app.loadLabel(pm); 534 info.activityName = info.appName; 535 info.icon = app.loadIcon(pm); 536 mVisibleApps.add(info); 537 } 538 } 539 } 540 Collections.sort(mVisibleApps, new AppLabelComparator()); 541 542 // Remove dupes 543 Set<String> dedupPackageSet = new HashSet<String>(); 544 for (int i = mVisibleApps.size() - 1; i >= 0; i--) { 545 SelectableAppInfo info = mVisibleApps.get(i); 546 if (DEBUG) Log.i(TAG, info.toString()); 547 String both = info.packageName + "+" + info.activityName; 548 if (!TextUtils.isEmpty(info.packageName) 549 && !TextUtils.isEmpty(info.activityName) 550 && dedupPackageSet.contains(both)) { 551 mVisibleApps.remove(i); 552 } else { 553 dedupPackageSet.add(both); 554 } 555 } 556 557 // Establish master/slave relationship for entries that share a package name 558 HashMap<String,SelectableAppInfo> packageMap = new HashMap<String,SelectableAppInfo>(); 559 for (SelectableAppInfo info : mVisibleApps) { 560 if (packageMap.containsKey(info.packageName)) { 561 info.masterEntry = packageMap.get(info.packageName); 562 } else { 563 packageMap.put(info.packageName, info); 564 } 565 } 566 } 567 568 private void populateApps() { 569 final Context context = getActivity(); 570 if (context == null) return; 571 final PackageManager pm = mPackageManager; 572 IPackageManager ipm = AppGlobals.getPackageManager(); 573 mAppList.removeAll(); 574 Intent restrictionsIntent = new Intent(Intent.ACTION_GET_RESTRICTION_ENTRIES); 575 final List<ResolveInfo> receivers = pm.queryBroadcastReceivers(restrictionsIntent, 0); 576 int i = 0; 577 if (mVisibleApps.size() > 0) { 578 for (SelectableAppInfo app : mVisibleApps) { 579 String packageName = app.packageName; 580 if (packageName == null) continue; 581 final boolean isSettingsApp = packageName.equals(context.getPackageName()); 582 AppRestrictionsPreference p = new AppRestrictionsPreference(context, this); 583 final boolean hasSettings = resolveInfoListHasPackage(receivers, packageName); 584 p.setIcon(app.icon != null ? app.icon.mutate() : null); 585 p.setChecked(false); 586 p.setTitle(app.activityName); 587 if (app.masterEntry != null) { 588 p.setSummary(context.getString(R.string.user_restrictions_controlled_by, 589 app.masterEntry.activityName)); 590 } 591 p.setKey(PKG_PREFIX + packageName); 592 p.setSettingsEnabled(hasSettings || isSettingsApp); 593 p.setPersistent(false); 594 p.setOnPreferenceChangeListener(this); 595 p.setOnPreferenceClickListener(this); 596 PackageInfo pi = null; 597 try { 598 pi = pm.getPackageInfo(packageName, 0); 599 } catch (NameNotFoundException re) { 600 try { 601 pi = ipm.getPackageInfo(packageName, 0, mUser.getIdentifier()); 602 } catch (RemoteException e) { 603 } 604 } 605 if (pi != null && pi.requiredForAllUsers) { 606 p.setChecked(true); 607 p.setImmutable(true); 608 // If the app is required and has no restrictions, skip showing it 609 if (!hasSettings && !isSettingsApp) continue; 610 } else if (!mNewUser && appInfoListHasPackage(mUserApps, packageName)) { 611 p.setChecked(true); 612 } 613 if (pi.requiredAccountType != null && pi.restrictedAccountType == null) { 614 p.setChecked(false); 615 p.setImmutable(true); 616 p.setSummary(R.string.app_not_supported_in_limited); 617 } 618 if (pi.restrictedAccountType != null) { 619 p.setSummary(R.string.app_sees_restricted_accounts); 620 } 621 if (app.masterEntry != null) { 622 p.setImmutable(true); 623 p.setChecked(mSelectedPackages.get(packageName)); 624 } 625 mAppList.addPreference(p); 626 if (isSettingsApp) { 627 p.setOrder(MAX_APP_RESTRICTIONS * 1); 628 } else { 629 p.setOrder(MAX_APP_RESTRICTIONS * (i + 2)); 630 } 631 p.setSelectableAppInfo(app); 632 mSelectedPackages.put(packageName, p.isChecked()); 633 mAppListChanged = true; 634 i++; 635 } 636 } 637 // If this is the first time for a new profile, install/uninstall default apps for profile 638 // to avoid taking the hit in onPause(), which can cause race conditions on user switch. 639 if (mNewUser && mFirstTime) { 640 mFirstTime = false; 641 updateUserAppList(); 642 } 643 } 644 645 private class AppLabelComparator implements Comparator<SelectableAppInfo> { 646 647 @Override 648 public int compare(SelectableAppInfo lhs, SelectableAppInfo rhs) { 649 String lhsLabel = lhs.activityName.toString(); 650 String rhsLabel = rhs.activityName.toString(); 651 return lhsLabel.toLowerCase().compareTo(rhsLabel.toLowerCase()); 652 } 653 } 654 655 private boolean resolveInfoListHasPackage(List<ResolveInfo> receivers, String packageName) { 656 for (ResolveInfo info : receivers) { 657 if (info.activityInfo.packageName.equals(packageName)) { 658 return true; 659 } 660 } 661 return false; 662 } 663 664 private boolean appInfoListHasPackage(List<ApplicationInfo> apps, String packageName) { 665 for (ApplicationInfo info : apps) { 666 if (info.packageName.equals(packageName)) { 667 return true; 668 } 669 } 670 return false; 671 } 672 673 private void updateAllEntries(String prefKey, boolean checked) { 674 for (int i = 0; i < mAppList.getPreferenceCount(); i++) { 675 Preference pref = mAppList.getPreference(i); 676 if (pref instanceof AppRestrictionsPreference) { 677 if (prefKey.equals(pref.getKey())) { 678 ((AppRestrictionsPreference) pref).setChecked(checked); 679 } 680 } 681 } 682 } 683 684 @Override 685 public void onClick(View v) { 686 if (v == mHeaderView) { 687 showDialog(DIALOG_ID_EDIT_USER_INFO); 688 } else if (v.getTag() instanceof AppRestrictionsPreference) { 689 AppRestrictionsPreference pref = (AppRestrictionsPreference) v.getTag(); 690 if (v.getId() == R.id.app_restrictions_settings) { 691 toggleAppPanel(pref); 692 } else if (!pref.isImmutable()) { 693 pref.setChecked(!pref.isChecked()); 694 final String packageName = pref.getKey().substring(PKG_PREFIX.length()); 695 mSelectedPackages.put(packageName, pref.isChecked()); 696 if (pref.isChecked() && pref.hasSettings 697 && pref.restrictions == null) { 698 // The restrictions have not been initialized, get and save them 699 requestRestrictionsForApp(packageName, pref); 700 } 701 mAppListChanged = true; 702 updateAllEntries(pref.getKey(), pref.isChecked()); 703 } 704 } 705 } 706 707 @Override 708 public boolean onPreferenceChange(Preference preference, Object newValue) { 709 String key = preference.getKey(); 710 if (key != null && key.contains(DELIMITER)) { 711 StringTokenizer st = new StringTokenizer(key, DELIMITER); 712 final String packageName = st.nextToken(); 713 final String restrictionKey = st.nextToken(); 714 AppRestrictionsPreference appPref = (AppRestrictionsPreference) 715 mAppList.findPreference(PKG_PREFIX+packageName); 716 ArrayList<RestrictionEntry> restrictions = appPref.getRestrictions(); 717 if (restrictions != null) { 718 for (RestrictionEntry entry : restrictions) { 719 if (entry.getKey().equals(restrictionKey)) { 720 switch (entry.getType()) { 721 case RestrictionEntry.TYPE_BOOLEAN: 722 entry.setSelectedState((Boolean) newValue); 723 break; 724 case RestrictionEntry.TYPE_CHOICE: 725 case RestrictionEntry.TYPE_CHOICE_LEVEL: 726 ListPreference listPref = (ListPreference) preference; 727 entry.setSelectedString((String) newValue); 728 String readable = findInArray(entry.getChoiceEntries(), 729 entry.getChoiceValues(), (String) newValue); 730 listPref.setSummary(readable); 731 break; 732 case RestrictionEntry.TYPE_MULTI_SELECT: 733 MultiSelectListPreference msListPref = 734 (MultiSelectListPreference) preference; 735 Set<String> set = (Set<String>) newValue; 736 String [] selectedValues = new String[set.size()]; 737 set.toArray(selectedValues); 738 entry.setAllSelectedStrings(selectedValues); 739 break; 740 default: 741 continue; 742 } 743 if (packageName.equals(getActivity().getPackageName())) { 744 RestrictionUtils.setRestrictions(getActivity(), restrictions, mUser); 745 } else { 746 mUserManager.setApplicationRestrictions(packageName, 747 RestrictionUtils.restrictionsToBundle(restrictions), 748 mUser); 749 } 750 break; 751 } 752 } 753 } 754 } 755 return true; 756 } 757 758 private void toggleAppPanel(AppRestrictionsPreference preference) { 759 if (preference.getKey().startsWith(PKG_PREFIX)) { 760 if (preference.panelOpen) { 761 for (Preference p : preference.childPreferences) { 762 mAppList.removePreference(p); 763 } 764 preference.childPreferences.clear(); 765 } else { 766 String packageName = preference.getKey().substring(PKG_PREFIX.length()); 767 if (packageName.equals(getActivity().getPackageName())) { 768 // Settings, fake it by using user restrictions 769 ArrayList<RestrictionEntry> restrictions = RestrictionUtils.getRestrictions( 770 getActivity(), mUser); 771 onRestrictionsReceived(preference, packageName, restrictions); 772 } else { 773 requestRestrictionsForApp(packageName, preference); 774 } 775 } 776 preference.panelOpen = !preference.panelOpen; 777 } 778 } 779 780 private void requestRestrictionsForApp(String packageName, 781 AppRestrictionsPreference preference) { 782 Bundle oldEntries = 783 mUserManager.getApplicationRestrictions(packageName, mUser); 784 Intent intent = new Intent(Intent.ACTION_GET_RESTRICTION_ENTRIES); 785 intent.setPackage(packageName); 786 intent.putExtra(Intent.EXTRA_RESTRICTIONS_BUNDLE, oldEntries); 787 intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); 788 getActivity().sendOrderedBroadcast(intent, null, 789 new RestrictionsResultReceiver(packageName, preference), 790 null, Activity.RESULT_OK, null, null); 791 } 792 793 class RestrictionsResultReceiver extends BroadcastReceiver { 794 795 private static final String CUSTOM_RESTRICTIONS_INTENT = Intent.EXTRA_RESTRICTIONS_INTENT; 796 String packageName; 797 AppRestrictionsPreference preference; 798 799 RestrictionsResultReceiver(String packageName, AppRestrictionsPreference preference) { 800 super(); 801 this.packageName = packageName; 802 this.preference = preference; 803 } 804 805 @Override 806 public void onReceive(Context context, Intent intent) { 807 Bundle results = getResultExtras(true); 808 final ArrayList<RestrictionEntry> restrictions = results.getParcelableArrayList( 809 Intent.EXTRA_RESTRICTIONS_LIST); 810 Intent restrictionsIntent = (Intent) results.getParcelable(CUSTOM_RESTRICTIONS_INTENT); 811 if (restrictions != null && restrictionsIntent == null) { 812 onRestrictionsReceived(preference, packageName, restrictions); 813 mUserManager.setApplicationRestrictions(packageName, 814 RestrictionUtils.restrictionsToBundle(restrictions), mUser); 815 } else if (restrictionsIntent != null) { 816 final Intent customIntent = restrictionsIntent; 817 if (restrictions != null) { 818 customIntent.putExtra(Intent.EXTRA_RESTRICTIONS_BUNDLE, 819 RestrictionUtils.restrictionsToBundle(restrictions)); 820 } 821 Preference p = new Preference(context); 822 p.setTitle(R.string.app_restrictions_custom_label); 823 p.setOnPreferenceClickListener(new OnPreferenceClickListener() { 824 @Override 825 public boolean onPreferenceClick(Preference preference) { 826 int requestCode = generateCustomActivityRequestCode( 827 RestrictionsResultReceiver.this.preference); 828 AppRestrictionsFragment.this.startActivityForResult( 829 customIntent, requestCode); 830 return false; 831 } 832 }); 833 p.setPersistent(false); 834 p.setOrder(preference.getOrder() + 1); 835 preference.childPreferences.add(p); 836 mAppList.addPreference(p); 837 preference.setRestrictions(restrictions); 838 } 839 } 840 } 841 842 private void onRestrictionsReceived(AppRestrictionsPreference preference, String packageName, 843 ArrayList<RestrictionEntry> restrictions) { 844 // Non-custom-activity case - expand the restrictions in-place 845 final Context context = preference.getContext(); 846 int count = 1; 847 for (RestrictionEntry entry : restrictions) { 848 Preference p = null; 849 switch (entry.getType()) { 850 case RestrictionEntry.TYPE_BOOLEAN: 851 p = new CheckBoxPreference(context); 852 p.setTitle(entry.getTitle()); 853 p.setSummary(entry.getDescription()); 854 ((CheckBoxPreference)p).setChecked(entry.getSelectedState()); 855 break; 856 case RestrictionEntry.TYPE_CHOICE: 857 case RestrictionEntry.TYPE_CHOICE_LEVEL: 858 p = new ListPreference(context); 859 p.setTitle(entry.getTitle()); 860 String value = entry.getSelectedString(); 861 if (value == null) { 862 value = entry.getDescription(); 863 } 864 p.setSummary(findInArray(entry.getChoiceEntries(), entry.getChoiceValues(), 865 value)); 866 ((ListPreference)p).setEntryValues(entry.getChoiceValues()); 867 ((ListPreference)p).setEntries(entry.getChoiceEntries()); 868 ((ListPreference)p).setValue(value); 869 ((ListPreference)p).setDialogTitle(entry.getTitle()); 870 break; 871 case RestrictionEntry.TYPE_MULTI_SELECT: 872 p = new MultiSelectListPreference(context); 873 p.setTitle(entry.getTitle()); 874 ((MultiSelectListPreference)p).setEntryValues(entry.getChoiceValues()); 875 ((MultiSelectListPreference)p).setEntries(entry.getChoiceEntries()); 876 HashSet<String> set = new HashSet<String>(); 877 for (String s : entry.getAllSelectedStrings()) { 878 set.add(s); 879 } 880 ((MultiSelectListPreference)p).setValues(set); 881 ((MultiSelectListPreference)p).setDialogTitle(entry.getTitle()); 882 break; 883 case RestrictionEntry.TYPE_NULL: 884 default: 885 } 886 if (p != null) { 887 p.setPersistent(false); 888 p.setOrder(preference.getOrder() + count); 889 // Store the restrictions key string as a key for the preference 890 p.setKey(preference.getKey().substring(PKG_PREFIX.length()) + DELIMITER 891 + entry.getKey()); 892 mAppList.addPreference(p); 893 p.setOnPreferenceChangeListener(AppRestrictionsFragment.this); 894 preference.childPreferences.add(p); 895 count++; 896 } 897 } 898 preference.setRestrictions(restrictions); 899 } 900 901 /** 902 * Generates a request code that is stored in a map to retrieve the associated 903 * AppRestrictionsPreference. 904 * @param preference 905 * @return 906 */ 907 private int generateCustomActivityRequestCode(AppRestrictionsPreference preference) { 908 mCustomRequestCode++; 909 mCustomRequestMap.put(mCustomRequestCode, preference); 910 return mCustomRequestCode; 911 } 912 913 @Override 914 public void onActivityResult(int requestCode, int resultCode, Intent data) { 915 super.onActivityResult(requestCode, resultCode, data); 916 917 if (mEditUserInfoDialog != null && mEditUserInfoDialog.isShowing() 918 && mEditUserPhotoController.onActivityResult(requestCode, resultCode, data)) { 919 return; 920 } 921 922 AppRestrictionsPreference pref = mCustomRequestMap.get(requestCode); 923 if (pref == null) { 924 Log.w(TAG, "Unknown requestCode " + requestCode); 925 return; 926 } 927 928 if (resultCode == Activity.RESULT_OK) { 929 String packageName = pref.getKey().substring(PKG_PREFIX.length()); 930 ArrayList<RestrictionEntry> list = 931 data.getParcelableArrayListExtra(Intent.EXTRA_RESTRICTIONS_LIST); 932 Bundle bundle = data.getBundleExtra(Intent.EXTRA_RESTRICTIONS_BUNDLE); 933 if (list != null) { 934 // If there's a valid result, persist it to the user manager. 935 pref.setRestrictions(list); 936 mUserManager.setApplicationRestrictions(packageName, 937 RestrictionUtils.restrictionsToBundle(list), mUser); 938 } else if (bundle != null) { 939 // If there's a valid result, persist it to the user manager. 940 mUserManager.setApplicationRestrictions(packageName, bundle, mUser); 941 } 942 toggleAppPanel(pref); 943 } 944 // Remove request from the map 945 mCustomRequestMap.remove(requestCode); 946 } 947 948 private String findInArray(String[] choiceEntries, String[] choiceValues, 949 String selectedString) { 950 for (int i = 0; i < choiceValues.length; i++) { 951 if (choiceValues[i].equals(selectedString)) { 952 return choiceEntries[i]; 953 } 954 } 955 return selectedString; 956 } 957 958 @Override 959 public boolean onPreferenceClick(Preference preference) { 960 if (preference.getKey().startsWith(PKG_PREFIX)) { 961 AppRestrictionsPreference arp = (AppRestrictionsPreference) preference; 962 if (!arp.isImmutable()) { 963 arp.setChecked(!arp.isChecked()); 964 mSelectedPackages.put(arp.getKey().substring(PKG_PREFIX.length()), arp.isChecked()); 965 updateAllEntries(arp.getKey(), arp.isChecked()); 966 mAppListChanged = true; 967 } 968 return true; 969 } 970 return false; 971 } 972 973 @Override 974 public Dialog onCreateDialog(int dialogId) { 975 if (dialogId == DIALOG_ID_EDIT_USER_INFO) { 976 if (mEditUserInfoDialog != null) { 977 return mEditUserInfoDialog; 978 } 979 980 LayoutInflater inflater = getActivity().getLayoutInflater(); 981 View content = inflater.inflate(R.layout.edit_user_info_dialog_content, null); 982 983 UserInfo info = mUserManager.getUserInfo(mUser.getIdentifier()); 984 985 final EditText userNameView = (EditText) content.findViewById(R.id.user_name); 986 userNameView.setText(info.name); 987 988 final ImageView userPhotoView = (ImageView) content.findViewById(R.id.user_photo); 989 Drawable drawable = null; 990 if (mSavedPhoto != null) { 991 drawable = CircleFramedDrawable.getInstance(getActivity(), mSavedPhoto); 992 } else { 993 drawable = mUserIconView.getDrawable(); 994 if (drawable == null) { 995 drawable = getCircularUserIcon(); 996 } 997 } 998 userPhotoView.setImageDrawable(drawable); 999 1000 mEditUserPhotoController = new EditUserPhotoController(this, userPhotoView, 1001 mSavedPhoto, drawable); 1002 1003 mEditUserInfoDialog = new AlertDialog.Builder(getActivity()) 1004 .setTitle(R.string.user_info_settings_title) 1005 .setIconAttribute(R.drawable.ic_settings_multiuser) 1006 .setView(content) 1007 .setCancelable(true) 1008 .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { 1009 @Override 1010 public void onClick(DialogInterface dialog, int which) { 1011 if (which == DialogInterface.BUTTON_POSITIVE) { 1012 // Update the name if changed. 1013 CharSequence userName = userNameView.getText(); 1014 if (!TextUtils.isEmpty(userName)) { 1015 CharSequence oldUserName = mUserNameView.getText(); 1016 if (oldUserName == null 1017 || !userName.toString().equals(oldUserName.toString())) { 1018 ((TextView) mHeaderView.findViewById(android.R.id.title)) 1019 .setText(userName.toString()); 1020 mUserManager.setUserName(mUser.getIdentifier(), 1021 userName.toString()); 1022 } 1023 } 1024 // Update the photo if changed. 1025 Drawable drawable = mEditUserPhotoController.getNewUserPhotoDrawable(); 1026 Bitmap bitmap = mEditUserPhotoController.getNewUserPhotoBitmap(); 1027 if (drawable != null && bitmap != null 1028 && !drawable.equals(mUserIconView.getDrawable())) { 1029 mUserIconView.setImageDrawable(drawable); 1030 new AsyncTask<Void, Void, Void>() { 1031 @Override 1032 protected Void doInBackground(Void... params) { 1033 mUserManager.setUserIcon(mUser.getIdentifier(), 1034 mEditUserPhotoController.getNewUserPhotoBitmap()); 1035 return null; 1036 } 1037 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null); 1038 } 1039 removeDialog(DIALOG_ID_EDIT_USER_INFO); 1040 } 1041 clearEditUserInfoDialog(); 1042 } 1043 }) 1044 .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { 1045 @Override 1046 public void onClick(DialogInterface dialog, int which) { 1047 clearEditUserInfoDialog(); 1048 } 1049 }) 1050 .create(); 1051 1052 // Make sure the IME is up. 1053 mEditUserInfoDialog.getWindow().setSoftInputMode( 1054 WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); 1055 1056 return mEditUserInfoDialog; 1057 } 1058 1059 return null; 1060 } 1061 1062 private void clearEditUserInfoDialog() { 1063 mEditUserInfoDialog = null; 1064 mSavedPhoto = null; 1065 } 1066 1067 private static class EditUserPhotoController { 1068 private static final int POPUP_LIST_ITEM_ID_CHOOSE_PHOTO = 1; 1069 private static final int POPUP_LIST_ITEM_ID_TAKE_PHOTO = 2; 1070 1071 // It seems that this class generates custom request codes and they may 1072 // collide with ours, these values are very unlikely to have a conflict. 1073 private static final int REQUEST_CODE_CHOOSE_PHOTO = Integer.MAX_VALUE; 1074 private static final int REQUEST_CODE_TAKE_PHOTO = Integer.MAX_VALUE - 1; 1075 private static final int REQUEST_CODE_CROP_PHOTO = Integer.MAX_VALUE - 2; 1076 1077 private static final String CROP_PICTURE_FILE_NAME = "CropEditUserPhoto.jpg"; 1078 private static final String TAKE_PICTURE_FILE_NAME = "TakeEditUserPhoto2.jpg"; 1079 1080 private final int mPhotoSize; 1081 1082 private final Context mContext; 1083 private final Fragment mFragment; 1084 private final ImageView mImageView; 1085 1086 private final Uri mCropPictureUri; 1087 private final Uri mTakePictureUri; 1088 1089 private Bitmap mNewUserPhotoBitmap; 1090 private Drawable mNewUserPhotoDrawable; 1091 1092 public EditUserPhotoController(Fragment fragment, ImageView view, 1093 Bitmap bitmap, Drawable drawable) { 1094 mContext = view.getContext(); 1095 mFragment = fragment; 1096 mImageView = view; 1097 mCropPictureUri = createTempImageUri(mContext, CROP_PICTURE_FILE_NAME); 1098 mTakePictureUri = createTempImageUri(mContext, TAKE_PICTURE_FILE_NAME); 1099 mPhotoSize = getPhotoSize(mContext); 1100 mImageView.setOnClickListener(new OnClickListener() { 1101 @Override 1102 public void onClick(View v) { 1103 showUpdatePhotoPopup(); 1104 } 1105 }); 1106 mNewUserPhotoBitmap = bitmap; 1107 mNewUserPhotoDrawable = drawable; 1108 } 1109 1110 public boolean onActivityResult(int requestCode, int resultCode, final Intent data) { 1111 if (resultCode != Activity.RESULT_OK) { 1112 return false; 1113 } 1114 switch (requestCode) { 1115 case REQUEST_CODE_CHOOSE_PHOTO: 1116 case REQUEST_CODE_CROP_PHOTO: { 1117 new AsyncTask<Void, Void, Bitmap>() { 1118 @Override 1119 protected Bitmap doInBackground(Void... params) { 1120 return BitmapFactory.decodeFile(mCropPictureUri.getPath()); 1121 } 1122 @Override 1123 protected void onPostExecute(Bitmap bitmap) { 1124 mNewUserPhotoBitmap = bitmap; 1125 mNewUserPhotoDrawable = CircleFramedDrawable 1126 .getInstance(mImageView.getContext(), mNewUserPhotoBitmap); 1127 mImageView.setImageDrawable(mNewUserPhotoDrawable); 1128 // Delete the files - not needed anymore. 1129 new File(mCropPictureUri.getPath()).delete(); 1130 new File(mTakePictureUri.getPath()).delete(); 1131 } 1132 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null); 1133 } return true; 1134 case REQUEST_CODE_TAKE_PHOTO: { 1135 cropPhoto(); 1136 } break; 1137 } 1138 return false; 1139 } 1140 1141 public Bitmap getNewUserPhotoBitmap() { 1142 return mNewUserPhotoBitmap; 1143 } 1144 1145 public Drawable getNewUserPhotoDrawable() { 1146 return mNewUserPhotoDrawable; 1147 } 1148 1149 private void showUpdatePhotoPopup() { 1150 final boolean canTakePhoto = canTakePhoto(); 1151 final boolean canChoosePhoto = canChoosePhoto(); 1152 1153 if (!canTakePhoto && !canChoosePhoto) { 1154 return; 1155 } 1156 1157 Context context = mImageView.getContext(); 1158 final List<AdapterItem> items = new ArrayList<AdapterItem>(); 1159 1160 if (canTakePhoto()) { 1161 String title = mImageView.getContext().getString( R.string.user_image_take_photo); 1162 AdapterItem item = new AdapterItem(title, POPUP_LIST_ITEM_ID_TAKE_PHOTO); 1163 items.add(item); 1164 } 1165 1166 if (canChoosePhoto) { 1167 String title = context.getString(R.string.user_image_choose_photo); 1168 AdapterItem item = new AdapterItem(title, POPUP_LIST_ITEM_ID_CHOOSE_PHOTO); 1169 items.add(item); 1170 } 1171 1172 final ListPopupWindow listPopupWindow = new ListPopupWindow(context); 1173 1174 listPopupWindow.setAnchorView(mImageView); 1175 listPopupWindow.setModal(true); 1176 listPopupWindow.setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED); 1177 1178 ListAdapter adapter = new ArrayAdapter<AdapterItem>(context, 1179 R.layout.edit_user_photo_popup_item, items); 1180 listPopupWindow.setAdapter(adapter); 1181 1182 final int width = Math.max(mImageView.getWidth(), context.getResources() 1183 .getDimensionPixelSize(R.dimen.update_user_photo_popup_min_width)); 1184 listPopupWindow.setWidth(width); 1185 1186 listPopupWindow.setOnItemClickListener(new AdapterView.OnItemClickListener() { 1187 @Override 1188 public void onItemClick(AdapterView<?> parent, View view, int position, long id) { 1189 AdapterItem item = items.get(position); 1190 switch (item.id) { 1191 case POPUP_LIST_ITEM_ID_CHOOSE_PHOTO: { 1192 choosePhoto(); 1193 listPopupWindow.dismiss(); 1194 } break; 1195 case POPUP_LIST_ITEM_ID_TAKE_PHOTO: { 1196 takePhoto(); 1197 listPopupWindow.dismiss(); 1198 } break; 1199 } 1200 } 1201 }); 1202 1203 listPopupWindow.show(); 1204 } 1205 1206 private boolean canTakePhoto() { 1207 return mImageView.getContext().getPackageManager().queryIntentActivities( 1208 new Intent(MediaStore.ACTION_IMAGE_CAPTURE), 1209 PackageManager.MATCH_DEFAULT_ONLY).size() > 0; 1210 } 1211 1212 private boolean canChoosePhoto() { 1213 Intent intent = new Intent(Intent.ACTION_GET_CONTENT); 1214 intent.setType("image/*"); 1215 return mImageView.getContext().getPackageManager().queryIntentActivities( 1216 intent, 0).size() > 0; 1217 } 1218 1219 private void takePhoto() { 1220 Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); 1221 intent.putExtra(MediaStore.EXTRA_OUTPUT, mTakePictureUri); 1222 mFragment.startActivityForResult(intent, REQUEST_CODE_TAKE_PHOTO); 1223 } 1224 1225 private void choosePhoto() { 1226 Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null); 1227 intent.setType("image/*"); 1228 intent.putExtra(MediaStore.EXTRA_OUTPUT, mCropPictureUri); 1229 appendCropExtras(intent); 1230 mFragment.startActivityForResult(intent, REQUEST_CODE_CHOOSE_PHOTO); 1231 } 1232 1233 private void cropPhoto() { 1234 Intent intent = new Intent("com.android.camera.action.CROP"); 1235 intent.setDataAndType(mTakePictureUri, "image/*"); 1236 intent.putExtra(MediaStore.EXTRA_OUTPUT, mCropPictureUri); 1237 appendCropExtras(intent); 1238 mFragment.startActivityForResult(intent, REQUEST_CODE_CROP_PHOTO); 1239 } 1240 1241 private void appendCropExtras(Intent intent) { 1242 intent.putExtra("crop", "true"); 1243 intent.putExtra("scale", true); 1244 intent.putExtra("scaleUpIfNeeded", true); 1245 intent.putExtra("aspectX", 1); 1246 intent.putExtra("aspectY", 1); 1247 intent.putExtra("outputX", mPhotoSize); 1248 intent.putExtra("outputY", mPhotoSize); 1249 } 1250 1251 private static int getPhotoSize(Context context) { 1252 Cursor cursor = context.getContentResolver().query( 1253 DisplayPhoto.CONTENT_MAX_DIMENSIONS_URI, 1254 new String[]{DisplayPhoto.DISPLAY_MAX_DIM}, null, null, null); 1255 try { 1256 cursor.moveToFirst(); 1257 return cursor.getInt(0); 1258 } finally { 1259 cursor.close(); 1260 } 1261 } 1262 1263 private static Uri createTempImageUri(Context context, String fileName) { 1264 File folder = context.getExternalCacheDir(); 1265 folder.mkdirs(); 1266 File fullPath = new File(folder, fileName); 1267 fullPath.delete(); 1268 return Uri.fromFile(fullPath.getAbsoluteFile()); 1269 } 1270 1271 private static final class AdapterItem { 1272 final String title; 1273 final int id; 1274 1275 public AdapterItem(String title, int id) { 1276 this.title = title; 1277 this.id = id; 1278 } 1279 1280 @Override 1281 public String toString() { 1282 return title; 1283 } 1284 } 1285 } 1286} 1287