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