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