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