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