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 17 package com.android.settings.users; 18 19 import android.app.Activity; 20 import android.app.AlertDialog; 21 import android.app.AppGlobals; 22 import android.app.Dialog; 23 import android.app.Fragment; 24 import android.appwidget.AppWidgetManager; 25 import android.content.BroadcastReceiver; 26 import android.content.Context; 27 import android.content.DialogInterface; 28 import android.content.Intent; 29 import android.content.IntentFilter; 30 import android.content.RestrictionEntry; 31 import android.content.pm.ApplicationInfo; 32 import android.content.pm.IPackageManager; 33 import android.content.pm.PackageInfo; 34 import android.content.pm.PackageManager; 35 import android.content.pm.PackageManager.NameNotFoundException; 36 import android.content.pm.ResolveInfo; 37 import android.content.pm.UserInfo; 38 import android.content.res.Resources; 39 import android.database.Cursor; 40 import android.graphics.Bitmap; 41 import android.graphics.BitmapFactory; 42 import android.graphics.ColorFilter; 43 import android.graphics.ColorMatrix; 44 import android.graphics.ColorMatrixColorFilter; 45 import android.graphics.drawable.Drawable; 46 import android.net.Uri; 47 import android.os.AsyncTask; 48 import android.os.Bundle; 49 import android.os.RemoteException; 50 import android.os.ServiceManager; 51 import android.os.UserHandle; 52 import android.os.UserManager; 53 import android.preference.CheckBoxPreference; 54 import android.preference.ListPreference; 55 import android.preference.MultiSelectListPreference; 56 import android.preference.Preference; 57 import android.preference.Preference.OnPreferenceChangeListener; 58 import android.preference.Preference.OnPreferenceClickListener; 59 import android.preference.PreferenceGroup; 60 import android.preference.SwitchPreference; 61 import android.provider.ContactsContract.DisplayPhoto; 62 import android.provider.MediaStore; 63 import android.text.TextUtils; 64 import android.util.Log; 65 import android.view.LayoutInflater; 66 import android.view.View; 67 import android.view.View.OnClickListener; 68 import android.view.inputmethod.InputMethod; 69 import android.view.inputmethod.InputMethodInfo; 70 import android.view.inputmethod.InputMethodManager; 71 import android.view.ViewGroup; 72 import android.view.WindowManager; 73 import android.widget.AdapterView; 74 import android.widget.ArrayAdapter; 75 import android.widget.CompoundButton; 76 import android.widget.CompoundButton.OnCheckedChangeListener; 77 import android.widget.EditText; 78 import android.widget.ImageView; 79 import android.widget.ListAdapter; 80 import android.widget.ListPopupWindow; 81 import android.widget.Switch; 82 import android.widget.TextView; 83 84 import com.android.settings.R; 85 import com.android.settings.SettingsPreferenceFragment; 86 87 import java.io.File; 88 import java.util.ArrayList; 89 import java.util.Collections; 90 import java.util.Comparator; 91 import java.util.HashMap; 92 import java.util.HashSet; 93 import java.util.List; 94 import java.util.Map; 95 import java.util.Set; 96 import java.util.StringTokenizer; 97 98 public 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 } else { 517 try { 518 PackageInfo pi = pm.getPackageInfo(app.packageName, 0); 519 // If it's a system app that requires an account and doesn't see restricted 520 // accounts, mark for removal. It might get shown in the UI if it has an icon 521 // but will still be marked as false and immutable. 522 if (pi.requiredAccountType != null && pi.restrictedAccountType == null) { 523 mSelectedPackages.put(app.packageName, false); 524 } 525 } catch (NameNotFoundException re) { 526 } 527 } 528 } 529 530 mUserApps = null; 531 try { 532 mUserApps = ipm.getInstalledApplications( 533 0, mUser.getIdentifier()).getList(); 534 } catch (RemoteException re) { 535 } 536 537 if (mUserApps != null) { 538 for (ApplicationInfo app : mUserApps) { 539 if ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0 540 && (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) == 0) { 541 // Downloaded app 542 SelectableAppInfo info = new SelectableAppInfo(); 543 info.packageName = app.packageName; 544 info.appName = app.loadLabel(pm); 545 info.activityName = info.appName; 546 info.icon = app.loadIcon(pm); 547 mVisibleApps.add(info); 548 } 549 } 550 } 551 Collections.sort(mVisibleApps, new AppLabelComparator()); 552 553 // Remove dupes 554 Set<String> dedupPackageSet = new HashSet<String>(); 555 for (int i = mVisibleApps.size() - 1; i >= 0; i--) { 556 SelectableAppInfo info = mVisibleApps.get(i); 557 if (DEBUG) Log.i(TAG, info.toString()); 558 String both = info.packageName + "+" + info.activityName; 559 if (!TextUtils.isEmpty(info.packageName) 560 && !TextUtils.isEmpty(info.activityName) 561 && dedupPackageSet.contains(both)) { 562 mVisibleApps.remove(i); 563 } else { 564 dedupPackageSet.add(both); 565 } 566 } 567 568 // Establish master/slave relationship for entries that share a package name 569 HashMap<String,SelectableAppInfo> packageMap = new HashMap<String,SelectableAppInfo>(); 570 for (SelectableAppInfo info : mVisibleApps) { 571 if (packageMap.containsKey(info.packageName)) { 572 info.masterEntry = packageMap.get(info.packageName); 573 } else { 574 packageMap.put(info.packageName, info); 575 } 576 } 577 } 578 579 private void populateApps() { 580 final Context context = getActivity(); 581 if (context == null) return; 582 final PackageManager pm = mPackageManager; 583 IPackageManager ipm = AppGlobals.getPackageManager(); 584 mAppList.removeAll(); 585 Intent restrictionsIntent = new Intent(Intent.ACTION_GET_RESTRICTION_ENTRIES); 586 final List<ResolveInfo> receivers = pm.queryBroadcastReceivers(restrictionsIntent, 0); 587 int i = 0; 588 if (mVisibleApps.size() > 0) { 589 for (SelectableAppInfo app : mVisibleApps) { 590 String packageName = app.packageName; 591 if (packageName == null) continue; 592 final boolean isSettingsApp = packageName.equals(context.getPackageName()); 593 AppRestrictionsPreference p = new AppRestrictionsPreference(context, this); 594 final boolean hasSettings = resolveInfoListHasPackage(receivers, packageName); 595 p.setIcon(app.icon != null ? app.icon.mutate() : null); 596 p.setChecked(false); 597 p.setTitle(app.activityName); 598 if (app.masterEntry != null) { 599 p.setSummary(context.getString(R.string.user_restrictions_controlled_by, 600 app.masterEntry.activityName)); 601 } 602 p.setKey(PKG_PREFIX + packageName); 603 p.setSettingsEnabled(hasSettings || isSettingsApp); 604 p.setPersistent(false); 605 p.setOnPreferenceChangeListener(this); 606 p.setOnPreferenceClickListener(this); 607 PackageInfo pi = null; 608 try { 609 pi = pm.getPackageInfo(packageName, 0); 610 } catch (NameNotFoundException re) { 611 try { 612 pi = ipm.getPackageInfo(packageName, 0, mUser.getIdentifier()); 613 } catch (RemoteException e) { 614 } 615 } 616 if (pi != null && pi.requiredForAllUsers) { 617 p.setChecked(true); 618 p.setImmutable(true); 619 // If the app is required and has no restrictions, skip showing it 620 if (!hasSettings && !isSettingsApp) continue; 621 // Get and populate the defaults, since the user is not going to be 622 // able to toggle this app ON (it's ON by default and immutable). 623 if (hasSettings) { 624 requestRestrictionsForApp(packageName, p); 625 } 626 } else if (!mNewUser && appInfoListHasPackage(mUserApps, packageName)) { 627 p.setChecked(true); 628 } 629 if (pi.requiredAccountType != null && pi.restrictedAccountType == null) { 630 p.setChecked(false); 631 p.setImmutable(true); 632 p.setSummary(R.string.app_not_supported_in_limited); 633 } 634 if (pi.restrictedAccountType != null) { 635 p.setSummary(R.string.app_sees_restricted_accounts); 636 } 637 if (app.masterEntry != null) { 638 p.setImmutable(true); 639 p.setChecked(mSelectedPackages.get(packageName)); 640 } 641 mAppList.addPreference(p); 642 if (isSettingsApp) { 643 p.setOrder(MAX_APP_RESTRICTIONS * 1); 644 } else { 645 p.setOrder(MAX_APP_RESTRICTIONS * (i + 2)); 646 } 647 p.setSelectableAppInfo(app); 648 mSelectedPackages.put(packageName, p.isChecked()); 649 mAppListChanged = true; 650 i++; 651 } 652 } 653 // If this is the first time for a new profile, install/uninstall default apps for profile 654 // to avoid taking the hit in onPause(), which can cause race conditions on user switch. 655 if (mNewUser && mFirstTime) { 656 mFirstTime = false; 657 updateUserAppList(); 658 } 659 } 660 661 private class AppLabelComparator implements Comparator<SelectableAppInfo> { 662 663 @Override 664 public int compare(SelectableAppInfo lhs, SelectableAppInfo rhs) { 665 String lhsLabel = lhs.activityName.toString(); 666 String rhsLabel = rhs.activityName.toString(); 667 return lhsLabel.toLowerCase().compareTo(rhsLabel.toLowerCase()); 668 } 669 } 670 671 private boolean resolveInfoListHasPackage(List<ResolveInfo> receivers, String packageName) { 672 for (ResolveInfo info : receivers) { 673 if (info.activityInfo.packageName.equals(packageName)) { 674 return true; 675 } 676 } 677 return false; 678 } 679 680 private boolean appInfoListHasPackage(List<ApplicationInfo> apps, String packageName) { 681 for (ApplicationInfo info : apps) { 682 if (info.packageName.equals(packageName)) { 683 return true; 684 } 685 } 686 return false; 687 } 688 689 private void updateAllEntries(String prefKey, boolean checked) { 690 for (int i = 0; i < mAppList.getPreferenceCount(); i++) { 691 Preference pref = mAppList.getPreference(i); 692 if (pref instanceof AppRestrictionsPreference) { 693 if (prefKey.equals(pref.getKey())) { 694 ((AppRestrictionsPreference) pref).setChecked(checked); 695 } 696 } 697 } 698 } 699 700 @Override 701 public void onClick(View v) { 702 if (v == mHeaderView) { 703 showDialog(DIALOG_ID_EDIT_USER_INFO); 704 } else if (v.getTag() instanceof AppRestrictionsPreference) { 705 AppRestrictionsPreference pref = (AppRestrictionsPreference) v.getTag(); 706 if (v.getId() == R.id.app_restrictions_settings) { 707 toggleAppPanel(pref); 708 } else if (!pref.isImmutable()) { 709 pref.setChecked(!pref.isChecked()); 710 final String packageName = pref.getKey().substring(PKG_PREFIX.length()); 711 mSelectedPackages.put(packageName, pref.isChecked()); 712 if (pref.isChecked() && pref.hasSettings 713 && pref.restrictions == null) { 714 // The restrictions have not been initialized, get and save them 715 requestRestrictionsForApp(packageName, pref); 716 } 717 mAppListChanged = true; 718 updateAllEntries(pref.getKey(), pref.isChecked()); 719 } 720 } 721 } 722 723 @Override 724 public boolean onPreferenceChange(Preference preference, Object newValue) { 725 String key = preference.getKey(); 726 if (key != null && key.contains(DELIMITER)) { 727 StringTokenizer st = new StringTokenizer(key, DELIMITER); 728 final String packageName = st.nextToken(); 729 final String restrictionKey = st.nextToken(); 730 AppRestrictionsPreference appPref = (AppRestrictionsPreference) 731 mAppList.findPreference(PKG_PREFIX+packageName); 732 ArrayList<RestrictionEntry> restrictions = appPref.getRestrictions(); 733 if (restrictions != null) { 734 for (RestrictionEntry entry : restrictions) { 735 if (entry.getKey().equals(restrictionKey)) { 736 switch (entry.getType()) { 737 case RestrictionEntry.TYPE_BOOLEAN: 738 entry.setSelectedState((Boolean) newValue); 739 break; 740 case RestrictionEntry.TYPE_CHOICE: 741 case RestrictionEntry.TYPE_CHOICE_LEVEL: 742 ListPreference listPref = (ListPreference) preference; 743 entry.setSelectedString((String) newValue); 744 String readable = findInArray(entry.getChoiceEntries(), 745 entry.getChoiceValues(), (String) newValue); 746 listPref.setSummary(readable); 747 break; 748 case RestrictionEntry.TYPE_MULTI_SELECT: 749 MultiSelectListPreference msListPref = 750 (MultiSelectListPreference) preference; 751 Set<String> set = (Set<String>) newValue; 752 String [] selectedValues = new String[set.size()]; 753 set.toArray(selectedValues); 754 entry.setAllSelectedStrings(selectedValues); 755 break; 756 default: 757 continue; 758 } 759 if (packageName.equals(getActivity().getPackageName())) { 760 RestrictionUtils.setRestrictions(getActivity(), restrictions, mUser); 761 } else { 762 mUserManager.setApplicationRestrictions(packageName, 763 RestrictionUtils.restrictionsToBundle(restrictions), 764 mUser); 765 } 766 break; 767 } 768 } 769 } 770 } 771 return true; 772 } 773 774 private void toggleAppPanel(AppRestrictionsPreference preference) { 775 if (preference.getKey().startsWith(PKG_PREFIX)) { 776 if (preference.panelOpen) { 777 for (Preference p : preference.childPreferences) { 778 mAppList.removePreference(p); 779 } 780 preference.childPreferences.clear(); 781 } else { 782 String packageName = preference.getKey().substring(PKG_PREFIX.length()); 783 if (packageName.equals(getActivity().getPackageName())) { 784 // Settings, fake it by using user restrictions 785 ArrayList<RestrictionEntry> restrictions = RestrictionUtils.getRestrictions( 786 getActivity(), mUser); 787 onRestrictionsReceived(preference, packageName, restrictions); 788 } else { 789 requestRestrictionsForApp(packageName, preference); 790 } 791 } 792 preference.panelOpen = !preference.panelOpen; 793 } 794 } 795 796 private void requestRestrictionsForApp(String packageName, 797 AppRestrictionsPreference preference) { 798 Bundle oldEntries = 799 mUserManager.getApplicationRestrictions(packageName, mUser); 800 Intent intent = new Intent(Intent.ACTION_GET_RESTRICTION_ENTRIES); 801 intent.setPackage(packageName); 802 intent.putExtra(Intent.EXTRA_RESTRICTIONS_BUNDLE, oldEntries); 803 intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); 804 getActivity().sendOrderedBroadcast(intent, null, 805 new RestrictionsResultReceiver(packageName, preference), 806 null, Activity.RESULT_OK, null, null); 807 } 808 809 class RestrictionsResultReceiver extends BroadcastReceiver { 810 811 private static final String CUSTOM_RESTRICTIONS_INTENT = Intent.EXTRA_RESTRICTIONS_INTENT; 812 String packageName; 813 AppRestrictionsPreference preference; 814 815 RestrictionsResultReceiver(String packageName, AppRestrictionsPreference preference) { 816 super(); 817 this.packageName = packageName; 818 this.preference = preference; 819 } 820 821 @Override 822 public void onReceive(Context context, Intent intent) { 823 Bundle results = getResultExtras(true); 824 final ArrayList<RestrictionEntry> restrictions = results.getParcelableArrayList( 825 Intent.EXTRA_RESTRICTIONS_LIST); 826 Intent restrictionsIntent = (Intent) results.getParcelable(CUSTOM_RESTRICTIONS_INTENT); 827 if (restrictions != null && restrictionsIntent == null) { 828 onRestrictionsReceived(preference, packageName, restrictions); 829 mUserManager.setApplicationRestrictions(packageName, 830 RestrictionUtils.restrictionsToBundle(restrictions), mUser); 831 } else if (restrictionsIntent != null) { 832 final Intent customIntent = restrictionsIntent; 833 if (restrictions != null) { 834 customIntent.putExtra(Intent.EXTRA_RESTRICTIONS_BUNDLE, 835 RestrictionUtils.restrictionsToBundle(restrictions)); 836 } 837 Preference p = new Preference(context); 838 p.setTitle(R.string.app_restrictions_custom_label); 839 p.setOnPreferenceClickListener(new OnPreferenceClickListener() { 840 @Override 841 public boolean onPreferenceClick(Preference preference) { 842 int requestCode = generateCustomActivityRequestCode( 843 RestrictionsResultReceiver.this.preference); 844 AppRestrictionsFragment.this.startActivityForResult( 845 customIntent, requestCode); 846 return false; 847 } 848 }); 849 p.setPersistent(false); 850 p.setOrder(preference.getOrder() + 1); 851 preference.childPreferences.add(p); 852 mAppList.addPreference(p); 853 preference.setRestrictions(restrictions); 854 } 855 } 856 } 857 858 private void onRestrictionsReceived(AppRestrictionsPreference preference, String packageName, 859 ArrayList<RestrictionEntry> restrictions) { 860 // Non-custom-activity case - expand the restrictions in-place 861 final Context context = preference.getContext(); 862 int count = 1; 863 for (RestrictionEntry entry : restrictions) { 864 Preference p = null; 865 switch (entry.getType()) { 866 case RestrictionEntry.TYPE_BOOLEAN: 867 p = new CheckBoxPreference(context); 868 p.setTitle(entry.getTitle()); 869 p.setSummary(entry.getDescription()); 870 ((CheckBoxPreference)p).setChecked(entry.getSelectedState()); 871 break; 872 case RestrictionEntry.TYPE_CHOICE: 873 case RestrictionEntry.TYPE_CHOICE_LEVEL: 874 p = new ListPreference(context); 875 p.setTitle(entry.getTitle()); 876 String value = entry.getSelectedString(); 877 if (value == null) { 878 value = entry.getDescription(); 879 } 880 p.setSummary(findInArray(entry.getChoiceEntries(), entry.getChoiceValues(), 881 value)); 882 ((ListPreference)p).setEntryValues(entry.getChoiceValues()); 883 ((ListPreference)p).setEntries(entry.getChoiceEntries()); 884 ((ListPreference)p).setValue(value); 885 ((ListPreference)p).setDialogTitle(entry.getTitle()); 886 break; 887 case RestrictionEntry.TYPE_MULTI_SELECT: 888 p = new MultiSelectListPreference(context); 889 p.setTitle(entry.getTitle()); 890 ((MultiSelectListPreference)p).setEntryValues(entry.getChoiceValues()); 891 ((MultiSelectListPreference)p).setEntries(entry.getChoiceEntries()); 892 HashSet<String> set = new HashSet<String>(); 893 for (String s : entry.getAllSelectedStrings()) { 894 set.add(s); 895 } 896 ((MultiSelectListPreference)p).setValues(set); 897 ((MultiSelectListPreference)p).setDialogTitle(entry.getTitle()); 898 break; 899 case RestrictionEntry.TYPE_NULL: 900 default: 901 } 902 if (p != null) { 903 p.setPersistent(false); 904 p.setOrder(preference.getOrder() + count); 905 // Store the restrictions key string as a key for the preference 906 p.setKey(preference.getKey().substring(PKG_PREFIX.length()) + DELIMITER 907 + entry.getKey()); 908 mAppList.addPreference(p); 909 p.setOnPreferenceChangeListener(AppRestrictionsFragment.this); 910 preference.childPreferences.add(p); 911 count++; 912 } 913 } 914 preference.setRestrictions(restrictions); 915 if (count == 1 // No visible restrictions 916 && preference.isImmutable() 917 && preference.isChecked()) { 918 // Special case of required app with no visible restrictions. Remove it 919 mAppList.removePreference(preference); 920 } 921 } 922 923 /** 924 * Generates a request code that is stored in a map to retrieve the associated 925 * AppRestrictionsPreference. 926 * @param preference 927 * @return 928 */ 929 private int generateCustomActivityRequestCode(AppRestrictionsPreference preference) { 930 mCustomRequestCode++; 931 mCustomRequestMap.put(mCustomRequestCode, preference); 932 return mCustomRequestCode; 933 } 934 935 @Override 936 public void onActivityResult(int requestCode, int resultCode, Intent data) { 937 super.onActivityResult(requestCode, resultCode, data); 938 939 if (mEditUserInfoDialog != null && mEditUserInfoDialog.isShowing() 940 && mEditUserPhotoController.onActivityResult(requestCode, resultCode, data)) { 941 return; 942 } 943 944 AppRestrictionsPreference pref = mCustomRequestMap.get(requestCode); 945 if (pref == null) { 946 Log.w(TAG, "Unknown requestCode " + requestCode); 947 return; 948 } 949 950 if (resultCode == Activity.RESULT_OK) { 951 String packageName = pref.getKey().substring(PKG_PREFIX.length()); 952 ArrayList<RestrictionEntry> list = 953 data.getParcelableArrayListExtra(Intent.EXTRA_RESTRICTIONS_LIST); 954 Bundle bundle = data.getBundleExtra(Intent.EXTRA_RESTRICTIONS_BUNDLE); 955 if (list != null) { 956 // If there's a valid result, persist it to the user manager. 957 pref.setRestrictions(list); 958 mUserManager.setApplicationRestrictions(packageName, 959 RestrictionUtils.restrictionsToBundle(list), mUser); 960 } else if (bundle != null) { 961 // If there's a valid result, persist it to the user manager. 962 mUserManager.setApplicationRestrictions(packageName, bundle, mUser); 963 } 964 toggleAppPanel(pref); 965 } 966 // Remove request from the map 967 mCustomRequestMap.remove(requestCode); 968 } 969 970 private String findInArray(String[] choiceEntries, String[] choiceValues, 971 String selectedString) { 972 for (int i = 0; i < choiceValues.length; i++) { 973 if (choiceValues[i].equals(selectedString)) { 974 return choiceEntries[i]; 975 } 976 } 977 return selectedString; 978 } 979 980 @Override 981 public boolean onPreferenceClick(Preference preference) { 982 if (preference.getKey().startsWith(PKG_PREFIX)) { 983 AppRestrictionsPreference arp = (AppRestrictionsPreference) preference; 984 if (!arp.isImmutable()) { 985 arp.setChecked(!arp.isChecked()); 986 mSelectedPackages.put(arp.getKey().substring(PKG_PREFIX.length()), arp.isChecked()); 987 updateAllEntries(arp.getKey(), arp.isChecked()); 988 mAppListChanged = true; 989 } 990 return true; 991 } 992 return false; 993 } 994 995 @Override 996 public Dialog onCreateDialog(int dialogId) { 997 if (dialogId == DIALOG_ID_EDIT_USER_INFO) { 998 if (mEditUserInfoDialog != null) { 999 return mEditUserInfoDialog; 1000 } 1001 1002 LayoutInflater inflater = getActivity().getLayoutInflater(); 1003 View content = inflater.inflate(R.layout.edit_user_info_dialog_content, null); 1004 1005 UserInfo info = mUserManager.getUserInfo(mUser.getIdentifier()); 1006 1007 final EditText userNameView = (EditText) content.findViewById(R.id.user_name); 1008 userNameView.setText(info.name); 1009 1010 final ImageView userPhotoView = (ImageView) content.findViewById(R.id.user_photo); 1011 Drawable drawable = null; 1012 if (mSavedPhoto != null) { 1013 drawable = CircleFramedDrawable.getInstance(getActivity(), mSavedPhoto); 1014 } else { 1015 drawable = mUserIconView.getDrawable(); 1016 if (drawable == null) { 1017 drawable = getCircularUserIcon(); 1018 } 1019 } 1020 userPhotoView.setImageDrawable(drawable); 1021 1022 mEditUserPhotoController = new EditUserPhotoController(this, userPhotoView, 1023 mSavedPhoto, drawable); 1024 1025 mEditUserInfoDialog = new AlertDialog.Builder(getActivity()) 1026 .setTitle(R.string.profile_info_settings_title) 1027 .setIconAttribute(R.drawable.ic_settings_multiuser) 1028 .setView(content) 1029 .setCancelable(true) 1030 .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { 1031 @Override 1032 public void onClick(DialogInterface dialog, int which) { 1033 if (which == DialogInterface.BUTTON_POSITIVE) { 1034 // Update the name if changed. 1035 CharSequence userName = userNameView.getText(); 1036 if (!TextUtils.isEmpty(userName)) { 1037 CharSequence oldUserName = mUserNameView.getText(); 1038 if (oldUserName == null 1039 || !userName.toString().equals(oldUserName.toString())) { 1040 ((TextView) mHeaderView.findViewById(android.R.id.title)) 1041 .setText(userName.toString()); 1042 mUserManager.setUserName(mUser.getIdentifier(), 1043 userName.toString()); 1044 } 1045 } 1046 // Update the photo if changed. 1047 Drawable drawable = mEditUserPhotoController.getNewUserPhotoDrawable(); 1048 Bitmap bitmap = mEditUserPhotoController.getNewUserPhotoBitmap(); 1049 if (drawable != null && bitmap != null 1050 && !drawable.equals(mUserIconView.getDrawable())) { 1051 mUserIconView.setImageDrawable(drawable); 1052 new AsyncTask<Void, Void, Void>() { 1053 @Override 1054 protected Void doInBackground(Void... params) { 1055 mUserManager.setUserIcon(mUser.getIdentifier(), 1056 mEditUserPhotoController.getNewUserPhotoBitmap()); 1057 return null; 1058 } 1059 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null); 1060 } 1061 removeDialog(DIALOG_ID_EDIT_USER_INFO); 1062 } 1063 clearEditUserInfoDialog(); 1064 } 1065 }) 1066 .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { 1067 @Override 1068 public void onClick(DialogInterface dialog, int which) { 1069 clearEditUserInfoDialog(); 1070 } 1071 }) 1072 .create(); 1073 1074 // Make sure the IME is up. 1075 mEditUserInfoDialog.getWindow().setSoftInputMode( 1076 WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); 1077 1078 return mEditUserInfoDialog; 1079 } 1080 1081 return null; 1082 } 1083 1084 private void clearEditUserInfoDialog() { 1085 mEditUserInfoDialog = null; 1086 mSavedPhoto = null; 1087 } 1088 1089 private static class EditUserPhotoController { 1090 private static final int POPUP_LIST_ITEM_ID_CHOOSE_PHOTO = 1; 1091 private static final int POPUP_LIST_ITEM_ID_TAKE_PHOTO = 2; 1092 1093 // It seems that this class generates custom request codes and they may 1094 // collide with ours, these values are very unlikely to have a conflict. 1095 private static final int REQUEST_CODE_CHOOSE_PHOTO = Integer.MAX_VALUE; 1096 private static final int REQUEST_CODE_TAKE_PHOTO = Integer.MAX_VALUE - 1; 1097 private static final int REQUEST_CODE_CROP_PHOTO = Integer.MAX_VALUE - 2; 1098 1099 private static final String CROP_PICTURE_FILE_NAME = "CropEditUserPhoto.jpg"; 1100 private static final String TAKE_PICTURE_FILE_NAME = "TakeEditUserPhoto2.jpg"; 1101 1102 private final int mPhotoSize; 1103 1104 private final Context mContext; 1105 private final Fragment mFragment; 1106 private final ImageView mImageView; 1107 1108 private final Uri mCropPictureUri; 1109 private final Uri mTakePictureUri; 1110 1111 private Bitmap mNewUserPhotoBitmap; 1112 private Drawable mNewUserPhotoDrawable; 1113 1114 public EditUserPhotoController(Fragment fragment, ImageView view, 1115 Bitmap bitmap, Drawable drawable) { 1116 mContext = view.getContext(); 1117 mFragment = fragment; 1118 mImageView = view; 1119 mCropPictureUri = createTempImageUri(mContext, CROP_PICTURE_FILE_NAME); 1120 mTakePictureUri = createTempImageUri(mContext, TAKE_PICTURE_FILE_NAME); 1121 mPhotoSize = getPhotoSize(mContext); 1122 mImageView.setOnClickListener(new OnClickListener() { 1123 @Override 1124 public void onClick(View v) { 1125 showUpdatePhotoPopup(); 1126 } 1127 }); 1128 mNewUserPhotoBitmap = bitmap; 1129 mNewUserPhotoDrawable = drawable; 1130 } 1131 1132 public boolean onActivityResult(int requestCode, int resultCode, final Intent data) { 1133 if (resultCode != Activity.RESULT_OK) { 1134 return false; 1135 } 1136 switch (requestCode) { 1137 case REQUEST_CODE_CHOOSE_PHOTO: 1138 case REQUEST_CODE_CROP_PHOTO: { 1139 new AsyncTask<Void, Void, Bitmap>() { 1140 @Override 1141 protected Bitmap doInBackground(Void... params) { 1142 return BitmapFactory.decodeFile(mCropPictureUri.getPath()); 1143 } 1144 @Override 1145 protected void onPostExecute(Bitmap bitmap) { 1146 mNewUserPhotoBitmap = bitmap; 1147 mNewUserPhotoDrawable = CircleFramedDrawable 1148 .getInstance(mImageView.getContext(), mNewUserPhotoBitmap); 1149 mImageView.setImageDrawable(mNewUserPhotoDrawable); 1150 // Delete the files - not needed anymore. 1151 new File(mCropPictureUri.getPath()).delete(); 1152 new File(mTakePictureUri.getPath()).delete(); 1153 } 1154 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null); 1155 } return true; 1156 case REQUEST_CODE_TAKE_PHOTO: { 1157 cropPhoto(); 1158 } break; 1159 } 1160 return false; 1161 } 1162 1163 public Bitmap getNewUserPhotoBitmap() { 1164 return mNewUserPhotoBitmap; 1165 } 1166 1167 public Drawable getNewUserPhotoDrawable() { 1168 return mNewUserPhotoDrawable; 1169 } 1170 1171 private void showUpdatePhotoPopup() { 1172 final boolean canTakePhoto = canTakePhoto(); 1173 final boolean canChoosePhoto = canChoosePhoto(); 1174 1175 if (!canTakePhoto && !canChoosePhoto) { 1176 return; 1177 } 1178 1179 Context context = mImageView.getContext(); 1180 final List<AdapterItem> items = new ArrayList<AdapterItem>(); 1181 1182 if (canTakePhoto()) { 1183 String title = mImageView.getContext().getString( R.string.user_image_take_photo); 1184 AdapterItem item = new AdapterItem(title, POPUP_LIST_ITEM_ID_TAKE_PHOTO); 1185 items.add(item); 1186 } 1187 1188 if (canChoosePhoto) { 1189 String title = context.getString(R.string.user_image_choose_photo); 1190 AdapterItem item = new AdapterItem(title, POPUP_LIST_ITEM_ID_CHOOSE_PHOTO); 1191 items.add(item); 1192 } 1193 1194 final ListPopupWindow listPopupWindow = new ListPopupWindow(context); 1195 1196 listPopupWindow.setAnchorView(mImageView); 1197 listPopupWindow.setModal(true); 1198 listPopupWindow.setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED); 1199 1200 ListAdapter adapter = new ArrayAdapter<AdapterItem>(context, 1201 R.layout.edit_user_photo_popup_item, items); 1202 listPopupWindow.setAdapter(adapter); 1203 1204 final int width = Math.max(mImageView.getWidth(), context.getResources() 1205 .getDimensionPixelSize(R.dimen.update_user_photo_popup_min_width)); 1206 listPopupWindow.setWidth(width); 1207 1208 listPopupWindow.setOnItemClickListener(new AdapterView.OnItemClickListener() { 1209 @Override 1210 public void onItemClick(AdapterView<?> parent, View view, int position, long id) { 1211 AdapterItem item = items.get(position); 1212 switch (item.id) { 1213 case POPUP_LIST_ITEM_ID_CHOOSE_PHOTO: { 1214 choosePhoto(); 1215 listPopupWindow.dismiss(); 1216 } break; 1217 case POPUP_LIST_ITEM_ID_TAKE_PHOTO: { 1218 takePhoto(); 1219 listPopupWindow.dismiss(); 1220 } break; 1221 } 1222 } 1223 }); 1224 1225 listPopupWindow.show(); 1226 } 1227 1228 private boolean canTakePhoto() { 1229 return mImageView.getContext().getPackageManager().queryIntentActivities( 1230 new Intent(MediaStore.ACTION_IMAGE_CAPTURE), 1231 PackageManager.MATCH_DEFAULT_ONLY).size() > 0; 1232 } 1233 1234 private boolean canChoosePhoto() { 1235 Intent intent = new Intent(Intent.ACTION_GET_CONTENT); 1236 intent.setType("image/*"); 1237 return mImageView.getContext().getPackageManager().queryIntentActivities( 1238 intent, 0).size() > 0; 1239 } 1240 1241 private void takePhoto() { 1242 Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); 1243 intent.putExtra(MediaStore.EXTRA_OUTPUT, mTakePictureUri); 1244 mFragment.startActivityForResult(intent, REQUEST_CODE_TAKE_PHOTO); 1245 } 1246 1247 private void choosePhoto() { 1248 Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null); 1249 intent.setType("image/*"); 1250 intent.putExtra(MediaStore.EXTRA_OUTPUT, mCropPictureUri); 1251 appendCropExtras(intent); 1252 mFragment.startActivityForResult(intent, REQUEST_CODE_CHOOSE_PHOTO); 1253 } 1254 1255 private void cropPhoto() { 1256 Intent intent = new Intent("com.android.camera.action.CROP"); 1257 intent.setDataAndType(mTakePictureUri, "image/*"); 1258 intent.putExtra(MediaStore.EXTRA_OUTPUT, mCropPictureUri); 1259 appendCropExtras(intent); 1260 mFragment.startActivityForResult(intent, REQUEST_CODE_CROP_PHOTO); 1261 } 1262 1263 private void appendCropExtras(Intent intent) { 1264 intent.putExtra("crop", "true"); 1265 intent.putExtra("scale", true); 1266 intent.putExtra("scaleUpIfNeeded", true); 1267 intent.putExtra("aspectX", 1); 1268 intent.putExtra("aspectY", 1); 1269 intent.putExtra("outputX", mPhotoSize); 1270 intent.putExtra("outputY", mPhotoSize); 1271 } 1272 1273 private static int getPhotoSize(Context context) { 1274 Cursor cursor = context.getContentResolver().query( 1275 DisplayPhoto.CONTENT_MAX_DIMENSIONS_URI, 1276 new String[]{DisplayPhoto.DISPLAY_MAX_DIM}, null, null, null); 1277 try { 1278 cursor.moveToFirst(); 1279 return cursor.getInt(0); 1280 } finally { 1281 cursor.close(); 1282 } 1283 } 1284 1285 private static Uri createTempImageUri(Context context, String fileName) { 1286 File folder = context.getExternalCacheDir(); 1287 folder.mkdirs(); 1288 File fullPath = new File(folder, fileName); 1289 fullPath.delete(); 1290 return Uri.fromFile(fullPath.getAbsoluteFile()); 1291 } 1292 1293 private static final class AdapterItem { 1294 final String title; 1295 final int id; 1296 1297 public AdapterItem(String title, int id) { 1298 this.title = title; 1299 this.id = id; 1300 } 1301 1302 @Override 1303 public String toString() { 1304 return title; 1305 } 1306 } 1307 } 1308 } 1309