1/*
2 * Copyright (c) 2018, Daniel Gultsch All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without modification,
5 * are permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright notice, this
8 * list of conditions and the following disclaimer.
9 *
10 * 2. Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation and/or
12 * other materials provided with the distribution.
13 *
14 * 3. Neither the name of the copyright holder nor the names of its contributors
15 * may be used to endorse or promote products derived from this software without
16 * specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
22 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30package eu.siacs.conversations.ui.util;
31
32import android.content.ClipData;
33import android.content.Context;
34import android.content.Intent;
35import android.net.Uri;
36import android.os.Parcel;
37import android.os.Parcelable;
38
39import androidx.annotation.NonNull;
40
41import com.google.common.base.MoreObjects;
42
43import eu.siacs.conversations.entities.Message;
44import eu.siacs.conversations.utils.MimeUtils;
45
46import java.io.File;
47import java.util.ArrayList;
48import java.util.Collections;
49import java.util.List;
50import java.util.UUID;
51
52public class Attachment implements Parcelable {
53
54 Attachment(Parcel in) {
55 uri = in.readParcelable(Uri.class.getClassLoader());
56 mime = in.readString();
57 uuid = UUID.fromString(in.readString());
58 type = Type.valueOf(in.readString());
59 }
60
61 @Override
62 public void writeToParcel(Parcel dest, int flags) {
63 dest.writeParcelable(uri, flags);
64 dest.writeString(mime);
65 dest.writeString(uuid.toString());
66 dest.writeString(type.toString());
67 }
68
69 @Override
70 public int describeContents() {
71 return 0;
72 }
73
74 public static final Creator<Attachment> CREATOR =
75 new Creator<Attachment>() {
76 @Override
77 public Attachment createFromParcel(Parcel in) {
78 return new Attachment(in);
79 }
80
81 @Override
82 public Attachment[] newArray(int size) {
83 return new Attachment[size];
84 }
85 };
86
87 public String getMime() {
88 return mime;
89 }
90
91 public Type getType() {
92 return type;
93 }
94
95 @NonNull
96 @Override
97 public String toString() {
98 return MoreObjects.toStringHelper(this)
99 .add("uri", uri)
100 .add("type", type)
101 .add("uuid", uuid)
102 .add("mime", mime)
103 .toString();
104 }
105
106 public enum Type {
107 FILE,
108 IMAGE,
109 LOCATION,
110 RECORDING
111 }
112
113 private final Uri uri;
114 private final Type type;
115 private final UUID uuid;
116 private final String mime;
117
118 private Attachment(UUID uuid, Uri uri, Type type, String mime) {
119 this.uri = uri;
120 this.type = type;
121 this.mime = mime;
122 this.uuid = uuid;
123 }
124
125 private Attachment(Uri uri, Type type, String mime) {
126 this.uri = uri;
127 this.type = type;
128 this.mime = mime;
129 this.uuid = UUID.randomUUID();
130 }
131
132 public static boolean canBeSendInBand(final List<Attachment> attachments) {
133 for (final Attachment attachment : attachments) {
134 if (attachment.type != Type.LOCATION) {
135 return false;
136 }
137 }
138 return true;
139 }
140
141 public static List<Attachment> of(final Context context, Uri uri, Type type) {
142 final String mime =
143 type == Type.LOCATION ? null : MimeUtils.guessMimeTypeFromUri(context, uri);
144 return Collections.singletonList(new Attachment(uri, type, mime));
145 }
146
147 public static Attachment of(final Message message) {
148 final UUID uuid = UUID.fromString(message.getUuid());
149 if (message.isGeoUri()) {
150 return new Attachment(uuid, Uri.EMPTY, Type.LOCATION, null);
151 }
152 final String mime = message.getMimeType();
153 if (MimeUtils.AMBIGUOUS_CONTAINER_FORMATS.contains(mime)) {
154 final Message.FileParams fileParams = message.getFileParams();
155 if (fileParams.width > 0 && fileParams.height > 0) {
156 return new Attachment(uuid, Uri.EMPTY, Type.FILE, "video/*");
157 } else if (fileParams.runtime > 0) {
158 return new Attachment(uuid, Uri.EMPTY, Type.FILE, "audio/*");
159 } else {
160 return new Attachment(uuid, Uri.EMPTY, Type.FILE, "application/octet-stream");
161 }
162 }
163 return new Attachment(uuid, Uri.EMPTY, Type.FILE, mime);
164 }
165
166 public static List<Attachment> of(final Context context, List<Uri> uris, final String type) {
167 final List<Attachment> attachments = new ArrayList<>();
168 for (final Uri uri : uris) {
169 if (uri == null) {
170 continue;
171 }
172 final String mime = MimeUtils.guessMimeTypeFromUriAndMime(context, uri, type);
173 attachments.add(
174 new Attachment(
175 uri, mime != null && isImage(mime) ? Type.IMAGE : Type.FILE, mime));
176 }
177 return attachments;
178 }
179
180 public static Attachment of(UUID uuid, final File file, String mime) {
181 return new Attachment(
182 uuid,
183 Uri.fromFile(file),
184 mime != null && (isImage(mime) || mime.startsWith("video/"))
185 ? Type.IMAGE
186 : Type.FILE,
187 mime);
188 }
189
190 public static List<Attachment> extractAttachments(
191 final Context context, final Intent intent, Type type) {
192 List<Attachment> uris = new ArrayList<>();
193 if (intent == null) {
194 return uris;
195 }
196 final String contentType = intent.getType();
197 final Uri data = intent.getData();
198 if (data == null) {
199 final ClipData clipData = intent.getClipData();
200 if (clipData != null) {
201 for (int i = 0; i < clipData.getItemCount(); ++i) {
202 final Uri uri = clipData.getItemAt(i).getUri();
203 final String mime =
204 MimeUtils.guessMimeTypeFromUriAndMime(context, uri, contentType);
205 uris.add(new Attachment(uri, type, mime));
206 }
207 }
208 } else {
209 final String mime = MimeUtils.guessMimeTypeFromUriAndMime(context, data, contentType);
210 uris.add(new Attachment(data, type, mime));
211 }
212 return uris;
213 }
214
215 public boolean renderThumbnail() {
216 return type == Type.IMAGE
217 || (type == Type.FILE && mime != null && renderFileThumbnail(mime));
218 }
219
220 private static boolean renderFileThumbnail(final String mime) {
221 return mime.startsWith("video/") || isImage(mime) || "application/pdf".equals(mime);
222 }
223
224 public Uri getUri() {
225 return uri;
226 }
227
228 public UUID getUuid() {
229 return uuid;
230 }
231
232 private static boolean isImage(final String mime) {
233 return mime.startsWith("image/");
234 }
235}