1 /* 2 * Copyright (C) 2010 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.contacts.editor; 18 19 import android.app.Activity; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.graphics.Bitmap; 23 import android.net.Uri; 24 import android.os.Bundle; 25 import android.provider.ContactsContract.CommonDataKinds.Photo; 26 import android.text.TextUtils; 27 import android.util.Log; 28 import android.view.LayoutInflater; 29 import android.view.MenuItem; 30 import android.view.View; 31 import android.view.ViewGroup; 32 import android.widget.AdapterView; 33 import android.widget.LinearLayout; 34 import android.widget.ListPopupWindow; 35 36 import com.android.contacts.ContactSaveService; 37 import com.android.contacts.R; 38 import com.android.contacts.activities.ContactEditorActivity; 39 import com.android.contacts.activities.ContactEditorBaseActivity.ContactEditor; 40 import com.android.contacts.common.model.AccountTypeManager; 41 import com.android.contacts.common.model.RawContactDelta; 42 import com.android.contacts.common.model.RawContactDeltaList; 43 import com.android.contacts.common.model.ValuesDelta; 44 import com.android.contacts.common.model.account.AccountType; 45 import com.android.contacts.common.model.account.AccountWithDataSet; 46 import com.android.contacts.common.util.AccountsListAdapter; 47 import com.android.contacts.common.util.AccountsListAdapter.AccountListFilter; 48 import com.android.contacts.detail.PhotoSelectionHandler; 49 import com.android.contacts.editor.Editor.EditorListener; 50 import com.android.contacts.util.ContactPhotoUtils; 51 import com.android.contacts.util.UiClosables; 52 53 import java.io.FileNotFoundException; 54 import java.util.Collections; 55 import java.util.HashMap; 56 import java.util.List; 57 58 /** 59 * Contact editor with all fields displayed. 60 */ 61 public class ContactEditorFragment extends ContactEditorBaseFragment implements 62 RawContactReadOnlyEditorView.Listener { 63 64 private static final String KEY_EXPANDED_EDITORS = "expandedEditors"; 65 66 private static final String KEY_RAW_CONTACT_ID_REQUESTING_PHOTO = "photorequester"; 67 private static final String KEY_CURRENT_PHOTO_URI = "currentphotouri"; 68 private static final String KEY_UPDATED_PHOTOS = "updatedPhotos"; 69 70 // Used to store which raw contact editors have been expanded. Keyed on raw contact ids. 71 private HashMap<Long, Boolean> mExpandedEditors = new HashMap<Long, Boolean>(); 72 73 /** 74 * The raw contact for which we started "take photo" or "choose photo from gallery" most 75 * recently. Used to restore {@link #mCurrentPhotoHandler} after orientation change. 76 */ 77 private long mRawContactIdRequestingPhoto; 78 79 /** 80 * The {@link PhotoHandler} for the photo editor for the {@link #mRawContactIdRequestingPhoto} 81 * raw contact. 82 * 83 * A {@link PhotoHandler} is created for each photo editor in {@link #bindPhotoHandler}, but 84 * the only "active" one should get the activity result. This member represents the active 85 * one. 86 */ 87 private PhotoHandler mCurrentPhotoHandler; 88 private Uri mCurrentPhotoUri; 89 private Bundle mUpdatedPhotos = new Bundle(); 90 91 public ContactEditorFragment() { 92 } 93 94 @Override 95 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) { 96 final View view = inflater.inflate(R.layout.contact_editor_fragment, container, false); 97 98 mContent = (LinearLayout) view.findViewById(R.id.editors); 99 100 setHasOptionsMenu(true); 101 102 return view; 103 } 104 105 @Override 106 public void onCreate(Bundle savedState) { 107 super.onCreate(savedState); 108 109 if (savedState != null) { 110 mExpandedEditors = (HashMap<Long, Boolean>) 111 savedState.getSerializable(KEY_EXPANDED_EDITORS); 112 mRawContactIdRequestingPhoto = savedState.getLong( 113 KEY_RAW_CONTACT_ID_REQUESTING_PHOTO); 114 mCurrentPhotoUri = savedState.getParcelable(KEY_CURRENT_PHOTO_URI); 115 mUpdatedPhotos = savedState.getParcelable(KEY_UPDATED_PHOTOS); 116 mRawContactIdToDisplayAlone = savedState.getLong( 117 ContactEditorBaseFragment.INTENT_EXTRA_RAW_CONTACT_ID_TO_DISPLAY_ALONE, -1); 118 } 119 } 120 121 @Override 122 public void load(String action, Uri lookupUri, Bundle intentExtras) { 123 super.load(action, lookupUri, intentExtras); 124 if (intentExtras != null) { 125 mRawContactIdToDisplayAlone = intentExtras.getLong( 126 ContactEditorBaseFragment.INTENT_EXTRA_RAW_CONTACT_ID_TO_DISPLAY_ALONE, -1); 127 } 128 } 129 130 @Override 131 public void onStart() { 132 getLoaderManager().initLoader(LOADER_GROUPS, null, mGroupsLoaderListener); 133 super.onStart(); 134 } 135 136 @Override 137 public void onExternalEditorRequest(AccountWithDataSet account, Uri uri) { 138 if (mListener != null) { 139 mListener.onCustomEditContactActivityRequested(account, uri, null, false); 140 } 141 } 142 143 @Override 144 public void onEditorExpansionChanged() { 145 updatedExpandedEditorsMap(); 146 } 147 148 @Override 149 protected void setGroupMetaData() { 150 if (mGroupMetaData == null) { 151 return; 152 } 153 int editorCount = mContent.getChildCount(); 154 for (int i = 0; i < editorCount; i++) { 155 BaseRawContactEditorView editor = (BaseRawContactEditorView) mContent.getChildAt(i); 156 editor.setGroupMetaData(mGroupMetaData); 157 } 158 } 159 160 @Override 161 public boolean onOptionsItemSelected(MenuItem item) { 162 if (item.getItemId() == android.R.id.home) { 163 return revert(); 164 } 165 return super.onOptionsItemSelected(item); 166 } 167 168 @Override 169 protected void bindEditors() { 170 // bindEditors() can only bind views if there is data in mState, so immediately return 171 // if mState is null 172 if (mState.isEmpty()) { 173 return; 174 } 175 176 // Check if delta list is ready. Delta list is populated from existing data and when 177 // editing an read-only contact, it's also populated with newly created data for the 178 // blank form. When the data is not ready, skip. This method will be called multiple times. 179 if ((mIsEdit && !mExistingContactDataReady) || (mHasNewContact && !mNewContactDataReady)) { 180 return; 181 } 182 183 // Sort the editors 184 Collections.sort(mState, mComparator); 185 186 // Remove any existing editors and rebuild any visible 187 mContent.removeAllViews(); 188 189 final LayoutInflater inflater = (LayoutInflater) mContext.getSystemService( 190 Context.LAYOUT_INFLATER_SERVICE); 191 final AccountTypeManager accountTypes = AccountTypeManager.getInstance(mContext); 192 int numRawContacts = mState.size(); 193 194 for (int i = 0; i < numRawContacts; i++) { 195 // TODO ensure proper ordering of entities in the list 196 final RawContactDelta rawContactDelta = mState.get(i); 197 if (!rawContactDelta.isVisible()) continue; 198 199 final AccountType type = rawContactDelta.getAccountType(accountTypes); 200 final long rawContactId = rawContactDelta.getRawContactId(); 201 202 if (mRawContactIdToDisplayAlone != -1 && mRawContactIdToDisplayAlone != rawContactId) { 203 continue; 204 } 205 206 final BaseRawContactEditorView editor; 207 if (!type.areContactsWritable()) { 208 editor = (BaseRawContactEditorView) inflater.inflate( 209 R.layout.raw_contact_readonly_editor_view, mContent, false); 210 } else { 211 editor = (RawContactEditorView) inflater.inflate(R.layout.raw_contact_editor_view, 212 mContent, false); 213 } 214 editor.setListener(this); 215 final List<AccountWithDataSet> accounts = AccountTypeManager.getInstance(mContext) 216 .getAccounts(true); 217 if (mHasNewContact && !mNewLocalProfile && accounts.size() > 1) { 218 addAccountSwitcher(mState.get(0), editor); 219 } 220 221 editor.setEnabled(isEnabled()); 222 223 if (mRawContactIdToDisplayAlone != -1) { 224 editor.setCollapsed(false); 225 } else if (mExpandedEditors.containsKey(rawContactId)) { 226 editor.setCollapsed(mExpandedEditors.get(rawContactId)); 227 } else { 228 // By default, only the first editor will be expanded. 229 editor.setCollapsed(i != 0); 230 } 231 232 mContent.addView(editor); 233 234 editor.setState(rawContactDelta, type, mViewIdGenerator, isEditingUserProfile()); 235 if (mRawContactIdToDisplayAlone != -1) { 236 editor.setCollapsible(false); 237 } else { 238 editor.setCollapsible(numRawContacts > 1); 239 } 240 241 // Set up the photo handler. 242 bindPhotoHandler(editor, type, mState); 243 244 // If a new photo was chosen but not yet saved, we need to update the UI to 245 // reflect this. 246 final Uri photoUri = updatedPhotoUriForRawContact(rawContactId); 247 if (photoUri != null) editor.setFullSizedPhoto(photoUri); 248 249 if (editor instanceof RawContactEditorView) { 250 final Activity activity = getActivity(); 251 final RawContactEditorView rawContactEditor = (RawContactEditorView) editor; 252 final ValuesDelta nameValuesDelta = rawContactEditor.getNameEditor().getValues(); 253 final EditorListener structuredNameListener = new EditorListener() { 254 255 @Override 256 public void onRequest(int request) { 257 // Make sure the activity is running 258 if (activity.isFinishing()) { 259 return; 260 } 261 if (!isEditingUserProfile()) { 262 if (request == EditorListener.FIELD_CHANGED) { 263 if (!nameValuesDelta.isSuperPrimary()) { 264 unsetSuperPrimaryForAllNameEditors(); 265 nameValuesDelta.setSuperPrimary(true); 266 } 267 acquireAggregationSuggestions(activity, 268 rawContactEditor.getNameEditor().getRawContactId(), 269 rawContactEditor.getNameEditor().getValues()); 270 } else if (request == EditorListener.FIELD_TURNED_EMPTY) { 271 if (nameValuesDelta.isSuperPrimary()) { 272 nameValuesDelta.setSuperPrimary(false); 273 } 274 } 275 } 276 } 277 278 @Override 279 public void onDeleteRequested(Editor removedEditor) { 280 } 281 }; 282 283 final StructuredNameEditorView nameEditor = rawContactEditor.getNameEditor(); 284 nameEditor.setEditorListener(structuredNameListener); 285 286 rawContactEditor.setAutoAddToDefaultGroup(mAutoAddToDefaultGroup); 287 288 if (!isEditingUserProfile() && isAggregationSuggestionRawContactId(rawContactId)) { 289 acquireAggregationSuggestions(activity, 290 rawContactEditor.getNameEditor().getRawContactId(), 291 rawContactEditor.getNameEditor().getValues()); 292 } 293 } 294 } 295 296 setGroupMetaData(); 297 298 // Show editor now that we've loaded state 299 mContent.setVisibility(View.VISIBLE); 300 301 // Refresh Action Bar as the visibility of the join command 302 // Activity can be null if we have been detached from the Activity 303 invalidateOptionsMenu(); 304 305 updatedExpandedEditorsMap(); 306 } 307 308 private void unsetSuperPrimaryForAllNameEditors() { 309 for (int i = 0; i < mContent.getChildCount(); i++) { 310 final View view = mContent.getChildAt(i); 311 if (view instanceof RawContactEditorView) { 312 final RawContactEditorView rawContactEditorView = (RawContactEditorView) view; 313 final StructuredNameEditorView nameEditorView = 314 rawContactEditorView.getNameEditor(); 315 if (nameEditorView != null) { 316 final ValuesDelta valuesDelta = nameEditorView.getValues(); 317 if (valuesDelta != null) { 318 valuesDelta.setSuperPrimary(false); 319 } 320 } 321 } 322 } 323 } 324 325 /** 326 * Update the values in {@link #mExpandedEditors}. 327 */ 328 private void updatedExpandedEditorsMap() { 329 for (int i = 0; i < mContent.getChildCount(); i++) { 330 final View childView = mContent.getChildAt(i); 331 if (childView instanceof BaseRawContactEditorView) { 332 BaseRawContactEditorView childEditor = (BaseRawContactEditorView) childView; 333 mExpandedEditors.put(childEditor.getRawContactId(), childEditor.isCollapsed()); 334 } 335 } 336 } 337 338 /** 339 * If we've stashed a temporary file containing a contact's new photo, return its URI. 340 * @param rawContactId identifies the raw-contact whose Bitmap we'll try to return. 341 * @return Uru of photo for specified raw-contact, or null 342 */ 343 private Uri updatedPhotoUriForRawContact(long rawContactId) { 344 return (Uri) mUpdatedPhotos.get(String.valueOf(rawContactId)); 345 } 346 347 private void bindPhotoHandler(BaseRawContactEditorView editor, AccountType type, 348 RawContactDeltaList state) { 349 final int mode; 350 boolean showIsPrimaryOption; 351 if (type.areContactsWritable()) { 352 if (editor.hasSetPhoto()) { 353 mode = PhotoActionPopup.Modes.WRITE_ABLE_PHOTO; 354 showIsPrimaryOption = hasMoreThanOnePhoto(); 355 } else { 356 mode = PhotoActionPopup.Modes.NO_PHOTO; 357 showIsPrimaryOption = false; 358 } 359 } else if (editor.hasSetPhoto() && hasMoreThanOnePhoto()) { 360 mode = PhotoActionPopup.Modes.READ_ONLY_PHOTO; 361 showIsPrimaryOption = true; 362 } else { 363 // Read-only and either no photo or the only photo ==> no options 364 editor.getPhotoEditor().setEditorListener(null); 365 editor.getPhotoEditor().setShowPrimary(false); 366 return; 367 } 368 if (mRawContactIdToDisplayAlone != -1) { 369 showIsPrimaryOption = false; 370 } 371 final PhotoHandler photoHandler = new PhotoHandler(mContext, editor, mode, state); 372 editor.getPhotoEditor().setEditorListener( 373 (PhotoHandler.PhotoEditorListener) photoHandler.getListener()); 374 editor.getPhotoEditor().setShowPrimary(showIsPrimaryOption); 375 376 // Note a newly created raw contact gets some random negative ID, so any value is valid 377 // here. (i.e. don't check against -1 or anything.) 378 if (mRawContactIdRequestingPhoto == editor.getRawContactId()) { 379 mCurrentPhotoHandler = photoHandler; 380 } 381 } 382 383 private void addAccountSwitcher( 384 final RawContactDelta currentState, BaseRawContactEditorView editor) { 385 final AccountWithDataSet currentAccount = new AccountWithDataSet( 386 currentState.getAccountName(), 387 currentState.getAccountType(), 388 currentState.getDataSet()); 389 final View accountView = editor.findViewById(R.id.account); 390 final View anchorView = editor.findViewById(R.id.account_selector_container); 391 if (accountView == null) { 392 return; 393 } 394 anchorView.setVisibility(View.VISIBLE); 395 accountView.setOnClickListener(new View.OnClickListener() { 396 @Override 397 public void onClick(View v) { 398 final ListPopupWindow popup = new ListPopupWindow(mContext, null); 399 final AccountsListAdapter adapter = 400 new AccountsListAdapter(mContext, 401 AccountListFilter.ACCOUNTS_CONTACT_WRITABLE, currentAccount); 402 popup.setWidth(anchorView.getWidth()); 403 popup.setAnchorView(anchorView); 404 popup.setAdapter(adapter); 405 popup.setModal(true); 406 popup.setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED); 407 popup.setOnItemClickListener(new AdapterView.OnItemClickListener() { 408 @Override 409 public void onItemClick(AdapterView<?> parent, View view, int position, 410 long id) { 411 UiClosables.closeQuietly(popup); 412 AccountWithDataSet newAccount = adapter.getItem(position); 413 if (!newAccount.equals(currentAccount)) { 414 rebindEditorsForNewContact(currentState, currentAccount, newAccount); 415 } 416 } 417 }); 418 popup.show(); 419 } 420 }); 421 } 422 423 @Override 424 protected boolean doSaveAction(int saveMode, Long joinContactId) { 425 final Intent intent = ContactSaveService.createSaveContactIntent(mContext, mState, 426 SAVE_MODE_EXTRA_KEY, saveMode, isEditingUserProfile(), 427 ((Activity) mContext).getClass(), ContactEditorActivity.ACTION_SAVE_COMPLETED, 428 mUpdatedPhotos, JOIN_CONTACT_ID_EXTRA_KEY, joinContactId); 429 return startSaveService(mContext, intent, saveMode); 430 } 431 432 @Override 433 public void onSaveInstanceState(Bundle outState) { 434 outState.putSerializable(KEY_EXPANDED_EDITORS, mExpandedEditors); 435 outState.putLong(KEY_RAW_CONTACT_ID_REQUESTING_PHOTO, mRawContactIdRequestingPhoto); 436 outState.putParcelable(KEY_CURRENT_PHOTO_URI, mCurrentPhotoUri); 437 outState.putParcelable(KEY_UPDATED_PHOTOS, mUpdatedPhotos); 438 outState.putLong(ContactEditorBaseFragment.INTENT_EXTRA_RAW_CONTACT_ID_TO_DISPLAY_ALONE, 439 mRawContactIdToDisplayAlone); 440 super.onSaveInstanceState(outState); 441 } 442 443 @Override 444 public void onActivityResult(int requestCode, int resultCode, Intent data) { 445 if (mStatus == Status.SUB_ACTIVITY) { 446 mStatus = Status.EDITING; 447 } 448 449 // See if the photo selection handler handles this result. 450 if (mCurrentPhotoHandler != null && mCurrentPhotoHandler.handlePhotoActivityResult( 451 requestCode, resultCode, data)) { 452 return; 453 } 454 455 super.onActivityResult(requestCode, resultCode, data); 456 } 457 458 @Override 459 protected void joinAggregate(final long contactId) { 460 final Intent intent = ContactSaveService.createJoinContactsIntent( 461 mContext, mContactIdForJoin, contactId, ContactEditorActivity.class, 462 ContactEditorActivity.ACTION_JOIN_COMPLETED); 463 mContext.startService(intent); 464 } 465 466 /** 467 * Sets the photo stored in mPhoto and writes it to the RawContact with the given id 468 */ 469 private void setPhoto(long rawContact, Bitmap photo, Uri photoUri) { 470 BaseRawContactEditorView requestingEditor = getRawContactEditorView(rawContact); 471 472 if (photo == null || photo.getHeight() <= 0 || photo.getWidth() <= 0) { 473 // This is unexpected. 474 Log.w(TAG, "Invalid bitmap passed to setPhoto()"); 475 } 476 477 if (requestingEditor != null) { 478 requestingEditor.setPhotoEntry(photo); 479 // Immediately set all other photos as non-primary. Otherwise the UI can display 480 // multiple photos as "Primary photo". 481 for (int i = 0; i < mContent.getChildCount(); i++) { 482 final View childView = mContent.getChildAt(i); 483 if (childView instanceof BaseRawContactEditorView 484 && childView != requestingEditor) { 485 final BaseRawContactEditorView rawContactEditor 486 = (BaseRawContactEditorView) childView; 487 rawContactEditor.getPhotoEditor().setSuperPrimary(false); 488 } 489 } 490 } else { 491 Log.w(TAG, "The contact that requested the photo is no longer present."); 492 } 493 494 mUpdatedPhotos.putParcelable(String.valueOf(rawContact), photoUri); 495 } 496 497 /** 498 * Finds raw contact editor view for the given rawContactId. 499 */ 500 @Override 501 protected View getAggregationAnchorView(long rawContactId) { 502 BaseRawContactEditorView editorView = getRawContactEditorView(rawContactId); 503 return editorView == null ? null : editorView.findViewById(R.id.anchor_view); 504 } 505 506 public BaseRawContactEditorView getRawContactEditorView(long rawContactId) { 507 for (int i = 0; i < mContent.getChildCount(); i++) { 508 final View childView = mContent.getChildAt(i); 509 if (childView instanceof BaseRawContactEditorView) { 510 final BaseRawContactEditorView editor = (BaseRawContactEditorView) childView; 511 if (editor.getRawContactId() == rawContactId) { 512 return editor; 513 } 514 } 515 } 516 return null; 517 } 518 519 /** 520 * Returns true if there is currently more than one photo on screen. 521 */ 522 private boolean hasMoreThanOnePhoto() { 523 int countWithPicture = 0; 524 final int numEntities = mState.size(); 525 for (int i = 0; i < numEntities; i++) { 526 final RawContactDelta entity = mState.get(i); 527 if (entity.isVisible()) { 528 final ValuesDelta primary = entity.getPrimaryEntry(Photo.CONTENT_ITEM_TYPE); 529 if (primary != null && primary.getPhoto() != null) { 530 countWithPicture++; 531 } else { 532 final long rawContactId = entity.getRawContactId(); 533 final Uri uri = mUpdatedPhotos.getParcelable(String.valueOf(rawContactId)); 534 if (uri != null) { 535 try { 536 mContext.getContentResolver().openInputStream(uri); 537 countWithPicture++; 538 } catch (FileNotFoundException e) { 539 } 540 } 541 } 542 543 if (countWithPicture > 1) { 544 return true; 545 } 546 } 547 } 548 return false; 549 } 550 551 /** 552 * Custom photo handler for the editor. The inner listener that this creates also has a 553 * reference to the editor and acts as an {@link EditorListener}, and uses that editor to hold 554 * state information in several of the listener methods. 555 */ 556 private final class PhotoHandler extends PhotoSelectionHandler { 557 558 final long mRawContactId; 559 private final BaseRawContactEditorView mEditor; 560 private final PhotoActionListener mPhotoEditorListener; 561 562 public PhotoHandler(Context context, BaseRawContactEditorView editor, int photoMode, 563 RawContactDeltaList state) { 564 super(context, editor.getPhotoEditor().getChangeAnchorView(), photoMode, false, state); 565 mEditor = editor; 566 mRawContactId = editor.getRawContactId(); 567 mPhotoEditorListener = new PhotoEditorListener(); 568 } 569 570 @Override 571 public PhotoActionListener getListener() { 572 return mPhotoEditorListener; 573 } 574 575 @Override 576 public void startPhotoActivity(Intent intent, int requestCode, Uri photoUri) { 577 mRawContactIdRequestingPhoto = mEditor.getRawContactId(); 578 mCurrentPhotoHandler = this; 579 mStatus = Status.SUB_ACTIVITY; 580 mCurrentPhotoUri = photoUri; 581 ContactEditorFragment.this.startActivityForResult(intent, requestCode); 582 } 583 584 private final class PhotoEditorListener extends PhotoSelectionHandler.PhotoActionListener 585 implements EditorListener { 586 587 @Override 588 public void onRequest(int request) { 589 if (!hasValidState()) return; 590 591 if (request == EditorListener.REQUEST_PICK_PHOTO) { 592 onClick(mEditor.getPhotoEditor()); 593 } 594 if (request == EditorListener.REQUEST_PICK_PRIMARY_PHOTO) { 595 useAsPrimaryChosen(); 596 } 597 } 598 599 @Override 600 public void onDeleteRequested(Editor removedEditor) { 601 // The picture cannot be deleted, it can only be removed, which is handled by 602 // onRemovePictureChosen() 603 } 604 605 /** 606 * User has chosen to set the selected photo as the (super) primary photo 607 */ 608 public void useAsPrimaryChosen() { 609 // Set the IsSuperPrimary for each editor 610 int count = mContent.getChildCount(); 611 for (int i = 0; i < count; i++) { 612 final View childView = mContent.getChildAt(i); 613 if (childView instanceof BaseRawContactEditorView) { 614 final BaseRawContactEditorView editor = 615 (BaseRawContactEditorView) childView; 616 final PhotoEditorView photoEditor = editor.getPhotoEditor(); 617 photoEditor.setSuperPrimary(editor == mEditor); 618 } 619 } 620 bindEditors(); 621 } 622 623 /** 624 * User has chosen to remove a picture 625 */ 626 @Override 627 public void onRemovePictureChosen() { 628 mEditor.setPhotoEntry(null); 629 630 // Prevent bitmap from being restored if rotate the device. 631 // (only if we first chose a new photo before removing it) 632 mUpdatedPhotos.remove(String.valueOf(mRawContactId)); 633 bindEditors(); 634 } 635 636 @Override 637 public void onPhotoSelected(Uri uri) throws FileNotFoundException { 638 final Bitmap bitmap = ContactPhotoUtils.getBitmapFromUri(mContext, uri); 639 setPhoto(mRawContactId, bitmap, uri); 640 mCurrentPhotoHandler = null; 641 bindEditors(); 642 } 643 644 @Override 645 public Uri getCurrentPhotoUri() { 646 return mCurrentPhotoUri; 647 } 648 649 @Override 650 public void onPhotoSelectionDismissed() { 651 // Nothing to do. 652 } 653 } 654 } 655 } 656