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