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