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.graphics.Bitmap; 26e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargusimport android.graphics.BitmapFactory; 276fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.media.MediaScannerConnection; 286fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.net.Uri; 296fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.provider.ContactsContract.CommonDataKinds.Photo; 306fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.provider.ContactsContract.DisplayPhoto; 319a409d474b2b4f2e4261657f864eeb866fcb8816Daniel Lehmannimport android.provider.ContactsContract.RawContacts; 326fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.provider.MediaStore; 336fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.util.Log; 346fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.view.View; 356fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.view.View.OnClickListener; 366fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.widget.ListPopupWindow; 376fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.widget.PopupWindow.OnDismissListener; 386fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.widget.Toast; 3961414c2c4627ab588cc0f54eae5e0c5591f7d8d9Chiao Cheng 4061414c2c4627ab588cc0f54eae5e0c5591f7d8d9Chiao Chengimport com.android.contacts.R; 4161414c2c4627ab588cc0f54eae5e0c5591f7d8d9Chiao Chengimport com.android.contacts.editor.PhotoActionPopup; 4261414c2c4627ab588cc0f54eae5e0c5591f7d8d9Chiao Chengimport com.android.contacts.model.AccountTypeManager; 43851222a96b5d68602fb361ea3527101e893f67e3Maurice Chuimport com.android.contacts.model.RawContactModifier; 44851222a96b5d68602fb361ea3527101e893f67e3Maurice Chuimport com.android.contacts.model.RawContactDelta; 45851222a96b5d68602fb361ea3527101e893f67e3Maurice Chuimport com.android.contacts.model.RawContactDelta.ValuesDelta; 46851222a96b5d68602fb361ea3527101e893f67e3Maurice Chuimport com.android.contacts.model.account.AccountType; 47851222a96b5d68602fb361ea3527101e893f67e3Maurice Chuimport com.android.contacts.model.RawContactDeltaList; 4861414c2c4627ab588cc0f54eae5e0c5591f7d8d9Chiao Chengimport com.android.contacts.util.ContactPhotoUtils; 4961414c2c4627ab588cc0f54eae5e0c5591f7d8d9Chiao Cheng 506fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport java.io.File; 516fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 526fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro/** 536fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * Handles displaying a photo selection popup for a given photo view and dealing with the results 546fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * that come back. 556fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro */ 56e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Garguspublic abstract class PhotoSelectionHandler implements OnClickListener { 576fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 586fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro private static final String TAG = PhotoSelectionHandler.class.getSimpleName(); 596fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 606fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro private static final int REQUEST_CODE_CAMERA_WITH_DATA = 1001; 616fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro private static final int REQUEST_CODE_PHOTO_PICKED_WITH_DATA = 1002; 626fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 63e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus protected final Context mContext; 646fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro private final View mPhotoView; 656fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro private final int mPhotoMode; 666fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro private final int mPhotoPickSize; 67851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu private final RawContactDeltaList mState; 686fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro private final boolean mIsDirectoryContact; 696fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro private ListPopupWindow mPopup; 706fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 716fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro public PhotoSelectionHandler(Context context, View photoView, int photoMode, 72851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu boolean isDirectoryContact, RawContactDeltaList state) { 736fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro mContext = context; 746fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro mPhotoView = photoView; 756fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro mPhotoMode = photoMode; 766fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro mIsDirectoryContact = isDirectoryContact; 776fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro mState = state; 786fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro mPhotoPickSize = getPhotoPickSize(); 796fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 806fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 816fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro public void destroy() { 826fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro if (mPopup != null) { 836fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro mPopup.dismiss(); 846fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 856fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 866fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 87e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus public abstract PhotoActionListener getListener(); 886fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 896fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro @Override 906fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro public void onClick(View v) { 919a409d474b2b4f2e4261657f864eeb866fcb8816Daniel Lehmann final PhotoActionListener listener = getListener(); 929a409d474b2b4f2e4261657f864eeb866fcb8816Daniel Lehmann if (listener != null) { 936fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro if (getWritableEntityIndex() != -1) { 946fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro mPopup = PhotoActionPopup.createPopupMenu( 959a409d474b2b4f2e4261657f864eeb866fcb8816Daniel Lehmann mContext, mPhotoView, listener, mPhotoMode); 966fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro mPopup.setOnDismissListener(new OnDismissListener() { 976fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro @Override 986fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro public void onDismiss() { 99e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus listener.onPhotoSelectionDismissed(); 1006fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 1016fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro }); 1026fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro mPopup.show(); 1036fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 1046fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 1056fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 1066fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 1076fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro /** 1086fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * Attempts to handle the given activity result. Returns whether this handler was able to 1096fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * process the result successfully. 1106fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * @param requestCode The request code. 1116fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * @param resultCode The result code. 1126fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * @param data The intent that was returned. 1136fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * @return Whether the handler was able to process the result. 1146fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro */ 1156fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro public boolean handlePhotoActivityResult(int requestCode, int resultCode, Intent data) { 1169a409d474b2b4f2e4261657f864eeb866fcb8816Daniel Lehmann final PhotoActionListener listener = getListener(); 1176fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro if (resultCode == Activity.RESULT_OK) { 1186fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro switch (requestCode) { 119ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus // Photo was chosen (either new or existing from gallery), and cropped. 1206fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro case REQUEST_CODE_PHOTO_PICKED_WITH_DATA: { 121ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus final String path = ContactPhotoUtils.pathForCroppedPhoto( 122ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus mContext, listener.getCurrentPhotoFile()); 123ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus Bitmap bitmap = BitmapFactory.decodeFile(path); 1249a409d474b2b4f2e4261657f864eeb866fcb8816Daniel Lehmann listener.onPhotoSelected(bitmap); 1256fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro return true; 1266fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 127ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus // Photo was successfully taken, now crop it. 1286fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro case REQUEST_CODE_CAMERA_WITH_DATA: { 1299a409d474b2b4f2e4261657f864eeb866fcb8816Daniel Lehmann doCropPhoto(listener.getCurrentPhotoFile()); 1306fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro return true; 1316fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 1326fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 1336fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 1346fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro return false; 1356fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 1366fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 1376fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro /** 1386fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * Return the index of the first entity in the contact data that belongs to a contact-writable 1396fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * account, or -1 if no such entity exists. 1406fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro */ 1416fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro private int getWritableEntityIndex() { 1426fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro // Directory entries are non-writable. 143e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus if (mIsDirectoryContact) return -1; 144e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus return mState.indexOfFirstWritableRawContact(mContext); 1456fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 1466fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 1476fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro /** 148e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus * Return the raw-contact id of the first entity in the contact data that belongs to a 149e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus * contact-writable account, or -1 if no such entity exists. 150e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus */ 151e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus protected long getWritableEntityId() { 152e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus int index = getWritableEntityIndex(); 153e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus if (index == -1) return -1; 154e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus return mState.get(index).getValues().getId(); 155e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus } 156e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus 157e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus /** 1586fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * Utility method to retrieve the entity delta for attaching the given bitmap to the contact. 1596fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * This will attach the photo to the first contact-writable account that provided data to the 1606fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * contact. It is the caller's responsibility to apply the delta. 1616fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * @return An entity delta list that can be applied to associate the bitmap with the contact, 1626fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * or null if the photo could not be parsed or none of the accounts associated with the 1636fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * contact are writable. 1646fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro */ 165851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu public RawContactDeltaList getDeltaForAttachingPhotoToContact() { 1666fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro // Find the first writable entity. 1676fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro int writableEntityIndex = getWritableEntityIndex(); 1686fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro if (writableEntityIndex != -1) { 1699a409d474b2b4f2e4261657f864eeb866fcb8816Daniel Lehmann // We are guaranteed to have contact data if we have a writable entity index. 170851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu final RawContactDelta delta = mState.get(writableEntityIndex); 1719a409d474b2b4f2e4261657f864eeb866fcb8816Daniel Lehmann 1729a409d474b2b4f2e4261657f864eeb866fcb8816Daniel Lehmann // Need to find the right account so that EntityModifier knows which fields to add 1739a409d474b2b4f2e4261657f864eeb866fcb8816Daniel Lehmann final ContentValues entityValues = delta.getValues().getCompleteValues(); 1749a409d474b2b4f2e4261657f864eeb866fcb8816Daniel Lehmann final String type = entityValues.getAsString(RawContacts.ACCOUNT_TYPE); 1759a409d474b2b4f2e4261657f864eeb866fcb8816Daniel Lehmann final String dataSet = entityValues.getAsString(RawContacts.DATA_SET); 1769a409d474b2b4f2e4261657f864eeb866fcb8816Daniel Lehmann final AccountType accountType = AccountTypeManager.getInstance(mContext).getAccountType( 1779a409d474b2b4f2e4261657f864eeb866fcb8816Daniel Lehmann type, dataSet); 1789a409d474b2b4f2e4261657f864eeb866fcb8816Daniel Lehmann 179851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu final ValuesDelta child = RawContactModifier.ensureKindExists( 1809a409d474b2b4f2e4261657f864eeb866fcb8816Daniel Lehmann delta, accountType, Photo.CONTENT_ITEM_TYPE); 1816fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro child.setFromTemplate(false); 182851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu child.setSuperPrimary(true); 1836fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 1846fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro return mState; 1856fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 1866fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro return null; 1876fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 1886fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 189e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus /** Used by subclasses to delegate to their enclosing Activity or Fragment. */ 190ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus protected abstract void startPhotoActivity(Intent intent, int requestCode, String photoFile); 191e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus 1926fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro /** 1936fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * Sends a newly acquired photo to Gallery for cropping 1946fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro */ 195ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus private void doCropPhoto(String fileName) { 1966fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro try { 197ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus // Obtain the absolute paths for the newly-taken photo, and the destination 198ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus // for the soon-to-be-cropped photo. 199ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus final String newPath = ContactPhotoUtils.pathForNewCameraPhoto(fileName); 200ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus final String croppedPath = ContactPhotoUtils.pathForCroppedPhoto(mContext, fileName); 201ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus 2026fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro // Add the image to the media store 2036fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro MediaScannerConnection.scanFile( 2046fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro mContext, 205ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus new String[] { newPath }, 2066fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro new String[] { null }, 2076fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro null); 2086fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 2096fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro // Launch gallery to crop the photo 210ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus final Intent intent = getCropImageIntent(newPath, croppedPath); 211ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus startPhotoActivity(intent, REQUEST_CODE_PHOTO_PICKED_WITH_DATA, fileName); 2126fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } catch (Exception e) { 2136fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro Log.e(TAG, "Cannot crop image", e); 2146fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro Toast.makeText(mContext, R.string.photoPickerNotFoundText, Toast.LENGTH_LONG).show(); 2156fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 2166fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 2176fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 218e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus /** 219e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus * Should initiate an activity to take a photo using the camera. 220e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus * @param photoFile The file path that will be used to store the photo. This is generally 221e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus * what should be returned by 222e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus * {@link PhotoSelectionHandler.PhotoActionListener#getCurrentPhotoFile()}. 223e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus */ 224ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus private void startTakePhotoActivity(String photoFile) { 225e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus final Intent intent = getTakePhotoIntent(photoFile); 226e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus startPhotoActivity(intent, REQUEST_CODE_CAMERA_WITH_DATA, photoFile); 2276fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 2286fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 229e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus /** 230e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus * Should initiate an activity pick a photo from the gallery. 231e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus * @param photoFile The temporary file that the cropped image is written to before being 232e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus * stored by the content-provider. 233e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus * {@link PhotoSelectionHandler#handlePhotoActivityResult(int, int, Intent)}. 234e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus */ 235ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus private void startPickFromGalleryActivity(String photoFile) { 236e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus final Intent intent = getPhotoPickIntent(photoFile); 237e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus startPhotoActivity(intent, REQUEST_CODE_PHOTO_PICKED_WITH_DATA, photoFile); 238e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus } 239e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus 2406fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro private int getPhotoPickSize() { 2416fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro // Note that this URI is safe to call on the UI thread. 2426fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro Cursor c = mContext.getContentResolver().query(DisplayPhoto.CONTENT_MAX_DIMENSIONS_URI, 2436fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro new String[]{DisplayPhoto.DISPLAY_MAX_DIM}, null, null, null); 2446fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro try { 2456fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro c.moveToFirst(); 2466fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro return c.getInt(0); 2476fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } finally { 2486fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro c.close(); 2496fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 2506fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 2516fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 2526fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro /** 2536fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * Constructs an intent for picking a photo from Gallery, cropping it and returning the bitmap. 2546fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro */ 255ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus private Intent getPhotoPickIntent(String photoFile) { 256ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus final String croppedPhotoPath = ContactPhotoUtils.pathForCroppedPhoto(mContext, photoFile); 257ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus final Uri croppedPhotoUri = Uri.fromFile(new File(croppedPhotoPath)); 258ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus final Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null); 2596fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro intent.setType("image/*"); 26061414c2c4627ab588cc0f54eae5e0c5591f7d8d9Chiao Cheng ContactPhotoUtils.addGalleryIntentExtras(intent, croppedPhotoUri, mPhotoPickSize); 2616fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro return intent; 2626fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 2636fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 2646fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro /** 2656fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * Constructs an intent for image cropping. 2666fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro */ 267ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus private Intent getCropImageIntent(String inputPhotoPath, String croppedPhotoPath) { 268ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus final Uri inputPhotoUri = Uri.fromFile(new File(inputPhotoPath)); 269ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus final Uri croppedPhotoUri = Uri.fromFile(new File(croppedPhotoPath)); 2706fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro Intent intent = new Intent("com.android.camera.action.CROP"); 271ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus intent.setDataAndType(inputPhotoUri, "image/*"); 27261414c2c4627ab588cc0f54eae5e0c5591f7d8d9Chiao Cheng ContactPhotoUtils.addGalleryIntentExtras(intent, croppedPhotoUri, mPhotoPickSize); 2736fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro return intent; 2746fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 2756fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 2766fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro /** 2776fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * Constructs an intent for capturing a photo and storing it in a temporary file. 2786fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro */ 279ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus private static Intent getTakePhotoIntent(String fileName) { 2806fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE, null); 281ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus final String newPhotoPath = ContactPhotoUtils.pathForNewCameraPhoto(fileName); 282ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(newPhotoPath))); 2836fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro return intent; 2846fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 2856fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 2866fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro public abstract class PhotoActionListener implements PhotoActionPopup.Listener { 2876fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro @Override 2886fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro public void onUseAsPrimaryChosen() { 2896fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro // No default implementation. 2906fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 2916fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 2926fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro @Override 2936fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro public void onRemovePictureChosen() { 2946fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro // No default implementation. 2956fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 2966fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 2976fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro @Override 2986fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro public void onTakePhotoChosen() { 2996fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro try { 3006fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro // Launch camera to take photo for selected contact 301ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus startTakePhotoActivity(ContactPhotoUtils.generateTempPhotoFileName()); 3026fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } catch (ActivityNotFoundException e) { 303ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus Toast.makeText( 304ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus mContext, R.string.photoPickerNotFoundText, Toast.LENGTH_LONG).show(); 3056fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 3066fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 3076fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 3086fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro @Override 3096fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro public void onPickFromGalleryChosen() { 3106fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro try { 3116fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro // Launch picker to choose photo for selected contact 312ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus startPickFromGalleryActivity(ContactPhotoUtils.generateTempPhotoFileName()); 3136fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } catch (ActivityNotFoundException e) { 314ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus Toast.makeText( 315ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus mContext, R.string.photoPickerNotFoundText, Toast.LENGTH_LONG).show(); 3166fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 3176fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 3186fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 3196fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro /** 3206fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * Called when the user has completed selection of a photo. 3216fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * @param bitmap The selected and cropped photo. 3226fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro */ 3236fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro public abstract void onPhotoSelected(Bitmap bitmap); 3246fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 3256fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro /** 3266fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * Gets the current photo file that is being interacted with. It is the activity or 3276fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * fragment's responsibility to maintain this in saved state, since this handler instance 3286fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * will not survive rotation. 3296fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro */ 330ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus public abstract String getCurrentPhotoFile(); 3316fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 3326fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro /** 3336fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * Called when the photo selection dialog is dismissed. 3346fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro */ 3356fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro public abstract void onPhotoSelectionDismissed(); 3366fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 3376fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro} 338