Walter Jang | 3efae4a | 2015-02-18 11:12:00 -0800 | [diff] [blame] | 1 | /* |
| 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 | |
| 17 | package com.android.contacts.editor; |
| 18 | |
Walter Jang | 3bb7a0f | 2015-05-21 12:06:23 -0700 | [diff] [blame] | 19 | import android.app.Activity; |
Walter Jang | 3efae4a | 2015-02-18 11:12:00 -0800 | [diff] [blame] | 20 | import android.content.Context; |
Walter Jang | 3bb7a0f | 2015-05-21 12:06:23 -0700 | [diff] [blame] | 21 | import android.content.res.TypedArray; |
Walter Jang | 3efae4a | 2015-02-18 11:12:00 -0800 | [diff] [blame] | 22 | import android.graphics.Bitmap; |
Walter Jang | 3efae4a | 2015-02-18 11:12:00 -0800 | [diff] [blame] | 23 | import android.net.Uri; |
| 24 | import android.provider.ContactsContract; |
Walter Jang | 3efae4a | 2015-02-18 11:12:00 -0800 | [diff] [blame] | 25 | import android.util.AttributeSet; |
Walter Jang | 3bb7a0f | 2015-05-21 12:06:23 -0700 | [diff] [blame] | 26 | import android.util.DisplayMetrics; |
Walter Jang | 1044645 | 2015-02-20 13:51:16 -0800 | [diff] [blame] | 27 | import android.util.TypedValue; |
Walter Jang | 3efae4a | 2015-02-18 11:12:00 -0800 | [diff] [blame] | 28 | import android.view.View; |
Walter Jang | 1044645 | 2015-02-20 13:51:16 -0800 | [diff] [blame] | 29 | import android.view.ViewGroup; |
Walter Jang | ff16eea | 2015-03-16 13:35:08 -0700 | [diff] [blame] | 30 | import android.widget.RelativeLayout; |
Walter Jang | 3efae4a | 2015-02-18 11:12:00 -0800 | [diff] [blame] | 31 | |
Gary Mai | 65971d0 | 2016-09-15 15:00:16 -0700 | [diff] [blame] | 32 | import com.android.contacts.R; |
| 33 | import com.android.contacts.common.ContactPhotoManager; |
| 34 | import com.android.contacts.common.model.ValuesDelta; |
| 35 | import com.android.contacts.common.util.MaterialColorMapUtils.MaterialPalette; |
| 36 | import com.android.contacts.util.SchedulingUtils; |
| 37 | import com.android.contacts.widget.QuickContactImageView; |
| 38 | |
Walter Jang | 3efae4a | 2015-02-18 11:12:00 -0800 | [diff] [blame] | 39 | /** |
Walter Jang | 31a74ad | 2015-10-02 19:17:39 -0700 | [diff] [blame] | 40 | * Displays a photo and calls the host back when the user clicks it. |
Walter Jang | 3efae4a | 2015-02-18 11:12:00 -0800 | [diff] [blame] | 41 | */ |
Gary Mai | 363af60 | 2016-09-28 10:01:23 -0700 | [diff] [blame] | 42 | public class PhotoEditorView extends RelativeLayout implements View.OnClickListener { |
Walter Jang | 3efae4a | 2015-02-18 11:12:00 -0800 | [diff] [blame] | 43 | |
Walter Jang | 31a74ad | 2015-10-02 19:17:39 -0700 | [diff] [blame] | 44 | /** |
| 45 | * Callbacks for the host of this view. |
| 46 | */ |
| 47 | public interface Listener { |
Walter Jang | 3efae4a | 2015-02-18 11:12:00 -0800 | [diff] [blame] | 48 | |
Walter Jang | 31a74ad | 2015-10-02 19:17:39 -0700 | [diff] [blame] | 49 | /** |
| 50 | * Invoked when the user wants to change their photo. |
| 51 | */ |
| 52 | void onPhotoEditorViewClicked(); |
| 53 | } |
| 54 | |
| 55 | private Listener mListener; |
Walter Jang | 3efae4a | 2015-02-18 11:12:00 -0800 | [diff] [blame] | 56 | |
Walter Jang | 1044645 | 2015-02-20 13:51:16 -0800 | [diff] [blame] | 57 | private final float mLandscapePhotoRatio; |
Walter Jang | 9d8f37e | 2015-03-16 13:56:15 -0700 | [diff] [blame] | 58 | private final float mPortraitPhotoRatio; |
Walter Jang | 1044645 | 2015-02-20 13:51:16 -0800 | [diff] [blame] | 59 | private final boolean mIsTwoPanel; |
| 60 | |
Walter Jang | 3bb7a0f | 2015-05-21 12:06:23 -0700 | [diff] [blame] | 61 | private final int mActionBarHeight; |
| 62 | private final int mStatusBarHeight; |
| 63 | |
Walter Jang | f46abd8 | 2015-02-20 16:52:04 -0800 | [diff] [blame] | 64 | private QuickContactImageView mPhotoImageView; |
Walter Jang | ab50e6f | 2015-06-15 08:57:22 -0700 | [diff] [blame] | 65 | private View mPhotoIcon; |
Walter Jang | 4a268d3 | 2015-03-16 18:18:59 -0700 | [diff] [blame] | 66 | private View mPhotoIconOverlay; |
Walter Jang | ab50e6f | 2015-06-15 08:57:22 -0700 | [diff] [blame] | 67 | private View mPhotoTouchInterceptOverlay; |
Gary Mai | 1872418 | 2016-09-20 17:33:30 -0700 | [diff] [blame] | 68 | private MaterialPalette mMaterialPalette; |
Walter Jang | 3efae4a | 2015-02-18 11:12:00 -0800 | [diff] [blame] | 69 | |
Walter Jang | 31a74ad | 2015-10-02 19:17:39 -0700 | [diff] [blame] | 70 | private boolean mReadOnly; |
| 71 | private boolean mIsNonDefaultPhotoBound; |
| 72 | |
Gary Mai | 363af60 | 2016-09-28 10:01:23 -0700 | [diff] [blame] | 73 | public PhotoEditorView(Context context) { |
Walter Jang | 1044645 | 2015-02-20 13:51:16 -0800 | [diff] [blame] | 74 | this(context, null); |
Walter Jang | 3efae4a | 2015-02-18 11:12:00 -0800 | [diff] [blame] | 75 | } |
| 76 | |
Gary Mai | 363af60 | 2016-09-28 10:01:23 -0700 | [diff] [blame] | 77 | public PhotoEditorView(Context context, AttributeSet attrs) { |
Walter Jang | 3efae4a | 2015-02-18 11:12:00 -0800 | [diff] [blame] | 78 | super(context, attrs); |
Walter Jang | 31a74ad | 2015-10-02 19:17:39 -0700 | [diff] [blame] | 79 | |
Walter Jang | 261e106 | 2015-03-13 11:07:23 -0700 | [diff] [blame] | 80 | mLandscapePhotoRatio = getTypedFloat(R.dimen.quickcontact_landscape_photo_ratio); |
Walter Jang | 9d8f37e | 2015-03-16 13:56:15 -0700 | [diff] [blame] | 81 | mPortraitPhotoRatio = getTypedFloat(R.dimen.editor_portrait_photo_ratio); |
Wenyi Wang | caf2619 | 2016-05-09 15:00:25 -0700 | [diff] [blame] | 82 | mIsTwoPanel = getResources().getBoolean(R.bool.contacteditor_two_panel); |
Walter Jang | 3bb7a0f | 2015-05-21 12:06:23 -0700 | [diff] [blame] | 83 | |
| 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 Jang | 3efae4a | 2015-02-18 11:12:00 -0800 | [diff] [blame] | 92 | } |
| 93 | |
Walter Jang | 261e106 | 2015-03-13 11:07:23 -0700 | [diff] [blame] | 94 | 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 Jang | 3efae4a | 2015-02-18 11:12:00 -0800 | [diff] [blame] | 100 | @Override |
| 101 | protected void onFinishInflate() { |
| 102 | super.onFinishInflate(); |
Walter Jang | f46abd8 | 2015-02-20 16:52:04 -0800 | [diff] [blame] | 103 | mPhotoImageView = (QuickContactImageView) findViewById(R.id.photo); |
Walter Jang | ab50e6f | 2015-06-15 08:57:22 -0700 | [diff] [blame] | 104 | mPhotoIcon = findViewById(R.id.photo_icon); |
Walter Jang | 4a268d3 | 2015-03-16 18:18:59 -0700 | [diff] [blame] | 105 | mPhotoIconOverlay = findViewById(R.id.photo_icon_overlay); |
Walter Jang | ab50e6f | 2015-06-15 08:57:22 -0700 | [diff] [blame] | 106 | mPhotoTouchInterceptOverlay = findViewById(R.id.photo_touch_intercept_overlay); |
Walter Jang | 3efae4a | 2015-02-18 11:12:00 -0800 | [diff] [blame] | 107 | } |
| 108 | |
Walter Jang | 31a74ad | 2015-10-02 19:17:39 -0700 | [diff] [blame] | 109 | public void setListener(Listener listener) { |
| 110 | mListener = listener; |
| 111 | } |
Walter Jang | 3efae4a | 2015-02-18 11:12:00 -0800 | [diff] [blame] | 112 | |
Walter Jang | 31a74ad | 2015-10-02 19:17:39 -0700 | [diff] [blame] | 113 | public void setReadOnly(boolean readOnly) { |
| 114 | mReadOnly = readOnly; |
Walter Jang | ab50e6f | 2015-06-15 08:57:22 -0700 | [diff] [blame] | 115 | if (mReadOnly) { |
| 116 | mPhotoIcon.setVisibility(View.GONE); |
| 117 | mPhotoIconOverlay.setVisibility(View.GONE); |
Gary Mai | 4ceabed | 2016-09-16 12:14:13 -0700 | [diff] [blame] | 118 | mPhotoTouchInterceptOverlay.setClickable(false); |
Gary Mai | 34047d2 | 2016-11-17 10:36:59 -0800 | [diff] [blame] | 119 | mPhotoTouchInterceptOverlay.setContentDescription(getContext().getString( |
| 120 | R.string.editor_contact_photo_content_description)); |
Walter Jang | ab50e6f | 2015-06-15 08:57:22 -0700 | [diff] [blame] | 121 | } else { |
Gary Mai | 4ceabed | 2016-09-16 12:14:13 -0700 | [diff] [blame] | 122 | mPhotoIcon.setVisibility(View.VISIBLE); |
| 123 | mPhotoIconOverlay.setVisibility(View.VISIBLE); |
Walter Jang | ab50e6f | 2015-06-15 08:57:22 -0700 | [diff] [blame] | 124 | mPhotoTouchInterceptOverlay.setOnClickListener(this); |
Gary Mai | ef6ceb2 | 2016-11-17 12:25:19 -0800 | [diff] [blame] | 125 | updatePhotoDescription(); |
Walter Jang | ab50e6f | 2015-06-15 08:57:22 -0700 | [diff] [blame] | 126 | } |
Walter Jang | fa127a1 | 2015-06-18 09:48:18 -0700 | [diff] [blame] | 127 | } |
| 128 | |
Gary Mai | 1872418 | 2016-09-20 17:33:30 -0700 | [diff] [blame] | 129 | public void setPalette(MaterialPalette palette) { |
| 130 | mMaterialPalette = palette; |
| 131 | } |
| 132 | |
Walter Jang | fa127a1 | 2015-06-18 09:48:18 -0700 | [diff] [blame] | 133 | /** |
Walter Jang | 31a74ad | 2015-10-02 19:17:39 -0700 | [diff] [blame] | 134 | * 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 Jang | fa127a1 | 2015-06-18 09:48:18 -0700 | [diff] [blame] | 137 | */ |
Gary Mai | 1872418 | 2016-09-20 17:33:30 -0700 | [diff] [blame] | 138 | public void setPhoto(ValuesDelta valuesDelta) { |
Walter Jang | 31a74ad | 2015-10-02 19:17:39 -0700 | [diff] [blame] | 139 | // 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 Jang | 3efae4a | 2015-02-18 11:12:00 -0800 | [diff] [blame] | 147 | } |
Walter Jang | 1044645 | 2015-02-20 13:51:16 -0800 | [diff] [blame] | 148 | |
Walter Jang | 31a74ad | 2015-10-02 19:17:39 -0700 | [diff] [blame] | 149 | // 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 Jang | f46abd8 | 2015-02-20 16:52:04 -0800 | [diff] [blame] | 155 | } |
| 156 | |
Gary Mai | 1872418 | 2016-09-20 17:33:30 -0700 | [diff] [blame] | 157 | setDefaultPhoto(mMaterialPalette); |
Walter Jang | 31a74ad | 2015-10-02 19:17:39 -0700 | [diff] [blame] | 158 | adjustDimensions(); |
| 159 | } |
| 160 | |
| 161 | private void adjustDimensions() { |
| 162 | // Follow the same logic as MultiShrinkScroll.initialize |
Walter Jang | 1044645 | 2015-02-20 13:51:16 -0800 | [diff] [blame] | 163 | SchedulingUtils.doOnPreDraw(this, /* drawNextFrame =*/ false, new Runnable() { |
| 164 | @Override |
| 165 | public void run() { |
Walter Jang | a5e4bb2 | 2015-02-24 13:08:16 -0800 | [diff] [blame] | 166 | final int photoHeight, photoWidth; |
| 167 | if (mIsTwoPanel) { |
Walter Jang | 3bb7a0f | 2015-05-21 12:06:23 -0700 | [diff] [blame] | 168 | photoHeight = getContentViewHeight(); |
Walter Jang | a5e4bb2 | 2015-02-24 13:08:16 -0800 | [diff] [blame] | 169 | photoWidth = (int) (photoHeight * mLandscapePhotoRatio); |
| 170 | } else { |
Walter Jang | 9d8f37e | 2015-03-16 13:56:15 -0700 | [diff] [blame] | 171 | // Make the photo slightly shorter that it is wide |
Walter Jang | 31a74ad | 2015-10-02 19:17:39 -0700 | [diff] [blame] | 172 | photoWidth = getContentViewWidth(); |
Walter Jang | 9d8f37e | 2015-03-16 13:56:15 -0700 | [diff] [blame] | 173 | photoHeight = (int) (photoWidth / mPortraitPhotoRatio); |
Walter Jang | a5e4bb2 | 2015-02-24 13:08:16 -0800 | [diff] [blame] | 174 | } |
Walter Jang | 1044645 | 2015-02-20 13:51:16 -0800 | [diff] [blame] | 175 | final ViewGroup.LayoutParams layoutParams = getLayoutParams(); |
| 176 | layoutParams.height = photoHeight; |
Walter Jang | a5e4bb2 | 2015-02-24 13:08:16 -0800 | [diff] [blame] | 177 | layoutParams.width = photoWidth; |
Walter Jang | 1044645 | 2015-02-20 13:51:16 -0800 | [diff] [blame] | 178 | setLayoutParams(layoutParams); |
| 179 | } |
| 180 | }); |
Walter Jang | 3efae4a | 2015-02-18 11:12:00 -0800 | [diff] [blame] | 181 | } |
| 182 | |
Walter Jang | 31a74ad | 2015-10-02 19:17:39 -0700 | [diff] [blame] | 183 | 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 Jang | 3bb7a0f | 2015-05-21 12:06:23 -0700 | [diff] [blame] | 190 | // 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 Jang | 3efae4a | 2015-02-18 11:12:00 -0800 | [diff] [blame] | 201 | /** |
Walter Jang | 31a74ad | 2015-10-02 19:17:39 -0700 | [diff] [blame] | 202 | * Whether a removable, non-default photo is bound to this view. |
Walter Jang | 3efae4a | 2015-02-18 11:12:00 -0800 | [diff] [blame] | 203 | */ |
| 204 | public boolean isWritablePhotoSet() { |
Walter Jang | 31a74ad | 2015-10-02 19:17:39 -0700 | [diff] [blame] | 205 | return !mReadOnly && mIsNonDefaultPhotoBound; |
Walter Jang | 3efae4a | 2015-02-18 11:12:00 -0800 | [diff] [blame] | 206 | } |
| 207 | |
| 208 | /** |
Walter Jang | 31a74ad | 2015-10-02 19:17:39 -0700 | [diff] [blame] | 209 | * Binds the given bitmap. |
Walter Jang | 3efae4a | 2015-02-18 11:12:00 -0800 | [diff] [blame] | 210 | */ |
Walter Jang | 31a74ad | 2015-10-02 19:17:39 -0700 | [diff] [blame] | 211 | private void setPhoto(Bitmap bitmap) { |
| 212 | mPhotoImageView.setImageBitmap(bitmap); |
| 213 | mIsNonDefaultPhotoBound = true; |
Gary Mai | ef6ceb2 | 2016-11-17 12:25:19 -0800 | [diff] [blame] | 214 | updatePhotoDescription(); |
Walter Jang | 31a74ad | 2015-10-02 19:17:39 -0700 | [diff] [blame] | 215 | } |
Walter Jang | 3efae4a | 2015-02-18 11:12:00 -0800 | [diff] [blame] | 216 | |
Walter Jang | 31a74ad | 2015-10-02 19:17:39 -0700 | [diff] [blame] | 217 | private void setDefaultPhoto(MaterialPalette materialPalette) { |
Gary Mai | da20b47 | 2016-09-20 14:46:40 -0700 | [diff] [blame] | 218 | mIsNonDefaultPhotoBound = false; |
Gary Mai | ef6ceb2 | 2016-11-17 12:25:19 -0800 | [diff] [blame] | 219 | updatePhotoDescription(); |
Walter Jang | 31a74ad | 2015-10-02 19:17:39 -0700 | [diff] [blame] | 220 | EditorUiUtils.setDefaultPhoto(mPhotoImageView, getResources(), materialPalette); |
Walter Jang | 3efae4a | 2015-02-18 11:12:00 -0800 | [diff] [blame] | 221 | } |
| 222 | |
Gary Mai | ef6ceb2 | 2016-11-17 12:25:19 -0800 | [diff] [blame] | 223 | 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 Jang | 3efae4a | 2015-02-18 11:12:00 -0800 | [diff] [blame] | 229 | /** |
Walter Jang | 31a74ad | 2015-10-02 19:17:39 -0700 | [diff] [blame] | 230 | * Binds a full size photo loaded from the given Uri. |
Walter Jang | 3efae4a | 2015-02-18 11:12:00 -0800 | [diff] [blame] | 231 | */ |
| 232 | public void setFullSizedPhoto(Uri photoUri) { |
Walter Jang | 31a74ad | 2015-10-02 19:17:39 -0700 | [diff] [blame] | 233 | EditorUiUtils.loadPhoto(ContactPhotoManager.getInstance(getContext()), |
| 234 | mPhotoImageView, photoUri); |
| 235 | mIsNonDefaultPhotoBound = true; |
Gary Mai | ef6ceb2 | 2016-11-17 12:25:19 -0800 | [diff] [blame] | 236 | updatePhotoDescription(); |
Walter Jang | 31a74ad | 2015-10-02 19:17:39 -0700 | [diff] [blame] | 237 | } |
| 238 | |
| 239 | /** |
| 240 | * Removes the current bound photo bitmap. |
| 241 | */ |
| 242 | public void removePhoto() { |
Gary Mai | 1872418 | 2016-09-20 17:33:30 -0700 | [diff] [blame] | 243 | setDefaultPhoto(mMaterialPalette); |
Walter Jang | 3efae4a | 2015-02-18 11:12:00 -0800 | [diff] [blame] | 244 | } |
| 245 | |
| 246 | @Override |
| 247 | public void onClick(View view) { |
Walter Jang | 31a74ad | 2015-10-02 19:17:39 -0700 | [diff] [blame] | 248 | if (mListener != null) { |
| 249 | mListener.onPhotoEditorViewClicked(); |
Walter Jang | 3efae4a | 2015-02-18 11:12:00 -0800 | [diff] [blame] | 250 | } |
| 251 | } |
| 252 | } |