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 package com.android.messaging.ui; 17 18 import android.content.Context; 19 import android.content.res.Resources; 20 import android.graphics.Rect; 21 import android.net.Uri; 22 import androidx.annotation.Nullable; 23 import android.text.TextUtils; 24 import android.view.LayoutInflater; 25 import android.view.View; 26 import android.view.View.OnClickListener; 27 import android.view.View.OnLongClickListener; 28 import android.view.ViewGroup; 29 import android.widget.FrameLayout.LayoutParams; 30 import android.widget.ImageView; 31 import android.widget.TextView; 32 33 import com.android.messaging.R; 34 import com.android.messaging.datamodel.DataModel; 35 import com.android.messaging.datamodel.data.MessagePartData; 36 import com.android.messaging.datamodel.data.PendingAttachmentData; 37 import com.android.messaging.datamodel.data.PersonItemData; 38 import com.android.messaging.datamodel.data.VCardContactItemData; 39 import com.android.messaging.datamodel.media.FileImageRequestDescriptor; 40 import com.android.messaging.datamodel.media.ImageRequest; 41 import com.android.messaging.datamodel.media.ImageRequestDescriptor; 42 import com.android.messaging.datamodel.media.UriImageRequestDescriptor; 43 import com.android.messaging.ui.MultiAttachmentLayout.OnAttachmentClickListener; 44 import com.android.messaging.ui.PersonItemView.PersonItemViewListener; 45 import com.android.messaging.util.Assert; 46 import com.android.messaging.util.ContentType; 47 import com.android.messaging.util.ImageUtils; 48 import com.android.messaging.util.UiUtils; 49 import com.android.messaging.util.UriUtil; 50 51 /** 52 * A view factory that creates previews for single/multiple attachments. 53 */ 54 public class AttachmentPreviewFactory { 55 /** Standalone attachment preview */ 56 public static final int TYPE_SINGLE = 1; 57 58 /** Attachment preview displayed in a multi-attachment layout */ 59 public static final int TYPE_MULTIPLE = 2; 60 61 /** Attachment preview displayed in the attachment chooser grid view */ 62 public static final int TYPE_CHOOSER_GRID = 3; 63 createAttachmentPreview(final LayoutInflater layoutInflater, final MessagePartData attachmentData, final ViewGroup parent, final int viewType, final boolean startImageRequest, @Nullable final OnAttachmentClickListener clickListener)64 public static View createAttachmentPreview(final LayoutInflater layoutInflater, 65 final MessagePartData attachmentData, final ViewGroup parent, 66 final int viewType, final boolean startImageRequest, 67 @Nullable final OnAttachmentClickListener clickListener) { 68 final String contentType = attachmentData.getContentType(); 69 View attachmentView = null; 70 if (attachmentData instanceof PendingAttachmentData) { 71 attachmentView = createPendingAttachmentPreview(layoutInflater, parent, 72 (PendingAttachmentData) attachmentData); 73 } else if (ContentType.isImageType(contentType)) { 74 attachmentView = createImagePreview(layoutInflater, attachmentData, parent, viewType, 75 startImageRequest); 76 } else if (ContentType.isAudioType(contentType)) { 77 attachmentView = createAudioPreview(layoutInflater, attachmentData, parent, viewType); 78 } else if (ContentType.isVideoType(contentType)) { 79 attachmentView = createVideoPreview(layoutInflater, attachmentData, parent, viewType); 80 } else if (ContentType.isVCardType(contentType)) { 81 attachmentView = createVCardPreview(layoutInflater, attachmentData, parent, viewType); 82 } else { 83 Assert.fail("unsupported attachment type: " + contentType); 84 return null; 85 } 86 87 // Some views have a caption, set the text/visibility if one exists 88 final TextView captionView = (TextView) attachmentView.findViewById(R.id.caption); 89 if (captionView != null) { 90 final String caption = attachmentData.getText(); 91 captionView.setVisibility(TextUtils.isEmpty(caption) ? View.GONE : View.VISIBLE); 92 captionView.setText(caption); 93 } 94 95 if (attachmentView != null && clickListener != null) { 96 attachmentView.setOnClickListener(new OnClickListener() { 97 @Override 98 public void onClick(final View view) { 99 final Rect bounds = UiUtils.getMeasuredBoundsOnScreen(view); 100 clickListener.onAttachmentClick(attachmentData, bounds, 101 false /* longPress */); 102 } 103 }); 104 attachmentView.setOnLongClickListener(new OnLongClickListener() { 105 @Override 106 public boolean onLongClick(final View view) { 107 final Rect bounds = UiUtils.getMeasuredBoundsOnScreen(view); 108 return clickListener.onAttachmentClick(attachmentData, bounds, 109 true /* longPress */); 110 } 111 }); 112 } 113 return attachmentView; 114 } 115 createMultiplePreview(final Context context, final OnAttachmentClickListener listener)116 public static MultiAttachmentLayout createMultiplePreview(final Context context, 117 final OnAttachmentClickListener listener) { 118 final MultiAttachmentLayout multiAttachmentLayout = 119 new MultiAttachmentLayout(context, null); 120 final ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams( 121 LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); 122 multiAttachmentLayout.setLayoutParams(layoutParams); 123 multiAttachmentLayout.setOnAttachmentClickListener(listener); 124 return multiAttachmentLayout; 125 } 126 getImageRequestDescriptorForAttachment( final MessagePartData attachmentData, final int desiredWidth, final int desiredHeight)127 public static ImageRequestDescriptor getImageRequestDescriptorForAttachment( 128 final MessagePartData attachmentData, final int desiredWidth, final int desiredHeight) { 129 final Uri uri = attachmentData.getContentUri(); 130 final String contentType = attachmentData.getContentType(); 131 if (ContentType.isImageType(contentType)) { 132 final String filePath = UriUtil.getFilePathFromUri(uri); 133 if (filePath != null) { 134 return new FileImageRequestDescriptor(filePath, desiredWidth, desiredHeight, 135 attachmentData.getWidth(), attachmentData.getHeight(), 136 false /* canUseThumbnail */, true /* allowCompression */, 137 false /* isStatic */); 138 } else { 139 return new UriImageRequestDescriptor(uri, desiredWidth, desiredHeight, 140 attachmentData.getWidth(), attachmentData.getHeight(), 141 true /* allowCompression */, false /* isStatic */, false /*cropToCircle*/, 142 ImageUtils.DEFAULT_CIRCLE_BACKGROUND_COLOR /* circleBackgroundColor */, 143 ImageUtils.DEFAULT_CIRCLE_STROKE_COLOR /* circleStrokeColor */); 144 } 145 } 146 return null; 147 } 148 createImagePreview(final LayoutInflater layoutInflater, final MessagePartData attachmentData, final ViewGroup parent, final int viewType, final boolean startImageRequest)149 private static View createImagePreview(final LayoutInflater layoutInflater, 150 final MessagePartData attachmentData, final ViewGroup parent, 151 final int viewType, final boolean startImageRequest) { 152 int layoutId = R.layout.attachment_single_image; 153 switch (viewType) { 154 case AttachmentPreviewFactory.TYPE_SINGLE: 155 layoutId = R.layout.attachment_single_image; 156 break; 157 case AttachmentPreviewFactory.TYPE_MULTIPLE: 158 layoutId = R.layout.attachment_multiple_image; 159 break; 160 case AttachmentPreviewFactory.TYPE_CHOOSER_GRID: 161 layoutId = R.layout.attachment_chooser_image; 162 break; 163 default: 164 Assert.fail("unsupported attachment view type!"); 165 break; 166 } 167 final View view = layoutInflater.inflate(layoutId, parent, false /* attachToRoot */); 168 final AsyncImageView imageView = (AsyncImageView) view.findViewById( 169 R.id.attachment_image_view); 170 int maxWidth = imageView.getMaxWidth(); 171 int maxHeight = imageView.getMaxHeight(); 172 if (viewType == TYPE_CHOOSER_GRID) { 173 final Resources resources = layoutInflater.getContext().getResources(); 174 maxWidth = maxHeight = resources.getDimensionPixelSize( 175 R.dimen.attachment_grid_image_cell_size); 176 } 177 if (maxWidth <= 0 || maxWidth == Integer.MAX_VALUE) { 178 maxWidth = ImageRequest.UNSPECIFIED_SIZE; 179 } 180 if (maxHeight <= 0 || maxHeight == Integer.MAX_VALUE) { 181 maxHeight = ImageRequest.UNSPECIFIED_SIZE; 182 } 183 if (startImageRequest) { 184 imageView.setImageResourceId(getImageRequestDescriptorForAttachment(attachmentData, 185 maxWidth, maxHeight)); 186 } 187 imageView.setContentDescription( 188 parent.getResources().getString(R.string.message_image_content_description)); 189 return view; 190 } 191 createPendingAttachmentPreview(final LayoutInflater layoutInflater, final ViewGroup parent, final PendingAttachmentData attachmentData)192 private static View createPendingAttachmentPreview(final LayoutInflater layoutInflater, 193 final ViewGroup parent, final PendingAttachmentData attachmentData) { 194 final View pendingItemView = layoutInflater.inflate(R.layout.attachment_pending_item, 195 parent, false); 196 final ImageView imageView = (ImageView) 197 pendingItemView.findViewById(R.id.pending_item_view); 198 final ViewGroup.LayoutParams layoutParams = imageView.getLayoutParams(); 199 final int defaultSize = layoutInflater.getContext().getResources().getDimensionPixelSize( 200 R.dimen.pending_attachment_size); 201 layoutParams.width = attachmentData.getWidth() == MessagePartData.UNSPECIFIED_SIZE ? 202 defaultSize : attachmentData.getWidth(); 203 layoutParams.height = attachmentData.getHeight() == MessagePartData.UNSPECIFIED_SIZE ? 204 defaultSize : attachmentData.getHeight(); 205 return pendingItemView; 206 } 207 createVCardPreview(final LayoutInflater layoutInflater, final MessagePartData attachmentData, final ViewGroup parent, final int viewType)208 private static View createVCardPreview(final LayoutInflater layoutInflater, 209 final MessagePartData attachmentData, final ViewGroup parent, 210 final int viewType) { 211 int layoutId = R.layout.attachment_single_vcard; 212 switch (viewType) { 213 case AttachmentPreviewFactory.TYPE_SINGLE: 214 layoutId = R.layout.attachment_single_vcard; 215 break; 216 case AttachmentPreviewFactory.TYPE_MULTIPLE: 217 layoutId = R.layout.attachment_multiple_vcard; 218 break; 219 case AttachmentPreviewFactory.TYPE_CHOOSER_GRID: 220 layoutId = R.layout.attachment_chooser_vcard; 221 break; 222 default: 223 Assert.fail("unsupported attachment view type!"); 224 break; 225 } 226 final View view = layoutInflater.inflate(layoutId, parent, false /* attachToRoot */); 227 final PersonItemView vcardPreview = (PersonItemView) view.findViewById( 228 R.id.vcard_attachment_view); 229 vcardPreview.setAvatarOnly(viewType != AttachmentPreviewFactory.TYPE_SINGLE); 230 vcardPreview.bind(DataModel.get().createVCardContactItemData(layoutInflater.getContext(), 231 attachmentData)); 232 vcardPreview.setListener(new PersonItemViewListener() { 233 @Override 234 public void onPersonClicked(final PersonItemData data) { 235 Assert.isTrue(data instanceof VCardContactItemData); 236 final VCardContactItemData vCardData = (VCardContactItemData) data; 237 if (vCardData.hasValidVCard()) { 238 final Uri vCardUri = vCardData.getVCardUri(); 239 UIIntents.get().launchVCardDetailActivity(vcardPreview.getContext(), vCardUri); 240 } 241 } 242 243 @Override 244 public boolean onPersonLongClicked(final PersonItemData data) { 245 return false; 246 } 247 }); 248 return view; 249 } 250 createAudioPreview(final LayoutInflater layoutInflater, final MessagePartData attachmentData, final ViewGroup parent, final int viewType)251 private static View createAudioPreview(final LayoutInflater layoutInflater, 252 final MessagePartData attachmentData, final ViewGroup parent, 253 final int viewType) { 254 int layoutId = R.layout.attachment_single_audio; 255 switch (viewType) { 256 case AttachmentPreviewFactory.TYPE_SINGLE: 257 layoutId = R.layout.attachment_single_audio; 258 break; 259 case AttachmentPreviewFactory.TYPE_MULTIPLE: 260 layoutId = R.layout.attachment_multiple_audio; 261 break; 262 case AttachmentPreviewFactory.TYPE_CHOOSER_GRID: 263 layoutId = R.layout.attachment_chooser_audio; 264 break; 265 default: 266 Assert.fail("unsupported attachment view type!"); 267 break; 268 } 269 final View view = layoutInflater.inflate(layoutId, parent, false /* attachToRoot */); 270 final AudioAttachmentView audioView = (AudioAttachmentView) 271 view.findViewById(R.id.audio_attachment_view); 272 audioView.bindMessagePartData( 273 attachmentData, false /* incoming */, false /* showAsSelected */); 274 return view; 275 } 276 createVideoPreview(final LayoutInflater layoutInflater, final MessagePartData attachmentData, final ViewGroup parent, final int viewType)277 private static View createVideoPreview(final LayoutInflater layoutInflater, 278 final MessagePartData attachmentData, final ViewGroup parent, 279 final int viewType) { 280 int layoutId = R.layout.attachment_single_video; 281 switch (viewType) { 282 case AttachmentPreviewFactory.TYPE_SINGLE: 283 layoutId = R.layout.attachment_single_video; 284 break; 285 case AttachmentPreviewFactory.TYPE_MULTIPLE: 286 layoutId = R.layout.attachment_multiple_video; 287 break; 288 case AttachmentPreviewFactory.TYPE_CHOOSER_GRID: 289 layoutId = R.layout.attachment_chooser_video; 290 break; 291 default: 292 Assert.fail("unsupported attachment view type!"); 293 break; 294 } 295 final VideoThumbnailView videoThumbnail = (VideoThumbnailView) layoutInflater.inflate( 296 layoutId, parent, false /* attachToRoot */); 297 videoThumbnail.setSource(attachmentData, false /* incomingMessage */); 298 return videoThumbnail; 299 } 300 } 301