• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.provider.ContactsContract.CommonDataKinds.Photo;
23 import android.util.AttributeSet;
24 import android.view.View;
25 import android.widget.ImageView;
26 import android.widget.LinearLayout;
27 
28 import com.android.contacts.R;
29 import com.android.contacts.common.model.RawContactDelta;
30 import com.android.contacts.common.ContactPhotoManager;
31 import com.android.contacts.common.ContactsUtils;
32 import com.android.contacts.common.model.ValuesDelta;
33 import com.android.contacts.common.model.dataitem.DataKind;
34 import com.android.contacts.util.ContactPhotoUtils;
35 
36 /**
37  * Simple editor for {@link Photo}.
38  */
39 public class PhotoEditorView extends LinearLayout implements Editor {
40 
41     private ImageView mPhotoImageView;
42     private View mFrameView;
43 
44     private ValuesDelta mEntry;
45     private EditorListener mListener;
46     private View mTriangleAffordance;
47 
48     private boolean mHasSetPhoto = false;
49     private boolean mReadOnly;
50 
PhotoEditorView(Context context)51     public PhotoEditorView(Context context) {
52         super(context);
53     }
54 
PhotoEditorView(Context context, AttributeSet attrs)55     public PhotoEditorView(Context context, AttributeSet attrs) {
56         super(context, attrs);
57     }
58 
59     @Override
setEnabled(boolean enabled)60     public void setEnabled(boolean enabled) {
61         super.setEnabled(enabled);
62         mFrameView.setEnabled(enabled);
63     }
64 
65     @Override
editNewlyAddedField()66     public void editNewlyAddedField() {
67         // Never called, since the user never adds a new photo-editor;
68         // you can only change the picture in an existing editor.
69     }
70 
71     /** {@inheritDoc} */
72     @Override
onFinishInflate()73     protected void onFinishInflate() {
74         super.onFinishInflate();
75         mTriangleAffordance = findViewById(R.id.photo_triangle_affordance);
76         mPhotoImageView = (ImageView) findViewById(R.id.photo);
77         mFrameView = findViewById(R.id.frame);
78         mFrameView.setOnClickListener(new OnClickListener() {
79             @Override
80             public void onClick(View v) {
81                 if (mListener != null) {
82                     mListener.onRequest(EditorListener.REQUEST_PICK_PHOTO);
83                 }
84             }
85         });
86     }
87 
88     /** {@inheritDoc} */
89     @Override
onFieldChanged(String column, String value)90     public void onFieldChanged(String column, String value) {
91         throw new UnsupportedOperationException("Photos don't support direct field changes");
92     }
93 
94     /** {@inheritDoc} */
95     @Override
setValues(DataKind kind, ValuesDelta values, RawContactDelta state, boolean readOnly, ViewIdGenerator vig)96     public void setValues(DataKind kind, ValuesDelta values, RawContactDelta state, boolean readOnly,
97             ViewIdGenerator vig) {
98         mEntry = values;
99         mReadOnly = readOnly;
100 
101         setId(vig.getId(state, kind, values, 0));
102 
103         if (values != null) {
104             // Try decoding photo if actual entry
105             final byte[] photoBytes = values.getAsByteArray(Photo.PHOTO);
106             if (photoBytes != null) {
107                 final Bitmap photo = BitmapFactory.decodeByteArray(photoBytes, 0,
108                         photoBytes.length);
109 
110                 mPhotoImageView.setImageBitmap(photo);
111                 mFrameView.setEnabled(isEnabled());
112                 mHasSetPhoto = true;
113                 mEntry.setFromTemplate(false);
114             } else {
115                 resetDefault();
116             }
117         } else {
118             resetDefault();
119         }
120     }
121 
122     /**
123      * Return true if a valid {@link Photo} has been set.
124      */
hasSetPhoto()125     public boolean hasSetPhoto() {
126         return mHasSetPhoto;
127     }
128 
129     /**
130      * Assign the given {@link Bitmap} as the new value, updating UI and
131      * readying for persisting through {@link ValuesDelta}.
132      */
setPhotoBitmap(Bitmap photo)133     public void setPhotoBitmap(Bitmap photo) {
134         if (photo == null) {
135             // Clear any existing photo and return
136             mEntry.put(Photo.PHOTO, (byte[])null);
137             resetDefault();
138             return;
139         }
140 
141         mPhotoImageView.setImageBitmap(photo);
142         mFrameView.setEnabled(isEnabled());
143         mHasSetPhoto = true;
144         mEntry.setFromTemplate(false);
145 
146         // When the user chooses a new photo mark it as super primary
147         mEntry.setSuperPrimary(true);
148 
149         // Even though high-res photos cannot be saved by passing them via
150         // an EntityDeltaList (since they cause the Bundle size limit to be
151         // exceeded), we still pass a low-res thumbnail. This simplifies
152         // code all over the place, because we don't have to test whether
153         // there is a change in EITHER the delta-list OR a changed photo...
154         // this way, there is always a change in the delta-list.
155         final int size = ContactsUtils.getThumbnailSize(getContext());
156         final Bitmap scaled = Bitmap.createScaledBitmap(photo, size, size, false);
157         final byte[] compressed = ContactPhotoUtils.compressBitmap(scaled);
158         if (compressed != null) mEntry.setPhoto(compressed);
159     }
160 
161     /**
162      * Set the super primary bit on the photo.
163      */
setSuperPrimary(boolean superPrimary)164     public void setSuperPrimary(boolean superPrimary) {
165         mEntry.put(Photo.IS_SUPER_PRIMARY, superPrimary ? 1 : 0);
166     }
167 
resetDefault()168     protected void resetDefault() {
169         // Invalid photo, show default "add photo" place-holder
170         mPhotoImageView.setImageDrawable(
171                 ContactPhotoManager.getDefaultAvatarDrawableForContact(getResources(), false, null));
172         mFrameView.setEnabled(!mReadOnly && isEnabled());
173         mHasSetPhoto = false;
174         mEntry.setFromTemplate(true);
175     }
176 
177     /** {@inheritDoc} */
178     @Override
setEditorListener(EditorListener listener)179     public void setEditorListener(EditorListener listener) {
180         mListener = listener;
181 
182         final boolean isPushable = listener != null;
183         mTriangleAffordance.setVisibility(isPushable ? View.VISIBLE : View.INVISIBLE);
184         mFrameView.setVisibility(isPushable ? View.VISIBLE : View.INVISIBLE);
185     }
186 
187     @Override
setDeletable(boolean deletable)188     public void setDeletable(boolean deletable) {
189         // Photo is not deletable
190     }
191 
192     @Override
isEmpty()193     public boolean isEmpty() {
194         return !mHasSetPhoto;
195     }
196 
197     @Override
deleteEditor()198     public void deleteEditor() {
199         // Photo is not deletable
200     }
201 
202     @Override
clearAllFields()203     public void clearAllFields() {
204         resetDefault();
205     }
206 }
207