1 /*
2  * Copyright (C) 2016 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.internal.util;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.content.ContentResolver.MimeTypeInfo;
22 import android.content.res.Resources;
23 import android.graphics.drawable.Icon;
24 import android.text.TextUtils;
25 import android.util.ArrayMap;
26 
27 import com.android.internal.R;
28 import com.android.internal.annotations.GuardedBy;
29 
30 import libcore.content.type.MimeMap;
31 
32 import java.util.Locale;
33 import java.util.Objects;
34 
35 public class MimeIconUtils {
36     @GuardedBy("sCache")
37     private static final ArrayMap<String, MimeTypeInfo> sCache = new ArrayMap<>();
38 
buildTypeInfo(String mimeType, int iconId, int labelId, int extLabelId)39     private static MimeTypeInfo buildTypeInfo(String mimeType, int iconId,
40             int labelId, int extLabelId) {
41         final Resources res = Resources.getSystem();
42 
43         // If this MIME type has an extension, customize the label
44         final CharSequence label;
45         final String ext = MimeMap.getDefault().guessExtensionFromMimeType(mimeType);
46         if (!TextUtils.isEmpty(ext) && extLabelId != -1) {
47             label = res.getString(extLabelId, ext.toUpperCase(Locale.US));
48         } else {
49             label = res.getString(labelId);
50         }
51 
52         return new MimeTypeInfo(Icon.createWithResource(res, iconId), label, label);
53     }
54 
buildTypeInfo(@onNull String mimeType)55     private static @Nullable MimeTypeInfo buildTypeInfo(@NonNull String mimeType) {
56         switch (mimeType) {
57             case "inode/directory":
58             case "vnd.android.document/directory":
59                 return buildTypeInfo(mimeType, R.drawable.ic_doc_folder,
60                         R.string.mime_type_folder, -1);
61 
62             case "application/vnd.android.package-archive":
63                 return buildTypeInfo(mimeType, R.drawable.ic_doc_apk,
64                         R.string.mime_type_apk, -1);
65 
66             case "application/pgp-keys":
67             case "application/pgp-signature":
68             case "application/x-pkcs12":
69             case "application/x-pkcs7-certreqresp":
70             case "application/x-pkcs7-crl":
71             case "application/x-x509-ca-cert":
72             case "application/x-x509-user-cert":
73             case "application/x-pkcs7-certificates":
74             case "application/x-pkcs7-mime":
75             case "application/x-pkcs7-signature":
76                 return buildTypeInfo(mimeType, R.drawable.ic_doc_certificate,
77                         R.string.mime_type_generic, R.string.mime_type_generic_ext);
78 
79             case "application/rdf+xml":
80             case "application/rss+xml":
81             case "application/x-object":
82             case "application/xhtml+xml":
83             case "text/css":
84             case "text/html":
85             case "text/xml":
86             case "text/x-c++hdr":
87             case "text/x-c++src":
88             case "text/x-chdr":
89             case "text/x-csrc":
90             case "text/x-dsrc":
91             case "text/x-csh":
92             case "text/x-haskell":
93             case "text/x-java":
94             case "text/x-literate-haskell":
95             case "text/x-pascal":
96             case "text/x-tcl":
97             case "text/x-tex":
98             case "application/x-latex":
99             case "application/x-texinfo":
100             case "application/atom+xml":
101             case "application/ecmascript":
102             case "application/json":
103             case "application/javascript":
104             case "application/xml":
105             case "text/javascript":
106             case "application/x-javascript":
107                 return buildTypeInfo(mimeType, R.drawable.ic_doc_codes,
108                         R.string.mime_type_document, R.string.mime_type_document_ext);
109 
110             case "application/mac-binhex40":
111             case "application/rar":
112             case "application/zip":
113             case "application/x-apple-diskimage":
114             case "application/x-debian-package":
115             case "application/x-gtar":
116             case "application/x-iso9660-image":
117             case "application/x-lha":
118             case "application/x-lzh":
119             case "application/x-lzx":
120             case "application/x-stuffit":
121             case "application/x-tar":
122             case "application/x-webarchive":
123             case "application/x-webarchive-xml":
124             case "application/gzip":
125             case "application/x-7z-compressed":
126             case "application/x-deb":
127             case "application/x-rar-compressed":
128                 return buildTypeInfo(mimeType, R.drawable.ic_doc_compressed,
129                         R.string.mime_type_compressed, R.string.mime_type_compressed_ext);
130 
131             case "text/x-vcard":
132             case "text/vcard":
133                 return buildTypeInfo(mimeType, R.drawable.ic_doc_contact,
134                         R.string.mime_type_generic, R.string.mime_type_generic_ext);
135 
136             case "text/calendar":
137             case "text/x-vcalendar":
138                 return buildTypeInfo(mimeType, R.drawable.ic_doc_event,
139                         R.string.mime_type_generic, R.string.mime_type_generic_ext);
140 
141             case "application/x-font":
142             case "application/font-woff":
143             case "application/x-font-woff":
144             case "application/x-font-ttf":
145                 return buildTypeInfo(mimeType, R.drawable.ic_doc_font,
146                         R.string.mime_type_generic, R.string.mime_type_generic_ext);
147 
148             case "application/vnd.oasis.opendocument.graphics":
149             case "application/vnd.oasis.opendocument.graphics-template":
150             case "application/vnd.oasis.opendocument.image":
151             case "application/vnd.stardivision.draw":
152             case "application/vnd.sun.xml.draw":
153             case "application/vnd.sun.xml.draw.template":
154             case "application/vnd.google-apps.drawing":
155                 return buildTypeInfo(mimeType, R.drawable.ic_doc_image,
156                         R.string.mime_type_image, R.string.mime_type_image_ext);
157 
158             case "application/pdf":
159                 return buildTypeInfo(mimeType, R.drawable.ic_doc_pdf,
160                         R.string.mime_type_document, R.string.mime_type_document_ext);
161 
162             case "application/vnd.stardivision.impress":
163             case "application/vnd.sun.xml.impress":
164             case "application/vnd.sun.xml.impress.template":
165             case "application/x-kpresenter":
166             case "application/vnd.oasis.opendocument.presentation":
167             case "application/vnd.google-apps.presentation":
168                 return buildTypeInfo(mimeType, R.drawable.ic_doc_presentation,
169                         R.string.mime_type_presentation, R.string.mime_type_presentation_ext);
170 
171             case "application/vnd.oasis.opendocument.spreadsheet":
172             case "application/vnd.oasis.opendocument.spreadsheet-template":
173             case "application/vnd.stardivision.calc":
174             case "application/vnd.sun.xml.calc":
175             case "application/vnd.sun.xml.calc.template":
176             case "application/x-kspread":
177             case "application/vnd.google-apps.spreadsheet":
178                 return buildTypeInfo(mimeType, R.drawable.ic_doc_spreadsheet,
179                         R.string.mime_type_spreadsheet, R.string.mime_type_spreadsheet_ext);
180 
181             case "application/vnd.oasis.opendocument.text":
182             case "application/vnd.oasis.opendocument.text-master":
183             case "application/vnd.oasis.opendocument.text-template":
184             case "application/vnd.oasis.opendocument.text-web":
185             case "application/vnd.stardivision.writer":
186             case "application/vnd.stardivision.writer-global":
187             case "application/vnd.sun.xml.writer":
188             case "application/vnd.sun.xml.writer.global":
189             case "application/vnd.sun.xml.writer.template":
190             case "application/x-abiword":
191             case "application/x-kword":
192             case "application/vnd.google-apps.document":
193                 return buildTypeInfo(mimeType, R.drawable.ic_doc_document,
194                         R.string.mime_type_document, R.string.mime_type_document_ext);
195 
196             case "application/x-quicktimeplayer":
197             case "application/x-shockwave-flash":
198                 return buildTypeInfo(mimeType, R.drawable.ic_doc_video,
199                         R.string.mime_type_video, R.string.mime_type_video_ext);
200 
201             case "application/msword":
202             case "application/vnd.openxmlformats-officedocument.wordprocessingml.document":
203             case "application/vnd.openxmlformats-officedocument.wordprocessingml.template":
204                 return buildTypeInfo(mimeType, R.drawable.ic_doc_word,
205                         R.string.mime_type_document, R.string.mime_type_document_ext);
206 
207             case "application/vnd.ms-excel":
208             case "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":
209             case "application/vnd.openxmlformats-officedocument.spreadsheetml.template":
210                 return buildTypeInfo(mimeType, R.drawable.ic_doc_excel,
211                         R.string.mime_type_spreadsheet, R.string.mime_type_spreadsheet_ext);
212 
213             case "application/vnd.ms-powerpoint":
214             case "application/vnd.openxmlformats-officedocument.presentationml.presentation":
215             case "application/vnd.openxmlformats-officedocument.presentationml.template":
216             case "application/vnd.openxmlformats-officedocument.presentationml.slideshow":
217                 return buildTypeInfo(mimeType, R.drawable.ic_doc_powerpoint,
218                         R.string.mime_type_presentation, R.string.mime_type_presentation_ext);
219 
220             default:
221                 return buildGenericTypeInfo(mimeType);
222         }
223     }
224 
buildGenericTypeInfo(@onNull String mimeType)225     private static @Nullable MimeTypeInfo buildGenericTypeInfo(@NonNull String mimeType) {
226         // Look for partial matches
227         if (mimeType.startsWith("audio/")) {
228             return buildTypeInfo(mimeType, R.drawable.ic_doc_audio,
229                     R.string.mime_type_audio, R.string.mime_type_audio_ext);
230         } else if (mimeType.startsWith("video/")) {
231             return buildTypeInfo(mimeType, R.drawable.ic_doc_video,
232                     R.string.mime_type_video, R.string.mime_type_video_ext);
233         } else if (mimeType.startsWith("image/")) {
234             return buildTypeInfo(mimeType, R.drawable.ic_doc_image,
235                     R.string.mime_type_image, R.string.mime_type_image_ext);
236         } else if (mimeType.startsWith("text/")) {
237             return buildTypeInfo(mimeType, R.drawable.ic_doc_text,
238                     R.string.mime_type_document, R.string.mime_type_document_ext);
239         }
240 
241         // As one last-ditch effort, try "bouncing" the MIME type through its
242         // default extension. This handles cases like "application/x-flac" to
243         // ".flac" to "audio/flac".
244         MimeMap mimeMap = MimeMap.getDefault();
245         final String bouncedMimeType = mimeMap
246                 .guessMimeTypeFromExtension(mimeMap.guessExtensionFromMimeType(mimeType));
247         if (bouncedMimeType != null && !Objects.equals(mimeType, bouncedMimeType)) {
248             return buildTypeInfo(bouncedMimeType);
249         }
250 
251         // Worst case, return a generic file
252         return buildTypeInfo(mimeType, R.drawable.ic_doc_generic,
253                 R.string.mime_type_generic, R.string.mime_type_generic_ext);
254     }
255 
getTypeInfo(@onNull String mimeType)256     public static @NonNull MimeTypeInfo getTypeInfo(@NonNull String mimeType) {
257         // Normalize MIME type
258         mimeType = mimeType.toLowerCase(Locale.US);
259 
260         synchronized (sCache) {
261             MimeTypeInfo res = sCache.get(mimeType);
262             if (res == null) {
263                 res = buildTypeInfo(mimeType);
264                 sCache.put(mimeType, res);
265             }
266             return res;
267         }
268     }
269 }
270