blob: 1e727df69711f6a952faa3a68053a7c3604343ce [file] [log] [blame]
Walter Jang3efae4a2015-02-18 11:12:00 -08001/*
2 * Copyright (C) 2015 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
Walter Jang3bb7a0f2015-05-21 12:06:23 -070019import android.app.Activity;
Walter Jang3efae4a2015-02-18 11:12:00 -080020import android.content.Context;
Walter Jang3bb7a0f2015-05-21 12:06:23 -070021import android.content.res.TypedArray;
Walter Jang3efae4a2015-02-18 11:12:00 -080022import android.graphics.Bitmap;
Walter Jang3efae4a2015-02-18 11:12:00 -080023import android.net.Uri;
24import android.provider.ContactsContract;
Walter Jang3efae4a2015-02-18 11:12:00 -080025import android.util.AttributeSet;
Walter Jang3bb7a0f2015-05-21 12:06:23 -070026import android.util.DisplayMetrics;
Walter Jang10446452015-02-20 13:51:16 -080027import android.util.TypedValue;
Walter Jang3efae4a2015-02-18 11:12:00 -080028import android.view.View;
Walter Jang10446452015-02-20 13:51:16 -080029import android.view.ViewGroup;
Walter Jangff16eea2015-03-16 13:35:08 -070030import android.widget.RelativeLayout;
Walter Jang3efae4a2015-02-18 11:12:00 -080031
Gary Mai65971d02016-09-15 15:00:16 -070032import com.android.contacts.R;
33import com.android.contacts.common.ContactPhotoManager;
34import com.android.contacts.common.model.ValuesDelta;
35import com.android.contacts.common.util.MaterialColorMapUtils.MaterialPalette;
36import com.android.contacts.util.SchedulingUtils;
37import com.android.contacts.widget.QuickContactImageView;
38
Walter Jang3efae4a2015-02-18 11:12:00 -080039/**
Walter Jang31a74ad2015-10-02 19:17:39 -070040 * Displays a photo and calls the host back when the user clicks it.
Walter Jang3efae4a2015-02-18 11:12:00 -080041 */
Gary Mai363af602016-09-28 10:01:23 -070042public class PhotoEditorView extends RelativeLayout implements View.OnClickListener {
Walter Jang3efae4a2015-02-18 11:12:00 -080043
Walter Jang31a74ad2015-10-02 19:17:39 -070044 /**
45 * Callbacks for the host of this view.
46 */
47 public interface Listener {
Walter Jang3efae4a2015-02-18 11:12:00 -080048
Walter Jang31a74ad2015-10-02 19:17:39 -070049 /**
50 * Invoked when the user wants to change their photo.
51 */
52 void onPhotoEditorViewClicked();
53 }
54
55 private Listener mListener;
Walter Jang3efae4a2015-02-18 11:12:00 -080056
Walter Jang10446452015-02-20 13:51:16 -080057 private final float mLandscapePhotoRatio;
Walter Jang9d8f37e2015-03-16 13:56:15 -070058 private final float mPortraitPhotoRatio;
Walter Jang10446452015-02-20 13:51:16 -080059 private final boolean mIsTwoPanel;
60
Walter Jang3bb7a0f2015-05-21 12:06:23 -070061 private final int mActionBarHeight;
62 private final int mStatusBarHeight;
63
Walter Jangf46abd82015-02-20 16:52:04 -080064 private QuickContactImageView mPhotoImageView;
Walter Jangab50e6f2015-06-15 08:57:22 -070065 private View mPhotoIcon;
Walter Jang4a268d32015-03-16 18:18:59 -070066 private View mPhotoIconOverlay;
Walter Jangab50e6f2015-06-15 08:57:22 -070067 private View mPhotoTouchInterceptOverlay;
Gary Mai18724182016-09-20 17:33:30 -070068 private MaterialPalette mMaterialPalette;
Walter Jang3efae4a2015-02-18 11:12:00 -080069
Walter Jang31a74ad2015-10-02 19:17:39 -070070 private boolean mReadOnly;
71 private boolean mIsNonDefaultPhotoBound;
72
Gary Mai363af602016-09-28 10:01:23 -070073 public PhotoEditorView(Context context) {
Walter Jang10446452015-02-20 13:51:16 -080074 this(context, null);
Walter Jang3efae4a2015-02-18 11:12:00 -080075 }
76
Gary Mai363af602016-09-28 10:01:23 -070077 public PhotoEditorView(Context context, AttributeSet attrs) {
Walter Jang3efae4a2015-02-18 11:12:00 -080078 super(context, attrs);
Walter Jang31a74ad2015-10-02 19:17:39 -070079
Walter Jang261e1062015-03-13 11:07:23 -070080 mLandscapePhotoRatio = getTypedFloat(R.dimen.quickcontact_landscape_photo_ratio);
Walter Jang9d8f37e2015-03-16 13:56:15 -070081 mPortraitPhotoRatio = getTypedFloat(R.dimen.editor_portrait_photo_ratio);
Wenyi Wangcaf26192016-05-09 15:00:25 -070082 mIsTwoPanel = getResources().getBoolean(R.bool.contacteditor_two_panel);
Walter Jang3bb7a0f2015-05-21 12:06:23 -070083
84 final TypedArray styledAttributes = getContext().getTheme().obtainStyledAttributes(
85 new int[] { android.R.attr.actionBarSize });
86 mActionBarHeight = (int) styledAttributes.getDimension(0, 0);
87 styledAttributes.recycle();
88
89 final int resourceId = getResources().getIdentifier(
90 "status_bar_height", "dimen", "android");
91 mStatusBarHeight = resourceId > 0 ? getResources().getDimensionPixelSize(resourceId) : 0;
Walter Jang3efae4a2015-02-18 11:12:00 -080092 }
93
Walter Jang261e1062015-03-13 11:07:23 -070094 private float getTypedFloat(int resourceId) {
95 final TypedValue typedValue = new TypedValue();
96 getResources().getValue(resourceId, typedValue, /* resolveRefs =*/ true);
97 return typedValue.getFloat();
98 }
99
Walter Jang3efae4a2015-02-18 11:12:00 -0800100 @Override
101 protected void onFinishInflate() {
102 super.onFinishInflate();
Walter Jangf46abd82015-02-20 16:52:04 -0800103 mPhotoImageView = (QuickContactImageView) findViewById(R.id.photo);
Walter Jangab50e6f2015-06-15 08:57:22 -0700104 mPhotoIcon = findViewById(R.id.photo_icon);
Walter Jang4a268d32015-03-16 18:18:59 -0700105 mPhotoIconOverlay = findViewById(R.id.photo_icon_overlay);
Walter Jangab50e6f2015-06-15 08:57:22 -0700106 mPhotoTouchInterceptOverlay = findViewById(R.id.photo_touch_intercept_overlay);
Walter Jang3efae4a2015-02-18 11:12:00 -0800107 }
108
Walter Jang31a74ad2015-10-02 19:17:39 -0700109 public void setListener(Listener listener) {
110 mListener = listener;
111 }
Walter Jang3efae4a2015-02-18 11:12:00 -0800112
Walter Jang31a74ad2015-10-02 19:17:39 -0700113 public void setReadOnly(boolean readOnly) {
114 mReadOnly = readOnly;
Walter Jangab50e6f2015-06-15 08:57:22 -0700115 if (mReadOnly) {
116 mPhotoIcon.setVisibility(View.GONE);
117 mPhotoIconOverlay.setVisibility(View.GONE);
Gary Mai4ceabed2016-09-16 12:14:13 -0700118 mPhotoTouchInterceptOverlay.setClickable(false);
Gary Mai34047d22016-11-17 10:36:59 -0800119 mPhotoTouchInterceptOverlay.setContentDescription(getContext().getString(
120 R.string.editor_contact_photo_content_description));
Walter Jangab50e6f2015-06-15 08:57:22 -0700121 } else {
Gary Mai4ceabed2016-09-16 12:14:13 -0700122 mPhotoIcon.setVisibility(View.VISIBLE);
123 mPhotoIconOverlay.setVisibility(View.VISIBLE);
Walter Jangab50e6f2015-06-15 08:57:22 -0700124 mPhotoTouchInterceptOverlay.setOnClickListener(this);
Gary Maief6ceb22016-11-17 12:25:19 -0800125 updatePhotoDescription();
Walter Jangab50e6f2015-06-15 08:57:22 -0700126 }
Walter Jangfa127a12015-06-18 09:48:18 -0700127 }
128
Gary Mai18724182016-09-20 17:33:30 -0700129 public void setPalette(MaterialPalette palette) {
130 mMaterialPalette = palette;
131 }
132
Walter Jangfa127a12015-06-18 09:48:18 -0700133 /**
Walter Jang31a74ad2015-10-02 19:17:39 -0700134 * Tries to bind a full size photo or a bitmap loaded from the given ValuesDelta,
135 * and falls back to the default avatar, tinted using the given MaterialPalette (if it's not
136 * null);
Walter Jangfa127a12015-06-18 09:48:18 -0700137 */
Gary Mai18724182016-09-20 17:33:30 -0700138 public void setPhoto(ValuesDelta valuesDelta) {
Walter Jang31a74ad2015-10-02 19:17:39 -0700139 // Check if we can update to the full size photo immediately
140 final Long photoFileId = EditorUiUtils.getPhotoFileId(valuesDelta);
141 if (photoFileId != null) {
142 final Uri photoUri = ContactsContract.DisplayPhoto.CONTENT_URI.buildUpon()
143 .appendPath(photoFileId.toString()).build();
144 setFullSizedPhoto(photoUri);
145 adjustDimensions();
146 return;
Walter Jang3efae4a2015-02-18 11:12:00 -0800147 }
Walter Jang10446452015-02-20 13:51:16 -0800148
Walter Jang31a74ad2015-10-02 19:17:39 -0700149 // Use the bitmap image from the values delta
150 final Bitmap bitmap = EditorUiUtils.getPhotoBitmap(valuesDelta);
151 if (bitmap != null) {
152 setPhoto(bitmap);
153 adjustDimensions();
154 return;
Walter Jangf46abd82015-02-20 16:52:04 -0800155 }
156
Gary Mai18724182016-09-20 17:33:30 -0700157 setDefaultPhoto(mMaterialPalette);
Walter Jang31a74ad2015-10-02 19:17:39 -0700158 adjustDimensions();
159 }
160
161 private void adjustDimensions() {
162 // Follow the same logic as MultiShrinkScroll.initialize
Walter Jang10446452015-02-20 13:51:16 -0800163 SchedulingUtils.doOnPreDraw(this, /* drawNextFrame =*/ false, new Runnable() {
164 @Override
165 public void run() {
Walter Janga5e4bb22015-02-24 13:08:16 -0800166 final int photoHeight, photoWidth;
167 if (mIsTwoPanel) {
Walter Jang3bb7a0f2015-05-21 12:06:23 -0700168 photoHeight = getContentViewHeight();
Walter Janga5e4bb22015-02-24 13:08:16 -0800169 photoWidth = (int) (photoHeight * mLandscapePhotoRatio);
170 } else {
Walter Jang9d8f37e2015-03-16 13:56:15 -0700171 // Make the photo slightly shorter that it is wide
Walter Jang31a74ad2015-10-02 19:17:39 -0700172 photoWidth = getContentViewWidth();
Walter Jang9d8f37e2015-03-16 13:56:15 -0700173 photoHeight = (int) (photoWidth / mPortraitPhotoRatio);
Walter Janga5e4bb22015-02-24 13:08:16 -0800174 }
Walter Jang10446452015-02-20 13:51:16 -0800175 final ViewGroup.LayoutParams layoutParams = getLayoutParams();
176 layoutParams.height = photoHeight;
Walter Janga5e4bb22015-02-24 13:08:16 -0800177 layoutParams.width = photoWidth;
Walter Jang10446452015-02-20 13:51:16 -0800178 setLayoutParams(layoutParams);
179 }
180 });
Walter Jang3efae4a2015-02-18 11:12:00 -0800181 }
182
Walter Jang31a74ad2015-10-02 19:17:39 -0700183 private int getContentViewWidth() {
184 final Activity activity = (Activity) getContext();
185 final DisplayMetrics displayMetrics = new DisplayMetrics();
186 activity.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
187 return displayMetrics.widthPixels;
188 }
189
Walter Jang3bb7a0f2015-05-21 12:06:23 -0700190 // We're calculating the height the hard way because using the height of the content view
191 // (found using android.view.Window.ID_ANDROID_CONTENT) with the soft keyboard up when
192 // going from portrait to landscape mode results in a very small height value.
193 // See b/20526470
194 private int getContentViewHeight() {
195 final Activity activity = (Activity) getContext();
196 final DisplayMetrics displayMetrics = new DisplayMetrics();
197 activity.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
198 return displayMetrics.heightPixels - mActionBarHeight - mStatusBarHeight;
199 }
200
Walter Jang3efae4a2015-02-18 11:12:00 -0800201 /**
Walter Jang31a74ad2015-10-02 19:17:39 -0700202 * Whether a removable, non-default photo is bound to this view.
Walter Jang3efae4a2015-02-18 11:12:00 -0800203 */
204 public boolean isWritablePhotoSet() {
Walter Jang31a74ad2015-10-02 19:17:39 -0700205 return !mReadOnly && mIsNonDefaultPhotoBound;
Walter Jang3efae4a2015-02-18 11:12:00 -0800206 }
207
208 /**
Walter Jang31a74ad2015-10-02 19:17:39 -0700209 * Binds the given bitmap.
Walter Jang3efae4a2015-02-18 11:12:00 -0800210 */
Walter Jang31a74ad2015-10-02 19:17:39 -0700211 private void setPhoto(Bitmap bitmap) {
212 mPhotoImageView.setImageBitmap(bitmap);
213 mIsNonDefaultPhotoBound = true;
Gary Maief6ceb22016-11-17 12:25:19 -0800214 updatePhotoDescription();
Walter Jang31a74ad2015-10-02 19:17:39 -0700215 }
Walter Jang3efae4a2015-02-18 11:12:00 -0800216
Walter Jang31a74ad2015-10-02 19:17:39 -0700217 private void setDefaultPhoto(MaterialPalette materialPalette) {
Gary Maida20b472016-09-20 14:46:40 -0700218 mIsNonDefaultPhotoBound = false;
Gary Maief6ceb22016-11-17 12:25:19 -0800219 updatePhotoDescription();
Walter Jang31a74ad2015-10-02 19:17:39 -0700220 EditorUiUtils.setDefaultPhoto(mPhotoImageView, getResources(), materialPalette);
Walter Jang3efae4a2015-02-18 11:12:00 -0800221 }
222
Gary Maief6ceb22016-11-17 12:25:19 -0800223 private void updatePhotoDescription() {
224 mPhotoTouchInterceptOverlay.setContentDescription(getContext().getString(
225 mIsNonDefaultPhotoBound
226 ? R.string.editor_change_photo_content_description
227 : R.string.editor_add_photo_content_description));
228 }
Walter Jang3efae4a2015-02-18 11:12:00 -0800229 /**
Walter Jang31a74ad2015-10-02 19:17:39 -0700230 * Binds a full size photo loaded from the given Uri.
Walter Jang3efae4a2015-02-18 11:12:00 -0800231 */
232 public void setFullSizedPhoto(Uri photoUri) {
Walter Jang31a74ad2015-10-02 19:17:39 -0700233 EditorUiUtils.loadPhoto(ContactPhotoManager.getInstance(getContext()),
234 mPhotoImageView, photoUri);
235 mIsNonDefaultPhotoBound = true;
Gary Maief6ceb22016-11-17 12:25:19 -0800236 updatePhotoDescription();
Walter Jang31a74ad2015-10-02 19:17:39 -0700237 }
238
239 /**
240 * Removes the current bound photo bitmap.
241 */
242 public void removePhoto() {
Gary Mai18724182016-09-20 17:33:30 -0700243 setDefaultPhoto(mMaterialPalette);
Walter Jang3efae4a2015-02-18 11:12:00 -0800244 }
245
246 @Override
247 public void onClick(View view) {
Walter Jang31a74ad2015-10-02 19:17:39 -0700248 if (mListener != null) {
249 mListener.onPhotoEditorViewClicked();
Walter Jang3efae4a2015-02-18 11:12:00 -0800250 }
251 }
252}