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