Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2011 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | package com.android.contacts.detail; |
| 18 | |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 19 | import android.app.Activity; |
| 20 | import android.content.ActivityNotFoundException; |
Daniel Lehmann | 9a409d4 | 2012-04-24 11:29:51 -0700 | [diff] [blame] | 21 | import android.content.ContentValues; |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 22 | import android.content.Context; |
| 23 | import android.content.Intent; |
Brian Attwell | 5234be9 | 2015-05-13 19:26:43 -0700 | [diff] [blame] | 24 | import android.content.pm.PackageManager; |
| 25 | import android.content.pm.ResolveInfo; |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 26 | import android.database.Cursor; |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 27 | import android.net.Uri; |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 28 | import android.provider.ContactsContract.CommonDataKinds.Photo; |
| 29 | import android.provider.ContactsContract.DisplayPhoto; |
Daniel Lehmann | 9a409d4 | 2012-04-24 11:29:51 -0700 | [diff] [blame] | 30 | import android.provider.ContactsContract.RawContacts; |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 31 | import android.provider.MediaStore; |
| 32 | import android.util.Log; |
| 33 | import android.view.View; |
| 34 | import android.view.View.OnClickListener; |
| 35 | import android.widget.ListPopupWindow; |
| 36 | import android.widget.PopupWindow.OnDismissListener; |
| 37 | import android.widget.Toast; |
Chiao Cheng | 61414c2 | 2012-06-18 16:59:06 -0700 | [diff] [blame] | 38 | |
| 39 | import com.android.contacts.R; |
| 40 | import com.android.contacts.editor.PhotoActionPopup; |
Chiao Cheng | 0d5588d | 2012-11-26 15:34:14 -0800 | [diff] [blame] | 41 | import com.android.contacts.common.model.AccountTypeManager; |
Yorke Lee | cd321f6 | 2013-10-28 15:20:15 -0700 | [diff] [blame] | 42 | import com.android.contacts.common.model.RawContactModifier; |
| 43 | import com.android.contacts.common.model.RawContactDelta; |
Chiao Cheng | 738ff86 | 2012-11-30 12:06:03 -0800 | [diff] [blame] | 44 | import com.android.contacts.common.model.ValuesDelta; |
Chiao Cheng | 428f008 | 2012-11-13 18:38:56 -0800 | [diff] [blame] | 45 | import com.android.contacts.common.model.account.AccountType; |
Yorke Lee | cd321f6 | 2013-10-28 15:20:15 -0700 | [diff] [blame] | 46 | import com.android.contacts.common.model.RawContactDeltaList; |
Chiao Cheng | 61414c2 | 2012-06-18 16:59:06 -0700 | [diff] [blame] | 47 | import com.android.contacts.util.ContactPhotoUtils; |
Chiao Cheng | 619ac16 | 2012-12-20 14:29:03 -0800 | [diff] [blame] | 48 | import com.android.contacts.util.UiClosables; |
Chiao Cheng | 61414c2 | 2012-06-18 16:59:06 -0700 | [diff] [blame] | 49 | |
Yorke Lee | 637a38e | 2013-09-14 08:36:33 -0700 | [diff] [blame] | 50 | import java.io.FileNotFoundException; |
Brian Attwell | 5234be9 | 2015-05-13 19:26:43 -0700 | [diff] [blame] | 51 | import java.util.List; |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 52 | |
| 53 | /** |
| 54 | * Handles displaying a photo selection popup for a given photo view and dealing with the results |
| 55 | * that come back. |
| 56 | */ |
Josh Gargus | e5d3f89 | 2012-04-11 11:56:15 -0700 | [diff] [blame] | 57 | public abstract class PhotoSelectionHandler implements OnClickListener { |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 58 | |
| 59 | private static final String TAG = PhotoSelectionHandler.class.getSimpleName(); |
| 60 | |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 61 | private static final int REQUEST_CODE_CAMERA_WITH_DATA = 1001; |
| 62 | private static final int REQUEST_CODE_PHOTO_PICKED_WITH_DATA = 1002; |
Yorke Lee | 637a38e | 2013-09-14 08:36:33 -0700 | [diff] [blame] | 63 | private static final int REQUEST_CROP_PHOTO = 1003; |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 64 | |
Jay Shrauner | b126f4a | 2014-01-18 10:45:01 -0800 | [diff] [blame] | 65 | // Height and width (in pixels) to request for the photo - queried from the provider. |
| 66 | private static int mPhotoDim; |
| 67 | // Default photo dimension to use if unable to query the provider. |
| 68 | private static final int mDefaultPhotoDim = 720; |
| 69 | |
Josh Gargus | e5d3f89 | 2012-04-11 11:56:15 -0700 | [diff] [blame] | 70 | protected final Context mContext; |
Brian Attwell | 7e67082 | 2014-11-07 16:36:30 -0800 | [diff] [blame] | 71 | private final View mChangeAnchorView; |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 72 | private final int mPhotoMode; |
| 73 | private final int mPhotoPickSize; |
Yorke Lee | 637a38e | 2013-09-14 08:36:33 -0700 | [diff] [blame] | 74 | private final Uri mCroppedPhotoUri; |
| 75 | private final Uri mTempPhotoUri; |
Maurice Chu | 851222a | 2012-06-21 11:43:08 -0700 | [diff] [blame] | 76 | private final RawContactDeltaList mState; |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 77 | private final boolean mIsDirectoryContact; |
| 78 | private ListPopupWindow mPopup; |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 79 | |
Brian Attwell | 7e67082 | 2014-11-07 16:36:30 -0800 | [diff] [blame] | 80 | public PhotoSelectionHandler(Context context, View changeAnchorView, int photoMode, |
Maurice Chu | 851222a | 2012-06-21 11:43:08 -0700 | [diff] [blame] | 81 | boolean isDirectoryContact, RawContactDeltaList state) { |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 82 | mContext = context; |
Brian Attwell | 7e67082 | 2014-11-07 16:36:30 -0800 | [diff] [blame] | 83 | mChangeAnchorView = changeAnchorView; |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 84 | mPhotoMode = photoMode; |
Yorke Lee | 637a38e | 2013-09-14 08:36:33 -0700 | [diff] [blame] | 85 | mTempPhotoUri = ContactPhotoUtils.generateTempImageUri(context); |
| 86 | mCroppedPhotoUri = ContactPhotoUtils.generateTempCroppedImageUri(mContext); |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 87 | mIsDirectoryContact = isDirectoryContact; |
| 88 | mState = state; |
| 89 | mPhotoPickSize = getPhotoPickSize(); |
| 90 | } |
| 91 | |
| 92 | public void destroy() { |
Chiao Cheng | 619ac16 | 2012-12-20 14:29:03 -0800 | [diff] [blame] | 93 | UiClosables.closeQuietly(mPopup); |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 94 | } |
| 95 | |
Josh Gargus | e5d3f89 | 2012-04-11 11:56:15 -0700 | [diff] [blame] | 96 | public abstract PhotoActionListener getListener(); |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 97 | |
| 98 | @Override |
| 99 | public void onClick(View v) { |
Daniel Lehmann | 9a409d4 | 2012-04-24 11:29:51 -0700 | [diff] [blame] | 100 | final PhotoActionListener listener = getListener(); |
| 101 | if (listener != null) { |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 102 | if (getWritableEntityIndex() != -1) { |
| 103 | mPopup = PhotoActionPopup.createPopupMenu( |
Brian Attwell | 7e67082 | 2014-11-07 16:36:30 -0800 | [diff] [blame] | 104 | mContext, mChangeAnchorView, listener, mPhotoMode); |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 105 | mPopup.setOnDismissListener(new OnDismissListener() { |
| 106 | @Override |
| 107 | public void onDismiss() { |
Josh Gargus | e5d3f89 | 2012-04-11 11:56:15 -0700 | [diff] [blame] | 108 | listener.onPhotoSelectionDismissed(); |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 109 | } |
| 110 | }); |
| 111 | mPopup.show(); |
| 112 | } |
| 113 | } |
| 114 | } |
| 115 | |
| 116 | /** |
| 117 | * Attempts to handle the given activity result. Returns whether this handler was able to |
| 118 | * process the result successfully. |
| 119 | * @param requestCode The request code. |
| 120 | * @param resultCode The result code. |
| 121 | * @param data The intent that was returned. |
| 122 | * @return Whether the handler was able to process the result. |
| 123 | */ |
| 124 | public boolean handlePhotoActivityResult(int requestCode, int resultCode, Intent data) { |
Daniel Lehmann | 9a409d4 | 2012-04-24 11:29:51 -0700 | [diff] [blame] | 125 | final PhotoActionListener listener = getListener(); |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 126 | if (resultCode == Activity.RESULT_OK) { |
| 127 | switch (requestCode) { |
Yorke Lee | 637a38e | 2013-09-14 08:36:33 -0700 | [diff] [blame] | 128 | // Cropped photo was returned |
| 129 | case REQUEST_CROP_PHOTO: { |
Yorke Lee | 637a38e | 2013-09-14 08:36:33 -0700 | [diff] [blame] | 130 | if (data != null && data.getData() != null) { |
Gary Mai | 84b1654 | 2016-12-15 17:25:56 -0800 | [diff] [blame] | 131 | final Uri croppedUri = data.getData(); |
| 132 | ContactPhotoUtils.savePhotoFromUriToUri(mContext, croppedUri, |
| 133 | mCroppedPhotoUri, /* deleteAfterSave */ false); |
Yorke Lee | 637a38e | 2013-09-14 08:36:33 -0700 | [diff] [blame] | 134 | } |
| 135 | |
| 136 | try { |
| 137 | // delete the original temporary photo if it exists |
| 138 | mContext.getContentResolver().delete(mTempPhotoUri, null, null); |
Gary Mai | 84b1654 | 2016-12-15 17:25:56 -0800 | [diff] [blame] | 139 | listener.onPhotoSelected(mCroppedPhotoUri); |
Yorke Lee | 637a38e | 2013-09-14 08:36:33 -0700 | [diff] [blame] | 140 | return true; |
| 141 | } catch (FileNotFoundException e) { |
| 142 | return false; |
| 143 | } |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 144 | } |
Yorke Lee | 637a38e | 2013-09-14 08:36:33 -0700 | [diff] [blame] | 145 | |
| 146 | // Photo was successfully taken or selected from gallery, now crop it. |
| 147 | case REQUEST_CODE_PHOTO_PICKED_WITH_DATA: |
| 148 | case REQUEST_CODE_CAMERA_WITH_DATA: |
| 149 | final Uri uri; |
| 150 | boolean isWritable = false; |
| 151 | if (data != null && data.getData() != null) { |
| 152 | uri = data.getData(); |
| 153 | } else { |
| 154 | uri = listener.getCurrentPhotoUri(); |
| 155 | isWritable = true; |
| 156 | } |
| 157 | final Uri toCrop; |
| 158 | if (isWritable) { |
| 159 | // Since this uri belongs to our file provider, we know that it is writable |
| 160 | // by us. This means that we don't have to save it into another temporary |
| 161 | // location just to be able to crop it. |
| 162 | toCrop = uri; |
| 163 | } else { |
| 164 | toCrop = mTempPhotoUri; |
| 165 | try { |
Jay Shrauner | a943371 | 2015-02-27 15:47:09 -0800 | [diff] [blame] | 166 | if (!ContactPhotoUtils.savePhotoFromUriToUri(mContext, uri, |
| 167 | toCrop, false)) { |
| 168 | return false; |
| 169 | } |
Yorke Lee | 637a38e | 2013-09-14 08:36:33 -0700 | [diff] [blame] | 170 | } catch (SecurityException e) { |
| 171 | Log.d(TAG, "Did not have read-access to uri : " + uri); |
| 172 | return false; |
| 173 | } |
| 174 | } |
| 175 | |
| 176 | doCropPhoto(toCrop, mCroppedPhotoUri); |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 177 | return true; |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 178 | } |
| 179 | } |
| 180 | return false; |
| 181 | } |
| 182 | |
| 183 | /** |
| 184 | * Return the index of the first entity in the contact data that belongs to a contact-writable |
| 185 | * account, or -1 if no such entity exists. |
| 186 | */ |
| 187 | private int getWritableEntityIndex() { |
| 188 | // Directory entries are non-writable. |
Josh Gargus | e5d3f89 | 2012-04-11 11:56:15 -0700 | [diff] [blame] | 189 | if (mIsDirectoryContact) return -1; |
| 190 | return mState.indexOfFirstWritableRawContact(mContext); |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 191 | } |
| 192 | |
| 193 | /** |
Josh Gargus | e692e01 | 2012-01-18 14:53:11 -0800 | [diff] [blame] | 194 | * Return the raw-contact id of the first entity in the contact data that belongs to a |
| 195 | * contact-writable account, or -1 if no such entity exists. |
| 196 | */ |
| 197 | protected long getWritableEntityId() { |
| 198 | int index = getWritableEntityIndex(); |
| 199 | if (index == -1) return -1; |
| 200 | return mState.get(index).getValues().getId(); |
| 201 | } |
| 202 | |
| 203 | /** |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 204 | * Utility method to retrieve the entity delta for attaching the given bitmap to the contact. |
| 205 | * This will attach the photo to the first contact-writable account that provided data to the |
| 206 | * contact. It is the caller's responsibility to apply the delta. |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 207 | * @return An entity delta list that can be applied to associate the bitmap with the contact, |
| 208 | * or null if the photo could not be parsed or none of the accounts associated with the |
| 209 | * contact are writable. |
| 210 | */ |
Maurice Chu | 851222a | 2012-06-21 11:43:08 -0700 | [diff] [blame] | 211 | public RawContactDeltaList getDeltaForAttachingPhotoToContact() { |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 212 | // Find the first writable entity. |
| 213 | int writableEntityIndex = getWritableEntityIndex(); |
| 214 | if (writableEntityIndex != -1) { |
Daniel Lehmann | 9a409d4 | 2012-04-24 11:29:51 -0700 | [diff] [blame] | 215 | // We are guaranteed to have contact data if we have a writable entity index. |
Maurice Chu | 851222a | 2012-06-21 11:43:08 -0700 | [diff] [blame] | 216 | final RawContactDelta delta = mState.get(writableEntityIndex); |
Daniel Lehmann | 9a409d4 | 2012-04-24 11:29:51 -0700 | [diff] [blame] | 217 | |
| 218 | // Need to find the right account so that EntityModifier knows which fields to add |
| 219 | final ContentValues entityValues = delta.getValues().getCompleteValues(); |
| 220 | final String type = entityValues.getAsString(RawContacts.ACCOUNT_TYPE); |
| 221 | final String dataSet = entityValues.getAsString(RawContacts.DATA_SET); |
| 222 | final AccountType accountType = AccountTypeManager.getInstance(mContext).getAccountType( |
| 223 | type, dataSet); |
| 224 | |
Maurice Chu | 851222a | 2012-06-21 11:43:08 -0700 | [diff] [blame] | 225 | final ValuesDelta child = RawContactModifier.ensureKindExists( |
Daniel Lehmann | 9a409d4 | 2012-04-24 11:29:51 -0700 | [diff] [blame] | 226 | delta, accountType, Photo.CONTENT_ITEM_TYPE); |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 227 | child.setFromTemplate(false); |
Maurice Chu | 851222a | 2012-06-21 11:43:08 -0700 | [diff] [blame] | 228 | child.setSuperPrimary(true); |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 229 | |
| 230 | return mState; |
| 231 | } |
| 232 | return null; |
| 233 | } |
| 234 | |
Josh Gargus | e5d3f89 | 2012-04-11 11:56:15 -0700 | [diff] [blame] | 235 | /** Used by subclasses to delegate to their enclosing Activity or Fragment. */ |
Yorke Lee | 637a38e | 2013-09-14 08:36:33 -0700 | [diff] [blame] | 236 | protected abstract void startPhotoActivity(Intent intent, int requestCode, Uri photoUri); |
Josh Gargus | e5d3f89 | 2012-04-11 11:56:15 -0700 | [diff] [blame] | 237 | |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 238 | /** |
| 239 | * Sends a newly acquired photo to Gallery for cropping |
| 240 | */ |
Yorke Lee | 637a38e | 2013-09-14 08:36:33 -0700 | [diff] [blame] | 241 | private void doCropPhoto(Uri inputUri, Uri outputUri) { |
Brian Attwell | 5234be9 | 2015-05-13 19:26:43 -0700 | [diff] [blame] | 242 | final Intent intent = getCropImageIntent(inputUri, outputUri); |
| 243 | if (!hasIntentHandler(intent)) { |
| 244 | try { |
| 245 | getListener().onPhotoSelected(inputUri); |
| 246 | } catch (FileNotFoundException e) { |
| 247 | Log.e(TAG, "Cannot save uncropped photo", e); |
| 248 | Toast.makeText(mContext, R.string.contactPhotoSavedErrorToast, |
| 249 | Toast.LENGTH_LONG).show(); |
| 250 | } |
| 251 | return; |
| 252 | } |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 253 | try { |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 254 | // Launch gallery to crop the photo |
Yorke Lee | 637a38e | 2013-09-14 08:36:33 -0700 | [diff] [blame] | 255 | startPhotoActivity(intent, REQUEST_CROP_PHOTO, inputUri); |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 256 | } catch (Exception e) { |
| 257 | Log.e(TAG, "Cannot crop image", e); |
| 258 | Toast.makeText(mContext, R.string.photoPickerNotFoundText, Toast.LENGTH_LONG).show(); |
| 259 | } |
| 260 | } |
| 261 | |
Josh Gargus | e5d3f89 | 2012-04-11 11:56:15 -0700 | [diff] [blame] | 262 | /** |
| 263 | * Should initiate an activity to take a photo using the camera. |
| 264 | * @param photoFile The file path that will be used to store the photo. This is generally |
| 265 | * what should be returned by |
| 266 | * {@link PhotoSelectionHandler.PhotoActionListener#getCurrentPhotoFile()}. |
| 267 | */ |
Yorke Lee | 637a38e | 2013-09-14 08:36:33 -0700 | [diff] [blame] | 268 | private void startTakePhotoActivity(Uri photoUri) { |
| 269 | final Intent intent = getTakePhotoIntent(photoUri); |
| 270 | startPhotoActivity(intent, REQUEST_CODE_CAMERA_WITH_DATA, photoUri); |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 271 | } |
| 272 | |
Josh Gargus | e5d3f89 | 2012-04-11 11:56:15 -0700 | [diff] [blame] | 273 | /** |
| 274 | * Should initiate an activity pick a photo from the gallery. |
| 275 | * @param photoFile The temporary file that the cropped image is written to before being |
| 276 | * stored by the content-provider. |
| 277 | * {@link PhotoSelectionHandler#handlePhotoActivityResult(int, int, Intent)}. |
| 278 | */ |
Yorke Lee | 637a38e | 2013-09-14 08:36:33 -0700 | [diff] [blame] | 279 | private void startPickFromGalleryActivity(Uri photoUri) { |
| 280 | final Intent intent = getPhotoPickIntent(photoUri); |
| 281 | startPhotoActivity(intent, REQUEST_CODE_PHOTO_PICKED_WITH_DATA, photoUri); |
Josh Gargus | e692e01 | 2012-01-18 14:53:11 -0800 | [diff] [blame] | 282 | } |
| 283 | |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 284 | private int getPhotoPickSize() { |
Jay Shrauner | b126f4a | 2014-01-18 10:45:01 -0800 | [diff] [blame] | 285 | if (mPhotoDim != 0) { |
| 286 | return mPhotoDim; |
| 287 | } |
| 288 | |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 289 | // Note that this URI is safe to call on the UI thread. |
| 290 | Cursor c = mContext.getContentResolver().query(DisplayPhoto.CONTENT_MAX_DIMENSIONS_URI, |
| 291 | new String[]{DisplayPhoto.DISPLAY_MAX_DIM}, null, null, null); |
Jay Shrauner | b126f4a | 2014-01-18 10:45:01 -0800 | [diff] [blame] | 292 | if (c != null) { |
| 293 | try { |
| 294 | if (c.moveToFirst()) { |
| 295 | mPhotoDim = c.getInt(0); |
| 296 | } |
| 297 | } finally { |
| 298 | c.close(); |
| 299 | } |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 300 | } |
Jay Shrauner | b126f4a | 2014-01-18 10:45:01 -0800 | [diff] [blame] | 301 | return mPhotoDim != 0 ? mPhotoDim : mDefaultPhotoDim; |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 302 | } |
| 303 | |
| 304 | /** |
Yorke Lee | 637a38e | 2013-09-14 08:36:33 -0700 | [diff] [blame] | 305 | * Constructs an intent for capturing a photo and storing it in a temporary output uri. |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 306 | */ |
Yorke Lee | 637a38e | 2013-09-14 08:36:33 -0700 | [diff] [blame] | 307 | private Intent getTakePhotoIntent(Uri outputUri) { |
| 308 | final Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE, null); |
| 309 | ContactPhotoUtils.addPhotoPickerExtras(intent, outputUri); |
| 310 | return intent; |
| 311 | } |
| 312 | |
| 313 | /** |
| 314 | * Constructs an intent for picking a photo from Gallery, and returning the bitmap. |
| 315 | */ |
| 316 | private Intent getPhotoPickIntent(Uri outputUri) { |
Wenyi Wang | bfea74f | 2015-10-19 13:30:18 -0700 | [diff] [blame] | 317 | final Intent intent = new Intent(Intent.ACTION_PICK, null); |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 318 | intent.setType("image/*"); |
Yorke Lee | 637a38e | 2013-09-14 08:36:33 -0700 | [diff] [blame] | 319 | ContactPhotoUtils.addPhotoPickerExtras(intent, outputUri); |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 320 | return intent; |
| 321 | } |
| 322 | |
Brian Attwell | 5234be9 | 2015-05-13 19:26:43 -0700 | [diff] [blame] | 323 | private boolean hasIntentHandler(Intent intent) { |
| 324 | final List<ResolveInfo> resolveInfo = mContext.getPackageManager() |
| 325 | .queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); |
| 326 | return resolveInfo != null && resolveInfo.size() > 0; |
| 327 | } |
| 328 | |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 329 | /** |
| 330 | * Constructs an intent for image cropping. |
| 331 | */ |
Yorke Lee | 637a38e | 2013-09-14 08:36:33 -0700 | [diff] [blame] | 332 | private Intent getCropImageIntent(Uri inputUri, Uri outputUri) { |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 333 | Intent intent = new Intent("com.android.camera.action.CROP"); |
Yorke Lee | 637a38e | 2013-09-14 08:36:33 -0700 | [diff] [blame] | 334 | intent.setDataAndType(inputUri, "image/*"); |
| 335 | ContactPhotoUtils.addPhotoPickerExtras(intent, outputUri); |
| 336 | ContactPhotoUtils.addCropExtras(intent, mPhotoPickSize); |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 337 | return intent; |
| 338 | } |
| 339 | |
| 340 | public abstract class PhotoActionListener implements PhotoActionPopup.Listener { |
| 341 | @Override |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 342 | public void onRemovePictureChosen() { |
| 343 | // No default implementation. |
| 344 | } |
| 345 | |
| 346 | @Override |
| 347 | public void onTakePhotoChosen() { |
| 348 | try { |
| 349 | // Launch camera to take photo for selected contact |
Yorke Lee | 637a38e | 2013-09-14 08:36:33 -0700 | [diff] [blame] | 350 | startTakePhotoActivity(mTempPhotoUri); |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 351 | } catch (ActivityNotFoundException e) { |
Josh Gargus | ebc1792 | 2012-05-04 18:47:09 -0700 | [diff] [blame] | 352 | Toast.makeText( |
| 353 | mContext, R.string.photoPickerNotFoundText, Toast.LENGTH_LONG).show(); |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 354 | } |
| 355 | } |
| 356 | |
| 357 | @Override |
| 358 | public void onPickFromGalleryChosen() { |
| 359 | try { |
| 360 | // Launch picker to choose photo for selected contact |
Yorke Lee | 637a38e | 2013-09-14 08:36:33 -0700 | [diff] [blame] | 361 | startPickFromGalleryActivity(mTempPhotoUri); |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 362 | } catch (ActivityNotFoundException e) { |
Josh Gargus | ebc1792 | 2012-05-04 18:47:09 -0700 | [diff] [blame] | 363 | Toast.makeText( |
| 364 | mContext, R.string.photoPickerNotFoundText, Toast.LENGTH_LONG).show(); |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 365 | } |
| 366 | } |
| 367 | |
| 368 | /** |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 369 | * Called when the user has completed selection of a photo. |
Yorke Lee | 637a38e | 2013-09-14 08:36:33 -0700 | [diff] [blame] | 370 | * @throws FileNotFoundException |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 371 | */ |
Yorke Lee | 637a38e | 2013-09-14 08:36:33 -0700 | [diff] [blame] | 372 | public abstract void onPhotoSelected(Uri uri) throws FileNotFoundException; |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 373 | |
| 374 | /** |
| 375 | * Gets the current photo file that is being interacted with. It is the activity or |
| 376 | * fragment's responsibility to maintain this in saved state, since this handler instance |
| 377 | * will not survive rotation. |
| 378 | */ |
Yorke Lee | 637a38e | 2013-09-14 08:36:33 -0700 | [diff] [blame] | 379 | public abstract Uri getCurrentPhotoUri(); |
Dave Santoro | 6fa7384 | 2011-09-28 14:37:06 -0700 | [diff] [blame] | 380 | |
| 381 | /** |
| 382 | * Called when the photo selection dialog is dismissed. |
| 383 | */ |
| 384 | public abstract void onPhotoSelectionDismissed(); |
| 385 | } |
| 386 | } |