PhotoSelectionHandler.java revision e5d3f897689c8ba0f275c7679c72eacb190ae9b8
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 com.android.contacts.R;
206fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport com.android.contacts.editor.PhotoActionPopup;
216fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport com.android.contacts.model.AccountType;
226fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport com.android.contacts.model.EntityDelta;
236fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport com.android.contacts.model.EntityDelta.ValuesDelta;
246fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport com.android.contacts.model.EntityDeltaList;
256fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport com.android.contacts.model.EntityModifier;
26e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargusimport com.android.contacts.util.ContactPhotoUtils;
276fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro
286fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.app.Activity;
296fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.content.ActivityNotFoundException;
306fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.content.Context;
316fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.content.Intent;
326fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.database.Cursor;
336fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.graphics.Bitmap;
34e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargusimport android.graphics.BitmapFactory;
356fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.media.MediaScannerConnection;
366fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.net.Uri;
376fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.provider.ContactsContract.CommonDataKinds.Photo;
386fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.provider.ContactsContract.DisplayPhoto;
396fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.provider.MediaStore;
406fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.util.Log;
416fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.view.View;
426fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.view.View.OnClickListener;
436fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.widget.ListPopupWindow;
446fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.widget.PopupWindow.OnDismissListener;
456fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.widget.Toast;
466fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport java.io.File;
476fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro
486fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro/**
496fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * Handles displaying a photo selection popup for a given photo view and dealing with the results
506fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * that come back.
516fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro */
52e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Garguspublic abstract class PhotoSelectionHandler implements OnClickListener {
536fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro
546fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro    private static final String TAG = PhotoSelectionHandler.class.getSimpleName();
556fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro
566fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro    private static final int REQUEST_CODE_CAMERA_WITH_DATA = 1001;
576fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro    private static final int REQUEST_CODE_PHOTO_PICKED_WITH_DATA = 1002;
586fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro
59e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus    protected final Context mContext;
606fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro    private final View mPhotoView;
616fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro    private final int mPhotoMode;
626fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro    private final int mPhotoPickSize;
636fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro    private final EntityDeltaList mState;
646fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro    private final boolean mIsDirectoryContact;
656fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro    private ListPopupWindow mPopup;
666fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro    private AccountType mWritableAccount;
676fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro    private PhotoActionListener mListener;
686fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro
696fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro    public PhotoSelectionHandler(Context context, View photoView, int photoMode,
706fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro            boolean isDirectoryContact, EntityDeltaList state) {
716fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        mContext = context;
726fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        mPhotoView = photoView;
736fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        mPhotoMode = photoMode;
746fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        mIsDirectoryContact = isDirectoryContact;
756fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        mState = state;
766fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        mPhotoPickSize = getPhotoPickSize();
77e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus
78e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus        // NOTE: subclasses should call setListener()
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) {
916fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        if (mListener != null) {
926fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro            if (getWritableEntityIndex() != -1) {
936fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro                mPopup = PhotoActionPopup.createPopupMenu(
946fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro                        mContext, mPhotoView, mListener, mPhotoMode);
95e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus                final PhotoActionListener listener = mListener; // a bit more bulletproof
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) {
1166fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        if (resultCode == Activity.RESULT_OK) {
1176fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro            switch (requestCode) {
1186fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro                case REQUEST_CODE_PHOTO_PICKED_WITH_DATA: {
119e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus                    Bitmap bitmap = BitmapFactory.decodeFile(
120e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus                            mListener.getCurrentPhotoFile().getAbsolutePath());
1216fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro                    mListener.onPhotoSelected(bitmap);
1226fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro                    return true;
1236fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro                }
1246fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro                case REQUEST_CODE_CAMERA_WITH_DATA: {
1256fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro                    doCropPhoto(mListener.getCurrentPhotoFile());
1266fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro                    return true;
1276fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro                }
1286fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro            }
1296fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        }
1306fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        return false;
1316fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro    }
1326fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro
1336fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro    /**
1346fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro     * Return the index of the first entity in the contact data that belongs to a contact-writable
1356fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro     * account, or -1 if no such entity exists.
1366fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro     */
1376fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro    private int getWritableEntityIndex() {
1386fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        // Directory entries are non-writable.
139e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus        if (mIsDirectoryContact) return -1;
140e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus        return mState.indexOfFirstWritableRawContact(mContext);
1416fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro    }
1426fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro
1436fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro    /**
144e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus     * Return the raw-contact id of the first entity in the contact data that belongs to a
145e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus     * contact-writable account, or -1 if no such entity exists.
146e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus     */
147e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus    protected long getWritableEntityId() {
148e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus        int index = getWritableEntityIndex();
149e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus        if (index == -1) return -1;
150e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus        return mState.get(index).getValues().getId();
151e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus    }
152e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus
153e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus    /**
1546fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro     * Utility method to retrieve the entity delta for attaching the given bitmap to the contact.
1556fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro     * This will attach the photo to the first contact-writable account that provided data to the
1566fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro     * contact.  It is the caller's responsibility to apply the delta.
1576fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro     * @return An entity delta list that can be applied to associate the bitmap with the contact,
1586fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro     *     or null if the photo could not be parsed or none of the accounts associated with the
1596fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro     *     contact are writable.
1606fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro     */
161e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus    public EntityDeltaList getDeltaForAttachingPhotoToContact() {
1626fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        // Find the first writable entity.
1636fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        int writableEntityIndex = getWritableEntityIndex();
1646fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        if (writableEntityIndex != -1) {
1656fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro            // Note - guaranteed to have contact data if we have a writable entity index.
1666fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro            EntityDelta delta = mState.get(writableEntityIndex);
1676fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro            ValuesDelta child = EntityModifier.ensureKindExists(
1686fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro                    delta, mWritableAccount, Photo.CONTENT_ITEM_TYPE);
1696fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro            child.setFromTemplate(false);
1706fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro            child.put(Photo.IS_SUPER_PRIMARY, 1);
1716fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro
1726fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro            return mState;
1736fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        }
1746fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        return null;
1756fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro    }
1766fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro
177e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus    /** Used by subclasses to delegate to their enclosing Activity or Fragment. */
178e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus    protected abstract void startPhotoActivity(Intent intent, int requestCode, File photoFile);
179e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus
1806fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro    /**
1816fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro     * Sends a newly acquired photo to Gallery for cropping
1826fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro     */
1836fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro    private void doCropPhoto(File f) {
1846fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        try {
1856fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro            // Add the image to the media store
1866fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro            MediaScannerConnection.scanFile(
1876fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro                    mContext,
1886fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro                    new String[] { f.getAbsolutePath() },
1896fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro                    new String[] { null },
1906fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro                    null);
1916fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro
1926fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro            // Launch gallery to crop the photo
193e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus            final Intent intent = getCropImageIntent(f);
194e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus            startPhotoActivity(intent, REQUEST_CODE_PHOTO_PICKED_WITH_DATA, f);
1956fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        } catch (Exception e) {
1966fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro            Log.e(TAG, "Cannot crop image", e);
1976fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro            Toast.makeText(mContext, R.string.photoPickerNotFoundText, Toast.LENGTH_LONG).show();
1986fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        }
1996fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro    }
2006fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro
201e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus    /**
202e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus     * Should initiate an activity to take a photo using the camera.
203e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus     * @param photoFile The file path that will be used to store the photo.  This is generally
204e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus     *     what should be returned by
205e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus     *     {@link PhotoSelectionHandler.PhotoActionListener#getCurrentPhotoFile()}.
206e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus     */
207e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus    private void startTakePhotoActivity(File photoFile) {
208e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus        final Intent intent = getTakePhotoIntent(photoFile);
209e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus        startPhotoActivity(intent, REQUEST_CODE_CAMERA_WITH_DATA, photoFile);
2106fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro    }
2116fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro
212e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus    /**
213e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus     * Should initiate an activity pick a photo from the gallery.
214e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus     * @param photoFile The temporary file that the cropped image is written to before being
215e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus     *     stored by the content-provider.
216e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus     *     {@link PhotoSelectionHandler#handlePhotoActivityResult(int, int, Intent)}.
217e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus     */
218e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus    private void startPickFromGalleryActivity(File photoFile) {
219e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus        final Intent intent = getPhotoPickIntent(photoFile);
220e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus        startPhotoActivity(intent, REQUEST_CODE_PHOTO_PICKED_WITH_DATA, photoFile);
221e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus    }
222e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus
2236fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro    private int getPhotoPickSize() {
2246fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        // Note that this URI is safe to call on the UI thread.
2256fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        Cursor c = mContext.getContentResolver().query(DisplayPhoto.CONTENT_MAX_DIMENSIONS_URI,
2266fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro                new String[]{DisplayPhoto.DISPLAY_MAX_DIM}, null, null, null);
2276fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        try {
2286fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro            c.moveToFirst();
2296fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro            return c.getInt(0);
2306fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        } finally {
2316fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro            c.close();
2326fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        }
2336fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro    }
2346fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro
2356fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro    /**
2366fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro     * Constructs an intent for picking a photo from Gallery, cropping it and returning the bitmap.
2376fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro     */
238e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus    private Intent getPhotoPickIntent(File photoFile) {
239e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus        Uri photoUri = Uri.fromFile(photoFile);
2406fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);
2416fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        intent.setType("image/*");
2426fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        intent.putExtra("crop", "true");
2436fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        intent.putExtra("aspectX", 1);
2446fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        intent.putExtra("aspectY", 1);
2456fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        intent.putExtra("outputX", mPhotoPickSize);
2466fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        intent.putExtra("outputY", mPhotoPickSize);
247e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus        intent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);
2486fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        return intent;
2496fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro    }
2506fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro
2516fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro    /**
2526fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro     * Constructs an intent for image cropping.
2536fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro     */
254e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus    private Intent getCropImageIntent(File photoFile) {
255e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus        Uri photoUri = Uri.fromFile(photoFile);
2566fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        Intent intent = new Intent("com.android.camera.action.CROP");
2576fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        intent.setDataAndType(photoUri, "image/*");
2586fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        intent.putExtra("crop", "true");
2596fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        intent.putExtra("aspectX", 1);
2606fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        intent.putExtra("aspectY", 1);
2616fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        intent.putExtra("outputX", mPhotoPickSize);
2626fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        intent.putExtra("outputY", mPhotoPickSize);
263e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus        intent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);
2646fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        return intent;
2656fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro    }
2666fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro
2676fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro    /**
2686fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro     * Constructs an intent for capturing a photo and storing it in a temporary file.
2696fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro     */
2706fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro    public static Intent getTakePhotoIntent(File f) {
2716fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE, null);
2726fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(f));
2736fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        return intent;
2746fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro    }
2756fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro
2766fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro    public abstract class PhotoActionListener implements PhotoActionPopup.Listener {
2776fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        @Override
2786fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        public void onUseAsPrimaryChosen() {
2796fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro            // No default implementation.
2806fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        }
2816fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro
2826fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        @Override
2836fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        public void onRemovePictureChosen() {
2846fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro            // No default implementation.
2856fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        }
2866fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro
2876fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        @Override
2886fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        public void onTakePhotoChosen() {
2896fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro            try {
2906fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro                // Launch camera to take photo for selected contact
291e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus                startTakePhotoActivity(ContactPhotoUtils.generateTempPhotoFile());
2926fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro            } catch (ActivityNotFoundException e) {
2936fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro                Toast.makeText(mContext, R.string.photoPickerNotFoundText,
2946fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro                        Toast.LENGTH_LONG).show();
2956fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro            }
2966fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        }
2976fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro
2986fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        @Override
2996fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        public void onPickFromGalleryChosen() {
3006fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro            try {
3016fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro                // Launch picker to choose photo for selected contact
302e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus                startPickFromGalleryActivity(ContactPhotoUtils.generateTempPhotoFile());
3036fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro            } catch (ActivityNotFoundException e) {
3046fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro                Toast.makeText(mContext, R.string.photoPickerNotFoundText,
3056fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro                        Toast.LENGTH_LONG).show();
3066fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro            }
3076fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        }
3086fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro
3096fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        /**
3106fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro         * Called when the user has completed selection of a photo.
3116fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro         * @param bitmap The selected and cropped photo.
3126fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro         */
3136fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        public abstract void onPhotoSelected(Bitmap bitmap);
3146fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro
3156fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        /**
3166fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro         * Gets the current photo file that is being interacted with.  It is the activity or
3176fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro         * fragment's responsibility to maintain this in saved state, since this handler instance
3186fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro         * will not survive rotation.
3196fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro         */
3206fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        public abstract File getCurrentPhotoFile();
3216fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro
3226fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        /**
3236fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro         * Called when the photo selection dialog is dismissed.
3246fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro         */
3256fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        public abstract void onPhotoSelectionDismissed();
3266fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro    }
3276fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro}
328