Home | History | Annotate | Download | only in editor
      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