Compact editor photo picker 1/2 (E15)
* Added photo selection fragment
* Swap between editor and photo selection fragments in CompactEditorActivity
* Moved PhotoHandler (which receives callbacks from PhotoSourceDialogFragment)
from the editor fragment to CompactEditorActivity since sourcing a photo
happens now from the editor photo view and the action bar when in the
photo selection fragment is visible.
* Extract code to get a bitmap or full size photo from a ValuesDelta in
CompactEditorPhotoView and move it to EditorUtiUtils so that it can be used
on both the photo selection fragment and the photo view
Bug 19697372
Bug 23589603
Change-Id: Iecebca44f505527d0be7a3803cd1d8fd4fef65e0
diff --git a/src/com/android/contacts/editor/CompactRawContactsEditorView.java b/src/com/android/contacts/editor/CompactRawContactsEditorView.java
index e71c814..090ea31 100644
--- a/src/com/android/contacts/editor/CompactRawContactsEditorView.java
+++ b/src/com/android/contacts/editor/CompactRawContactsEditorView.java
@@ -17,6 +17,7 @@
package com.android.contacts.editor;
import com.android.contacts.R;
+import com.android.contacts.common.ContactsUtils;
import com.android.contacts.common.model.AccountTypeManager;
import com.android.contacts.common.model.RawContactDelta;
import com.android.contacts.common.model.RawContactDeltaList;
@@ -27,7 +28,7 @@
import com.android.contacts.common.model.dataitem.DataKind;
import com.android.contacts.common.util.AccountsListAdapter;
import com.android.contacts.common.util.MaterialColorMapUtils;
-import com.android.contacts.editor.CompactContactEditorFragment.PhotoHandler;
+import com.android.contacts.util.ContactPhotoUtils;
import com.android.contacts.util.UiClosables;
import android.content.Context;
@@ -62,6 +63,8 @@
import android.widget.ListPopupWindow;
import android.widget.TextView;
+import java.io.File;
+import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -321,6 +324,7 @@
private boolean mIsExpanded;
private long mPhotoRawContactId;
+ private ValuesDelta mPhotoValuesDelta;
public CompactRawContactsEditorView(Context context) {
super(context);
@@ -401,17 +405,17 @@
}
/**
- * Pass through to {@link CompactPhotoEditorView#setPhotoHandler}.
+ * Pass through to {@link CompactPhotoEditorView#setListener}.
*/
- public void setPhotoHandler(PhotoHandler photoHandler) {
- mPhotoView.setPhotoHandler(photoHandler);
+ public void setPhotoListener(CompactPhotoEditorView.Listener listener) {
+ mPhotoView.setListener(listener);
}
- /**
- * Pass through to {@link CompactPhotoEditorView#setPhoto}.
- */
- public void setPhoto(Bitmap bitmap) {
- mPhotoView.setPhoto(bitmap);
+ public void removePhoto() {
+ mPhotoValuesDelta.setFromTemplate(false);
+ mPhotoValuesDelta.put(Photo.PHOTO, (byte[]) null);
+
+ mPhotoView.removePhoto();
}
/**
@@ -421,6 +425,28 @@
mPhotoView.setFullSizedPhoto(photoUri);
}
+ public void updatePhoto(Uri photoUri) {
+ // Even though high-res photos cannot be saved by passing them via
+ // an EntityDeltaList (since they cause the Bundle size limit to be
+ // exceeded), we still pass a low-res thumbnail. This simplifies
+ // code all over the place, because we don't have to test whether
+ // there is a change in EITHER the delta-list OR a changed photo...
+ // this way, there is always a change in the delta-list.
+ mPhotoValuesDelta.setFromTemplate(false);
+ mPhotoValuesDelta.setSuperPrimary(true);
+ try {
+ final byte[] bytes = EditorUiUtils.getCompressedThumbnailBitmapBytes(
+ getContext(), photoUri);
+ if (bytes != null) {
+ mPhotoValuesDelta.setPhoto(bytes);
+ }
+ } catch (FileNotFoundException e) {
+ elog("Failed to get bitmap from photo Uri");
+ }
+
+ mPhotoView.setFullSizedPhoto(photoUri);
+ }
+
/**
* Pass through to {@link CompactPhotoEditorView#isWritablePhotoSet}.
*/
@@ -435,6 +461,77 @@
return mPhotoRawContactId;
}
+ /**
+ * Returns a data holder for every non-default/non-empty photo from each raw contact, whether
+ * the raw contact is writable or not.
+ */
+ public ArrayList<CompactPhotoSelectionFragment.Photo> getPhotos() {
+ final ArrayList<CompactPhotoSelectionFragment.Photo> photos = new ArrayList<>();
+
+ final List<KindSectionData> kindSectionDataList =
+ mKindSectionDataMap.get(Photo.CONTENT_ITEM_TYPE);
+ for (int i = 0; i < kindSectionDataList.size(); i++) {
+ final KindSectionData kindSectionData = kindSectionDataList.get(i);
+ final AccountType accountType = kindSectionData.getAccountType();
+ final List<ValuesDelta> valuesDeltaList = kindSectionData.getValuesDeltas();
+ if (valuesDeltaList == null || valuesDeltaList.isEmpty()) continue;
+ for (int j = 0; j < valuesDeltaList.size(); j++) {
+ final ValuesDelta valuesDelta = valuesDeltaList.get(j);
+ final Bitmap bitmap = EditorUiUtils.getPhotoBitmap(valuesDelta);
+ if (bitmap == null) continue;
+
+ final CompactPhotoSelectionFragment.Photo photo =
+ new CompactPhotoSelectionFragment.Photo();
+ photo.titleRes = accountType.titleRes;
+ photo.iconRes = accountType.iconRes;
+ photo.syncAdapterPackageName = accountType.syncAdapterPackageName;
+ photo.valuesDelta = valuesDelta;
+ photo.primary = valuesDelta.isSuperPrimary();
+ photo.kindSectionDataListIndex = i;
+ photo.valuesDeltaListIndex = j;
+ photos.add(photo);
+ }
+ }
+
+ return photos;
+ }
+
+ /**
+ * Marks the raw contact photo given as primary for the aggregate contact and updates the
+ * UI.
+ */
+ public void setPrimaryPhoto(CompactPhotoSelectionFragment.Photo photo) {
+ // Unset primary for all other photos
+ final List<KindSectionData> kindSectionDataList =
+ mKindSectionDataMap.get(Photo.CONTENT_ITEM_TYPE);
+ for (KindSectionData kindSectionData : kindSectionDataList) {
+ final List<ValuesDelta> valuesDeltaList = kindSectionData.getValuesDeltas();
+ for (ValuesDelta valuesDelta : valuesDeltaList) {
+ valuesDelta.setSuperPrimary(false);
+ }
+ }
+
+ // Find the values delta to mark as primary
+ if (photo.kindSectionDataListIndex < 0
+ || photo.kindSectionDataListIndex >= kindSectionDataList.size()) {
+ wlog("Invalid kind section data list index");
+ return;
+ }
+ final KindSectionData kindSectionData =
+ kindSectionDataList.get(photo.kindSectionDataListIndex);
+ final List<ValuesDelta> valuesDeltaList = kindSectionData.getValuesDeltas();
+ if (photo.valuesDeltaListIndex >= valuesDeltaList.size()) {
+ wlog("Invalid values delta list index");
+ return;
+ }
+ final ValuesDelta valuesDelta = valuesDeltaList.get(photo.valuesDeltaListIndex);
+ valuesDelta.setFromTemplate(false);
+ valuesDelta.setSuperPrimary(true);
+
+ // Update the UI
+ mPhotoView.setPhoto(valuesDelta, mMaterialPalette);
+ }
+
public View getAggregationAnchorView() {
final List<CompactKindSectionView> kindSectionViews = getKindSectionViews(
StructuredName.CONTENT_ITEM_TYPE);
@@ -691,31 +788,50 @@
}
private void addPhotoView() {
- // Get the kind section data and values delta that will back the photo view
- Pair<KindSectionData,ValuesDelta> pair = getPrimaryKindSectionData(mPhotoId);
+ // Get the kind section data and values delta that we will display in the photo view
+ Pair<KindSectionData,ValuesDelta> pair = getPrimaryPhotoKindSectionData(mPhotoId);
if (pair == null) {
wlog("photo: no kind section data parsed");
+ mPhotoView.setReadOnly(true);
return;
}
- final KindSectionData kindSectionData = pair.first;
- final ValuesDelta valuesDelta = pair.second;
- // If we're editing a read-only contact we want to display the photo from the
- // read-only contact in a photo editor backed by the new raw contact
- // that was created.
- if (mHasNewContact) {
- mPhotoRawContactId = mPrimaryRawContactDelta == null
- ? null : mPrimaryRawContactDelta.getRawContactId();
+ // Set the photo view
+ final ValuesDelta primaryValuesDelta = pair.second;
+ mPhotoView.setPhoto(primaryValuesDelta, mMaterialPalette);
+
+ // Find the raw contact ID and values delta that will be written when the photo is edited
+ final KindSectionData primaryKindSectionData = pair.first;
+ if (mHasNewContact && mPrimaryRawContactDelta != null
+ && !primaryKindSectionData.getValuesDeltas().isEmpty()) {
+ // If we're editing a read-only contact we want to display the photo from the
+ // read-only contact in a photo editor view, but update the new raw contact
+ // that was created.
+ mPhotoRawContactId = mPrimaryRawContactDelta.getRawContactId();
+ mPhotoValuesDelta = primaryKindSectionData.getValuesDeltas().get(0);
+ mPhotoView.setReadOnly(false);
+ return;
+ }
+ if (primaryKindSectionData.getAccountType().areContactsWritable() &&
+ !primaryKindSectionData.getValuesDeltas().isEmpty()) {
+ mPhotoRawContactId = primaryKindSectionData.getRawContactDelta().getRawContactId();
+ mPhotoValuesDelta = primaryKindSectionData.getValuesDeltas().get(0);
+ mPhotoView.setReadOnly(false);
+ return;
}
- mPhotoRawContactId = kindSectionData.getRawContactDelta().getRawContactId();
- mPhotoView.setValues(kindSectionData.getDataKind(), valuesDelta,
- kindSectionData.getRawContactDelta(),
- !kindSectionData.getAccountType().areContactsWritable(), mMaterialPalette,
- mViewIdGenerator);
+ final KindSectionData writableKindSectionData = getFirstWritablePhotoKindSectionData();
+ if (writableKindSectionData == null
+ || writableKindSectionData.getValuesDeltas().isEmpty()) {
+ mPhotoView.setReadOnly(true);
+ return;
+ }
+ mPhotoRawContactId = writableKindSectionData.getRawContactDelta().getRawContactId();
+ mPhotoValuesDelta = writableKindSectionData.getValuesDeltas().get(0);
+ mPhotoView.setReadOnly(false);
}
- private Pair<KindSectionData,ValuesDelta> getPrimaryKindSectionData(long id) {
+ private Pair<KindSectionData,ValuesDelta> getPrimaryPhotoKindSectionData(long id) {
final String mimeType = Photo.CONTENT_ITEM_TYPE;
final List<KindSectionData> kindSectionDataList = mKindSectionDataMap.get(mimeType);
@@ -766,6 +882,17 @@
? new Pair<>(resultKindSectionData, resultValuesDelta) : null;
}
+ private KindSectionData getFirstWritablePhotoKindSectionData() {
+ final String mimeType = Photo.CONTENT_ITEM_TYPE;
+ final List<KindSectionData> kindSectionDataList = mKindSectionDataMap.get(mimeType);
+ for (KindSectionData kindSectionData : kindSectionDataList) {
+ if (kindSectionData.getAccountType().areContactsWritable()) {
+ return kindSectionData;
+ }
+ }
+ return null;
+ }
+
private void addKindSectionViews() {
// Sort the kinds
final TreeSet<Map.Entry<String,List<KindSectionData>>> entries =