16fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro/* 26fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * Copyright (C) 2011 The Android Open Source Project 36fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * 46fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * Licensed under the Apache License, Version 2.0 (the "License"); 56fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * you may not use this file except in compliance with the License. 66fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * You may obtain a copy of the License at 76fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * 86fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * http://www.apache.org/licenses/LICENSE-2.0 96fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * 106fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * Unless required by applicable law or agreed to in writing, software 116fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * distributed under the License is distributed on an "AS IS" BASIS, 126fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 136fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * See the License for the specific language governing permissions and 146fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * limitations under the License. 156fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro */ 166fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 176fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoropackage com.android.contacts.detail; 186fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 196fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.app.Activity; 206fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.content.ActivityNotFoundException; 219a409d474b2b4f2e4261657f864eeb866fcb8816Daniel Lehmannimport android.content.ContentValues; 226fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.content.Context; 236fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.content.Intent; 246fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.database.Cursor; 256fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.net.Uri; 266fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.provider.ContactsContract.CommonDataKinds.Photo; 276fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.provider.ContactsContract.DisplayPhoto; 289a409d474b2b4f2e4261657f864eeb866fcb8816Daniel Lehmannimport android.provider.ContactsContract.RawContacts; 296fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.provider.MediaStore; 306fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.util.Log; 316fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.view.View; 326fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.view.View.OnClickListener; 336fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.widget.ListPopupWindow; 346fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.widget.PopupWindow.OnDismissListener; 356fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.widget.Toast; 3661414c2c4627ab588cc0f54eae5e0c5591f7d8d9Chiao Cheng 3761414c2c4627ab588cc0f54eae5e0c5591f7d8d9Chiao Chengimport com.android.contacts.R; 3861414c2c4627ab588cc0f54eae5e0c5591f7d8d9Chiao Chengimport com.android.contacts.editor.PhotoActionPopup; 390d5588da244d0992c3ff8f25d0875fdf95a8c644Chiao Chengimport com.android.contacts.common.model.AccountTypeManager; 40cd321f65f1e50409812976380ad1f0fdb3fa35cbYorke Leeimport com.android.contacts.common.model.RawContactModifier; 41cd321f65f1e50409812976380ad1f0fdb3fa35cbYorke Leeimport com.android.contacts.common.model.RawContactDelta; 42738ff8623dc77dd91a1b9023861e924ba5e4c27eChiao Chengimport com.android.contacts.common.model.ValuesDelta; 43428f008513d1591cc08fcfe2cf0c9237fb313241Chiao Chengimport com.android.contacts.common.model.account.AccountType; 44cd321f65f1e50409812976380ad1f0fdb3fa35cbYorke Leeimport com.android.contacts.common.model.RawContactDeltaList; 4561414c2c4627ab588cc0f54eae5e0c5591f7d8d9Chiao Chengimport com.android.contacts.util.ContactPhotoUtils; 46619ac169e45bf547a2cbcd8a9775bb234f45c00fChiao Chengimport com.android.contacts.util.UiClosables; 4761414c2c4627ab588cc0f54eae5e0c5591f7d8d9Chiao Cheng 48637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Leeimport java.io.FileNotFoundException; 496fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 506fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro/** 516fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * Handles displaying a photo selection popup for a given photo view and dealing with the results 526fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * that come back. 536fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro */ 54e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Garguspublic abstract class PhotoSelectionHandler implements OnClickListener { 556fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 566fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro private static final String TAG = PhotoSelectionHandler.class.getSimpleName(); 576fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 586fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro private static final int REQUEST_CODE_CAMERA_WITH_DATA = 1001; 596fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro private static final int REQUEST_CODE_PHOTO_PICKED_WITH_DATA = 1002; 60637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee private static final int REQUEST_CROP_PHOTO = 1003; 616fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 62e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus protected final Context mContext; 636fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro private final View mPhotoView; 646fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro private final int mPhotoMode; 656fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro private final int mPhotoPickSize; 66637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee private final Uri mCroppedPhotoUri; 67637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee private final Uri mTempPhotoUri; 68851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu private final RawContactDeltaList mState; 696fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro private final boolean mIsDirectoryContact; 706fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro private ListPopupWindow mPopup; 716fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 726fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro public PhotoSelectionHandler(Context context, View photoView, int photoMode, 73851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu boolean isDirectoryContact, RawContactDeltaList state) { 746fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro mContext = context; 756fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro mPhotoView = photoView; 766fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro mPhotoMode = photoMode; 77637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee mTempPhotoUri = ContactPhotoUtils.generateTempImageUri(context); 78637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee mCroppedPhotoUri = ContactPhotoUtils.generateTempCroppedImageUri(mContext); 796fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro mIsDirectoryContact = isDirectoryContact; 806fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro mState = state; 816fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro mPhotoPickSize = getPhotoPickSize(); 826fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 836fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 846fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro public void destroy() { 85619ac169e45bf547a2cbcd8a9775bb234f45c00fChiao Cheng UiClosables.closeQuietly(mPopup); 866fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 876fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 88e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus public abstract PhotoActionListener getListener(); 896fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 906fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro @Override 916fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro public void onClick(View v) { 929a409d474b2b4f2e4261657f864eeb866fcb8816Daniel Lehmann final PhotoActionListener listener = getListener(); 939a409d474b2b4f2e4261657f864eeb866fcb8816Daniel Lehmann if (listener != null) { 946fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro if (getWritableEntityIndex() != -1) { 956fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro mPopup = PhotoActionPopup.createPopupMenu( 969a409d474b2b4f2e4261657f864eeb866fcb8816Daniel Lehmann mContext, mPhotoView, listener, mPhotoMode); 976fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro mPopup.setOnDismissListener(new OnDismissListener() { 986fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro @Override 996fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro public void onDismiss() { 100e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus listener.onPhotoSelectionDismissed(); 1016fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 1026fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro }); 1036fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro mPopup.show(); 1046fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 1056fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 1066fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 1076fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 1086fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro /** 1096fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * Attempts to handle the given activity result. Returns whether this handler was able to 1106fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * process the result successfully. 1116fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * @param requestCode The request code. 1126fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * @param resultCode The result code. 1136fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * @param data The intent that was returned. 1146fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * @return Whether the handler was able to process the result. 1156fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro */ 1166fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro public boolean handlePhotoActivityResult(int requestCode, int resultCode, Intent data) { 1179a409d474b2b4f2e4261657f864eeb866fcb8816Daniel Lehmann final PhotoActionListener listener = getListener(); 1186fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro if (resultCode == Activity.RESULT_OK) { 1196fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro switch (requestCode) { 120637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee // Cropped photo was returned 121637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee case REQUEST_CROP_PHOTO: { 122637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee final Uri uri; 123637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee if (data != null && data.getData() != null) { 124637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee uri = data.getData(); 125637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee } else { 126637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee uri = mCroppedPhotoUri; 127637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee } 128637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee 129637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee try { 130637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee // delete the original temporary photo if it exists 131637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee mContext.getContentResolver().delete(mTempPhotoUri, null, null); 132637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee listener.onPhotoSelected(uri); 133637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee return true; 134637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee } catch (FileNotFoundException e) { 135637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee return false; 136637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee } 1376fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 138637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee 139637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee // Photo was successfully taken or selected from gallery, now crop it. 140637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee case REQUEST_CODE_PHOTO_PICKED_WITH_DATA: 141637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee case REQUEST_CODE_CAMERA_WITH_DATA: 142637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee final Uri uri; 143637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee boolean isWritable = false; 144637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee if (data != null && data.getData() != null) { 145637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee uri = data.getData(); 146637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee } else { 147637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee uri = listener.getCurrentPhotoUri(); 148637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee isWritable = true; 149637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee } 150637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee final Uri toCrop; 151637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee if (isWritable) { 152637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee // Since this uri belongs to our file provider, we know that it is writable 153637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee // by us. This means that we don't have to save it into another temporary 154637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee // location just to be able to crop it. 155637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee toCrop = uri; 156637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee } else { 157637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee toCrop = mTempPhotoUri; 158637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee try { 159637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee ContactPhotoUtils.savePhotoFromUriToUri(mContext, uri, 160637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee toCrop, false); 161637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee } catch (SecurityException e) { 162637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee Log.d(TAG, "Did not have read-access to uri : " + uri); 163637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee return false; 164637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee } 165637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee } 166637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee 167637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee doCropPhoto(toCrop, mCroppedPhotoUri); 1686fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro return true; 1696fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 1706fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 1716fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro return false; 1726fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 1736fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 1746fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro /** 1756fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * Return the index of the first entity in the contact data that belongs to a contact-writable 1766fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * account, or -1 if no such entity exists. 1776fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro */ 1786fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro private int getWritableEntityIndex() { 1796fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro // Directory entries are non-writable. 180e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus if (mIsDirectoryContact) return -1; 181e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus return mState.indexOfFirstWritableRawContact(mContext); 1826fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 1836fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 1846fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro /** 185e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus * Return the raw-contact id of the first entity in the contact data that belongs to a 186e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus * contact-writable account, or -1 if no such entity exists. 187e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus */ 188e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus protected long getWritableEntityId() { 189e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus int index = getWritableEntityIndex(); 190e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus if (index == -1) return -1; 191e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus return mState.get(index).getValues().getId(); 192e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus } 193e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus 194e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus /** 1956fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * Utility method to retrieve the entity delta for attaching the given bitmap to the contact. 1966fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * This will attach the photo to the first contact-writable account that provided data to the 1976fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * contact. It is the caller's responsibility to apply the delta. 1986fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * @return An entity delta list that can be applied to associate the bitmap with the contact, 1996fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * or null if the photo could not be parsed or none of the accounts associated with the 2006fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * contact are writable. 2016fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro */ 202851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu public RawContactDeltaList getDeltaForAttachingPhotoToContact() { 2036fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro // Find the first writable entity. 2046fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro int writableEntityIndex = getWritableEntityIndex(); 2056fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro if (writableEntityIndex != -1) { 2069a409d474b2b4f2e4261657f864eeb866fcb8816Daniel Lehmann // We are guaranteed to have contact data if we have a writable entity index. 207851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu final RawContactDelta delta = mState.get(writableEntityIndex); 2089a409d474b2b4f2e4261657f864eeb866fcb8816Daniel Lehmann 2099a409d474b2b4f2e4261657f864eeb866fcb8816Daniel Lehmann // Need to find the right account so that EntityModifier knows which fields to add 2109a409d474b2b4f2e4261657f864eeb866fcb8816Daniel Lehmann final ContentValues entityValues = delta.getValues().getCompleteValues(); 2119a409d474b2b4f2e4261657f864eeb866fcb8816Daniel Lehmann final String type = entityValues.getAsString(RawContacts.ACCOUNT_TYPE); 2129a409d474b2b4f2e4261657f864eeb866fcb8816Daniel Lehmann final String dataSet = entityValues.getAsString(RawContacts.DATA_SET); 2139a409d474b2b4f2e4261657f864eeb866fcb8816Daniel Lehmann final AccountType accountType = AccountTypeManager.getInstance(mContext).getAccountType( 2149a409d474b2b4f2e4261657f864eeb866fcb8816Daniel Lehmann type, dataSet); 2159a409d474b2b4f2e4261657f864eeb866fcb8816Daniel Lehmann 216851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu final ValuesDelta child = RawContactModifier.ensureKindExists( 2179a409d474b2b4f2e4261657f864eeb866fcb8816Daniel Lehmann delta, accountType, Photo.CONTENT_ITEM_TYPE); 2186fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro child.setFromTemplate(false); 219851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu child.setSuperPrimary(true); 2206fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 2216fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro return mState; 2226fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 2236fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro return null; 2246fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 2256fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 226e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus /** Used by subclasses to delegate to their enclosing Activity or Fragment. */ 227637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee protected abstract void startPhotoActivity(Intent intent, int requestCode, Uri photoUri); 228e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus 2296fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro /** 2306fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * Sends a newly acquired photo to Gallery for cropping 2316fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro */ 232637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee private void doCropPhoto(Uri inputUri, Uri outputUri) { 2336fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro try { 2346fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro // Launch gallery to crop the photo 235637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee final Intent intent = getCropImageIntent(inputUri, outputUri); 236637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee startPhotoActivity(intent, REQUEST_CROP_PHOTO, inputUri); 2376fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } catch (Exception e) { 2386fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro Log.e(TAG, "Cannot crop image", e); 2396fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro Toast.makeText(mContext, R.string.photoPickerNotFoundText, Toast.LENGTH_LONG).show(); 2406fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 2416fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 2426fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 243e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus /** 244e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus * Should initiate an activity to take a photo using the camera. 245e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus * @param photoFile The file path that will be used to store the photo. This is generally 246e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus * what should be returned by 247e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus * {@link PhotoSelectionHandler.PhotoActionListener#getCurrentPhotoFile()}. 248e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus */ 249637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee private void startTakePhotoActivity(Uri photoUri) { 250637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee final Intent intent = getTakePhotoIntent(photoUri); 251637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee startPhotoActivity(intent, REQUEST_CODE_CAMERA_WITH_DATA, photoUri); 2526fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 2536fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 254e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus /** 255e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus * Should initiate an activity pick a photo from the gallery. 256e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus * @param photoFile The temporary file that the cropped image is written to before being 257e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus * stored by the content-provider. 258e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus * {@link PhotoSelectionHandler#handlePhotoActivityResult(int, int, Intent)}. 259e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus */ 260637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee private void startPickFromGalleryActivity(Uri photoUri) { 261637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee final Intent intent = getPhotoPickIntent(photoUri); 262637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee startPhotoActivity(intent, REQUEST_CODE_PHOTO_PICKED_WITH_DATA, photoUri); 263e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus } 264e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus 2656fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro private int getPhotoPickSize() { 2666fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro // Note that this URI is safe to call on the UI thread. 2676fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro Cursor c = mContext.getContentResolver().query(DisplayPhoto.CONTENT_MAX_DIMENSIONS_URI, 2686fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro new String[]{DisplayPhoto.DISPLAY_MAX_DIM}, null, null, null); 2696fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro try { 2706fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro c.moveToFirst(); 2716fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro return c.getInt(0); 2726fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } finally { 2736fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro c.close(); 2746fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 2756fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 2766fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 2776fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro /** 278637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee * Constructs an intent for capturing a photo and storing it in a temporary output uri. 2796fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro */ 280637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee private Intent getTakePhotoIntent(Uri outputUri) { 281637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee final Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE, null); 282637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee ContactPhotoUtils.addPhotoPickerExtras(intent, outputUri); 2836fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro return intent; 2846fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 2856fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 2866fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro /** 287637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee * Constructs an intent for picking a photo from Gallery, and returning the bitmap. 2886fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro */ 289637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee private Intent getPhotoPickIntent(Uri outputUri) { 290637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee final Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null); 291637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee intent.setType("image/*"); 292637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee ContactPhotoUtils.addPhotoPickerExtras(intent, outputUri); 2936fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro return intent; 2946fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 2956fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 2966fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro /** 297637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee * Constructs an intent for image cropping. 2986fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro */ 299637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee private Intent getCropImageIntent(Uri inputUri, Uri outputUri) { 300637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee Intent intent = new Intent("com.android.camera.action.CROP"); 301637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee intent.setDataAndType(inputUri, "image/*"); 302637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee ContactPhotoUtils.addPhotoPickerExtras(intent, outputUri); 303637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee ContactPhotoUtils.addCropExtras(intent, mPhotoPickSize); 3046fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro return intent; 3056fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 3066fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 3076fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro public abstract class PhotoActionListener implements PhotoActionPopup.Listener { 3086fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro @Override 3096fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro public void onUseAsPrimaryChosen() { 3106fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro // No default implementation. 3116fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 3126fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 3136fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro @Override 3146fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro public void onRemovePictureChosen() { 3156fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro // No default implementation. 3166fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 3176fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 3186fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro @Override 3196fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro public void onTakePhotoChosen() { 3206fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro try { 3216fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro // Launch camera to take photo for selected contact 322637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee startTakePhotoActivity(mTempPhotoUri); 3236fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } catch (ActivityNotFoundException e) { 324ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus Toast.makeText( 325ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus mContext, R.string.photoPickerNotFoundText, Toast.LENGTH_LONG).show(); 3266fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 3276fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 3286fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 3296fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro @Override 3306fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro public void onPickFromGalleryChosen() { 3316fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro try { 3326fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro // Launch picker to choose photo for selected contact 333637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee startPickFromGalleryActivity(mTempPhotoUri); 3346fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } catch (ActivityNotFoundException e) { 335ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus Toast.makeText( 336ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus mContext, R.string.photoPickerNotFoundText, Toast.LENGTH_LONG).show(); 3376fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 3386fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 3396fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 3406fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro /** 3416fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * Called when the user has completed selection of a photo. 342637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee * @throws FileNotFoundException 3436fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro */ 344637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee public abstract void onPhotoSelected(Uri uri) throws FileNotFoundException; 3456fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 3466fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro /** 3476fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * Gets the current photo file that is being interacted with. It is the activity or 3486fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * fragment's responsibility to maintain this in saved state, since this handler instance 3496fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * will not survive rotation. 3506fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro */ 351637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee public abstract Uri getCurrentPhotoUri(); 3526fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 3536fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro /** 3546fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * Called when the photo selection dialog is dismissed. 3556fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro */ 3566fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro public abstract void onPhotoSelectionDismissed(); 3576fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 3586fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro} 359