AttachPhotoActivity.java revision ebc17929007ecf12f25b8e90023766e8a083d11e
1/* 2 * Copyright (C) 2006 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 17package com.android.contacts.activities; 18 19import com.android.contacts.ContactLoader; 20import com.android.contacts.ContactLoader.Result; 21import com.android.contacts.ContactSaveService; 22import com.android.contacts.ContactsActivity; 23import com.android.contacts.ContactsUtils; 24import com.android.contacts.model.AccountType; 25import com.android.contacts.model.EntityDelta; 26import com.android.contacts.model.EntityDeltaList; 27import com.android.contacts.model.EntityModifier; 28import com.android.contacts.util.ContactPhotoUtils; 29 30import android.content.ContentResolver; 31import android.content.Intent; 32import android.content.Loader; 33import android.content.Loader.OnLoadCompleteListener; 34import android.database.Cursor; 35import android.graphics.Bitmap; 36import android.graphics.BitmapFactory; 37import android.net.Uri; 38import android.os.Bundle; 39import android.provider.ContactsContract.CommonDataKinds.Photo; 40import android.provider.ContactsContract.Contacts; 41import android.provider.ContactsContract.DisplayPhoto; 42import android.provider.MediaStore; 43import android.util.Log; 44 45import java.io.File; 46 47/** 48 * Provides an external interface for other applications to attach images 49 * to contacts. It will first present a contact picker and then run the 50 * image that is handed to it through the cropper to make the image the proper 51 * size and give the user a chance to use the face detector. 52 */ 53public class AttachPhotoActivity extends ContactsActivity { 54 private static final String TAG = AttachPhotoActivity.class.getSimpleName(); 55 56 private static final int REQUEST_PICK_CONTACT = 1; 57 private static final int REQUEST_CROP_PHOTO = 2; 58 59 private static final String KEY_CONTACT_URI = "contact_uri"; 60 private static final String KEY_TEMP_PHOTO_URI = "temp_photo_uri"; 61 62 private File mTempPhotoFile; 63 private Uri mTempPhotoUri; 64 65 private ContentResolver mContentResolver; 66 67 // Height and width (in pixels) to request for the photo - queried from the provider. 68 private static int mPhotoDim; 69 70 private Uri mContactUri; 71 72 @Override 73 public void onCreate(Bundle icicle) { 74 super.onCreate(icicle); 75 76 if (icicle != null) { 77 final String uri = icicle.getString(KEY_CONTACT_URI); 78 mContactUri = (uri == null) ? null : Uri.parse(uri); 79 80 mTempPhotoUri = Uri.parse(icicle.getString(KEY_TEMP_PHOTO_URI)); 81 mTempPhotoFile = new File(mTempPhotoUri.getPath()); 82 } else { 83 mTempPhotoFile = ContactPhotoUtils.generateTempPhotoFile(this); 84 mTempPhotoUri = Uri.fromFile(mTempPhotoFile); 85 86 Intent intent = new Intent(Intent.ACTION_GET_CONTENT); 87 intent.setType(Contacts.CONTENT_ITEM_TYPE); 88 startActivityForResult(intent, REQUEST_PICK_CONTACT); 89 } 90 91 mContentResolver = getContentResolver(); 92 93 // Load the photo dimension to request. 94 Cursor c = mContentResolver.query(DisplayPhoto.CONTENT_MAX_DIMENSIONS_URI, 95 new String[]{DisplayPhoto.DISPLAY_MAX_DIM}, null, null, null); 96 try { 97 c.moveToFirst(); 98 mPhotoDim = c.getInt(0); 99 } finally { 100 c.close(); 101 } 102 } 103 104 @Override 105 protected void onSaveInstanceState(Bundle outState) { 106 super.onSaveInstanceState(outState); 107 if (mContactUri != null) outState.putString(KEY_CONTACT_URI, mContactUri.toString()); 108 outState.putString(KEY_TEMP_PHOTO_URI, mTempPhotoUri.toString()); 109 } 110 111 @Override 112 protected void onActivityResult(int requestCode, int resultCode, Intent result) { 113 if (resultCode != RESULT_OK) { 114 finish(); 115 return; 116 } 117 118 if (requestCode == REQUEST_PICK_CONTACT) { 119 // A contact was picked. Launch the cropper to get face detection, the right size, etc. 120 // TODO: get these values from constants somewhere 121 Intent myIntent = getIntent(); 122 Intent intent = new Intent("com.android.camera.action.CROP", myIntent.getData()); 123 if (myIntent.getStringExtra("mimeType") != null) { 124 intent.setDataAndType(myIntent.getData(), myIntent.getStringExtra("mimeType")); 125 } 126 intent.putExtra("crop", "true"); 127 intent.putExtra("aspectX", 1); 128 intent.putExtra("aspectY", 1); 129 intent.putExtra("outputX", mPhotoDim); 130 intent.putExtra("outputY", mPhotoDim); 131 intent.putExtra(MediaStore.EXTRA_OUTPUT, mTempPhotoUri); 132 133 startActivityForResult(intent, REQUEST_CROP_PHOTO); 134 135 mContactUri = result.getData(); 136 137 } else if (requestCode == REQUEST_CROP_PHOTO) { 138 loadContact(mContactUri, new Listener() { 139 @Override 140 public void onContactLoaded(ContactLoader.Result contact) { 141 saveContact(contact); 142 } 143 }); 144 } 145 } 146 147 // TODO: consider moving this to ContactLoader, especially if we keep adding similar 148 // code elsewhere (ViewNotificationService is another case). The only concern is that, 149 // although this is convenient, it isn't quite as robust as using LoaderManager... for 150 // instance, the loader doesn't persist across Activity restarts. 151 private void loadContact(Uri contactUri, final Listener listener) { 152 final ContactLoader loader = new ContactLoader(this, contactUri); 153 loader.registerListener(0, new OnLoadCompleteListener<ContactLoader.Result>() { 154 @Override 155 public void onLoadComplete( 156 Loader<ContactLoader.Result> loader, ContactLoader.Result contact) { 157 try { 158 loader.reset(); 159 } 160 catch (RuntimeException e) { 161 Log.e(TAG, "Error resetting loader", e); 162 } 163 listener.onContactLoaded(contact); 164 } 165 }); 166 loader.startLoading(); 167 } 168 169 private interface Listener { 170 public void onContactLoaded(Result contact); 171 } 172 173 /** 174 * If prerequisites have been met, attach the photo to a raw-contact and save. 175 * The prerequisites are: 176 * - photo has been cropped 177 * - contact has been loaded 178 */ 179 private void saveContact(ContactLoader.Result contact) { 180 181 // Obtain the raw-contact that we will save to. 182 EntityDeltaList deltaList = contact.createEntityDeltaList(); 183 EntityDelta raw = deltaList.getFirstWritableRawContact(this); 184 if (raw == null) { 185 Log.w(TAG, "no writable raw-contact found"); 186 return; 187 } 188 189 // Create a scaled, compressed bitmap to add to the entity-delta list. 190 final int size = ContactsUtils.getThumbnailSize(this); 191 final Bitmap bitmap = BitmapFactory.decodeFile(mTempPhotoFile.getAbsolutePath()); 192 final Bitmap scaled = Bitmap.createScaledBitmap(bitmap, size, size, false); 193 final byte[] compressed = ContactPhotoUtils.compressBitmap(scaled); 194 if (compressed == null) { 195 Log.w(TAG, "could not create scaled and compressed Bitmap"); 196 return; 197 } 198 199 // Add compressed bitmap to entity-delta... this allows us to save to 200 // a new contact; otherwise the entity-delta-list would be empty, and 201 // the ContactSaveService would not create the new contact, and the 202 // full-res photo would fail to be saved to the non-existent contact. 203 AccountType account = raw.getRawContactAccountType(this); 204 EntityDelta.ValuesDelta values = 205 EntityModifier.ensureKindExists(raw, account, Photo.CONTENT_ITEM_TYPE); 206 if (values == null) { 207 Log.w(TAG, "cannot attach photo to this account type"); 208 return; 209 } 210 values.put(Photo.PHOTO, compressed); 211 212 // Finally, invoke the ContactSaveService. 213 Log.v(TAG, "all prerequisites met, about to save photo to contact"); 214 Intent intent = ContactSaveService.createSaveContactIntent( 215 this, 216 deltaList, 217 "", 0, 218 contact.isUserProfile(), 219 null, null, 220 raw.getRawContactId(), 221 mTempPhotoFile.getAbsolutePath()); 222 startService(intent); 223 finish(); 224 } 225} 226