Home | History | Annotate | Download | only in editor
      1 /*
      2  * Copyright (C) 2009 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.editor;
     18 
     19 import android.content.Context;
     20 import android.graphics.Bitmap;
     21 import android.graphics.BitmapFactory;
     22 import android.net.Uri;
     23 import android.provider.ContactsContract.CommonDataKinds.Photo;
     24 import android.provider.ContactsContract.DisplayPhoto;
     25 import android.util.AttributeSet;
     26 import android.view.View;
     27 import android.widget.Button;
     28 import android.widget.ImageView;
     29 import android.widget.LinearLayout;
     30 import android.widget.RadioButton;
     31 
     32 import com.android.contacts.R;
     33 import com.android.contacts.common.ContactPhotoManager.DefaultImageProvider;
     34 import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
     35 import com.android.contacts.common.model.RawContactDelta;
     36 import com.android.contacts.common.ContactPhotoManager;
     37 import com.android.contacts.common.ContactsUtils;
     38 import com.android.contacts.common.model.ValuesDelta;
     39 import com.android.contacts.common.model.dataitem.DataKind;
     40 import com.android.contacts.util.ContactPhotoUtils;
     41 
     42 /**
     43  * Simple editor for {@link Photo}.
     44  */
     45 public class PhotoEditorView extends LinearLayout implements Editor {
     46 
     47     private ImageView mPhotoImageView;
     48     private Button mChangeButton;
     49     private RadioButton mPrimaryCheckBox;
     50 
     51     private ValuesDelta mEntry;
     52     private EditorListener mListener;
     53     private ContactPhotoManager mContactPhotoManager;
     54 
     55     private boolean mHasSetPhoto = false;
     56 
     57     public PhotoEditorView(Context context) {
     58         super(context);
     59     }
     60 
     61     public PhotoEditorView(Context context, AttributeSet attrs) {
     62         super(context, attrs);
     63     }
     64 
     65     @Override
     66     public void setEnabled(boolean enabled) {
     67         super.setEnabled(enabled);
     68     }
     69 
     70     @Override
     71     public void editNewlyAddedField() {
     72         // Never called, since the user never adds a new photo-editor;
     73         // you can only change the picture in an existing editor.
     74     }
     75 
     76     /** {@inheritDoc} */
     77     @Override
     78     protected void onFinishInflate() {
     79         super.onFinishInflate();
     80         mContactPhotoManager = ContactPhotoManager.getInstance(getContext());
     81         mPhotoImageView = (ImageView) findViewById(R.id.photo);
     82         mPrimaryCheckBox = (RadioButton) findViewById(R.id.primary_checkbox);
     83         mChangeButton = (Button) findViewById(R.id.change_button);
     84         mPrimaryCheckBox = (RadioButton) findViewById(R.id.primary_checkbox);
     85         if (mChangeButton != null) {
     86             mChangeButton.setOnClickListener(new OnClickListener() {
     87                 @Override
     88                 public void onClick(View v) {
     89                     if (mListener != null) {
     90                         mListener.onRequest(EditorListener.REQUEST_PICK_PHOTO);
     91                     }
     92                 }
     93             });
     94         }
     95         // Turn off own state management. We do this ourselves on rotation.
     96         mPrimaryCheckBox.setSaveEnabled(false);
     97         mPrimaryCheckBox.setOnClickListener(new OnClickListener() {
     98             @Override
     99             public void onClick(View v) {
    100                 if (mListener != null) {
    101                     mListener.onRequest(EditorListener.REQUEST_PICK_PRIMARY_PHOTO);
    102                 }
    103             }
    104         });
    105     }
    106 
    107     /** {@inheritDoc} */
    108     @Override
    109     public void onFieldChanged(String column, String value) {
    110         throw new UnsupportedOperationException("Photos don't support direct field changes");
    111     }
    112 
    113     /** {@inheritDoc} */
    114     @Override
    115     public void setValues(DataKind kind, ValuesDelta values, RawContactDelta state, boolean readOnly,
    116             ViewIdGenerator vig) {
    117         mEntry = values;
    118 
    119         setId(vig.getId(state, kind, values, 0));
    120 
    121         mPrimaryCheckBox.setChecked(values != null && values.isSuperPrimary());
    122 
    123         if (values != null) {
    124             // Try decoding photo if actual entry
    125             final byte[] photoBytes = values.getAsByteArray(Photo.PHOTO);
    126             if (photoBytes != null) {
    127                 final Bitmap photo = BitmapFactory.decodeByteArray(photoBytes, 0,
    128                         photoBytes.length);
    129 
    130                 mPhotoImageView.setImageBitmap(photo);
    131                 mHasSetPhoto = true;
    132                 mEntry.setFromTemplate(false);
    133 
    134                 if (values.getAfter() == null || values.getAfter().get(Photo.PHOTO) == null) {
    135                     // If the user hasn't updated the PHOTO value, then PHOTO_FILE_ID may contain
    136                     // a reference to a larger version of PHOTO that we can bind to the UI.
    137                     // Otherwise, we need to wait for a call to #setFullSizedPhoto() to update
    138                     // our full sized image.
    139                     final Integer photoFileId = values.getAsInteger(Photo.PHOTO_FILE_ID);
    140                     if (photoFileId != null) {
    141                         final Uri photoUri = DisplayPhoto.CONTENT_URI.buildUpon()
    142                                 .appendPath(photoFileId.toString()).build();
    143                         setFullSizedPhoto(photoUri);
    144                     }
    145                 }
    146 
    147             } else {
    148                 resetDefault();
    149             }
    150         } else {
    151             resetDefault();
    152         }
    153     }
    154 
    155     /**
    156      * Whether to display a "Primary photo" RadioButton. This is only needed if there are multiple
    157      * candidate photos.
    158      */
    159     public void setShowPrimary(boolean showPrimaryCheckBox) {
    160         mPrimaryCheckBox.setVisibility(showPrimaryCheckBox ? View.VISIBLE : View.GONE);
    161     }
    162 
    163     /**
    164      * Return true if a valid {@link Photo} has been set.
    165      */
    166     public boolean hasSetPhoto() {
    167         return mHasSetPhoto;
    168     }
    169 
    170     /**
    171      * Assign the given {@link Bitmap} as the new value for the sake of building
    172      * {@link ValuesDelta}. We may as well bind a thumbnail to the UI while we are at it.
    173      */
    174     public void setPhotoEntry(Bitmap photo) {
    175         if (photo == null) {
    176             // Clear any existing photo and return
    177             mEntry.put(Photo.PHOTO, (byte[])null);
    178             resetDefault();
    179             return;
    180         }
    181 
    182         final int size = ContactsUtils.getThumbnailSize(getContext());
    183         final Bitmap scaled = Bitmap.createScaledBitmap(photo, size, size, false);
    184 
    185         mPhotoImageView.setImageBitmap(scaled);
    186         mHasSetPhoto = true;
    187         mEntry.setFromTemplate(false);
    188 
    189         // When the user chooses a new photo mark it as super primary
    190         mEntry.setSuperPrimary(true);
    191 
    192         // Even though high-res photos cannot be saved by passing them via
    193         // an EntityDeltaList (since they cause the Bundle size limit to be
    194         // exceeded), we still pass a low-res thumbnail. This simplifies
    195         // code all over the place, because we don't have to test whether
    196         // there is a change in EITHER the delta-list OR a changed photo...
    197         // this way, there is always a change in the delta-list.
    198         final byte[] compressed = ContactPhotoUtils.compressBitmap(scaled);
    199         if (compressed != null) {
    200             mEntry.setPhoto(compressed);
    201         }
    202     }
    203 
    204     /**
    205      * Bind the {@param photoUri}'s photo to editor's UI. This doesn't affect {@link ValuesDelta}.
    206      */
    207     public void setFullSizedPhoto(Uri photoUri) {
    208         if (photoUri != null) {
    209             final DefaultImageProvider fallbackToPreviousImage = new DefaultImageProvider() {
    210                 @Override
    211                 public void applyDefaultImage(ImageView view, int extent, boolean darkTheme,
    212                         DefaultImageRequest defaultImageRequest) {
    213                     // Before we finish setting the full sized image, don't change the current
    214                     // image that is set in any way.
    215                 }
    216             };
    217             mContactPhotoManager.loadPhoto(mPhotoImageView, photoUri,
    218                     mPhotoImageView.getWidth(), /* darkTheme = */ false, /* isCircular = */ false,
    219                     /* defaultImageRequest = */ null, fallbackToPreviousImage);
    220         }
    221     }
    222 
    223     /**
    224      * Set the super primary bit on the photo.
    225      */
    226     public void setSuperPrimary(boolean superPrimary) {
    227         mEntry.put(Photo.IS_SUPER_PRIMARY, superPrimary ? 1 : 0);
    228     }
    229 
    230     protected void resetDefault() {
    231         // Invalid photo, show default "add photo" place-holder
    232         mPhotoImageView.setImageDrawable(
    233                 ContactPhotoManager.getDefaultAvatarDrawableForContact(getResources(), false, null));
    234         mHasSetPhoto = false;
    235         mEntry.setFromTemplate(true);
    236     }
    237 
    238     /** {@inheritDoc} */
    239     @Override
    240     public void setEditorListener(EditorListener listener) {
    241         mListener = listener;
    242     }
    243 
    244     @Override
    245     public void setDeletable(boolean deletable) {
    246         // Photo is not deletable
    247     }
    248 
    249     @Override
    250     public boolean isEmpty() {
    251         return !mHasSetPhoto;
    252     }
    253 
    254     @Override
    255     public void markDeleted() {
    256         // Photo is not deletable
    257     }
    258 
    259     @Override
    260     public void deleteEditor() {
    261         // Photo is not deletable
    262     }
    263 
    264     @Override
    265     public void clearAllFields() {
    266         resetDefault();
    267     }
    268 
    269     /**
    270      * The change drop down menu should be anchored to this view.
    271      */
    272     public View getChangeAnchorView() {
    273         return mChangeButton;
    274     }
    275 }
    276