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