1 /* 2 * Copyright (C) 2019 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.providers.media.util; 18 19 import android.content.ClipDescription; 20 import android.mtp.MtpConstants; 21 import android.provider.MediaStore.Files.FileColumns; 22 import android.webkit.MimeTypeMap; 23 24 import androidx.annotation.NonNull; 25 import androidx.annotation.Nullable; 26 27 import java.io.File; 28 import java.util.Locale; 29 import java.util.Objects; 30 31 public class MimeUtils { 32 33 /** 34 * Resolve the MIME type of the given file, returning 35 * {@code application/octet-stream} if the type cannot be determined. 36 */ resolveMimeType(@onNull File file)37 public static @NonNull String resolveMimeType(@NonNull File file) { 38 final String extension = FileUtils.extractFileExtension(file.getPath()); 39 if (extension == null) return ClipDescription.MIMETYPE_UNKNOWN; 40 41 final String mimeType = MimeTypeMap.getSingleton() 42 .getMimeTypeFromExtension(extension.toLowerCase(Locale.ROOT)); 43 if (mimeType == null) return ClipDescription.MIMETYPE_UNKNOWN; 44 45 return mimeType; 46 } 47 48 /** 49 * Resolve the {@link FileColumns#MEDIA_TYPE} of the given MIME type. This 50 * carefully checks for more specific types before generic ones, such as 51 * treating {@code audio/mpegurl} as a playlist instead of an audio file. 52 */ resolveMediaType(@onNull String mimeType)53 public static int resolveMediaType(@NonNull String mimeType) { 54 if (isPlaylistMimeType(mimeType)) { 55 return FileColumns.MEDIA_TYPE_PLAYLIST; 56 } else if (isSubtitleMimeType(mimeType)) { 57 return FileColumns.MEDIA_TYPE_SUBTITLE; 58 } else if (isAudioMimeType(mimeType)) { 59 return FileColumns.MEDIA_TYPE_AUDIO; 60 } else if (isVideoMimeType(mimeType)) { 61 return FileColumns.MEDIA_TYPE_VIDEO; 62 } else if (isImageMimeType(mimeType)) { 63 return FileColumns.MEDIA_TYPE_IMAGE; 64 } else if (isDocumentMimeType(mimeType)) { 65 return FileColumns.MEDIA_TYPE_DOCUMENT; 66 } else { 67 return FileColumns.MEDIA_TYPE_NONE; 68 } 69 } 70 71 /** 72 * Resolve the {@link FileColumns#FORMAT} of the given MIME type. Note that 73 * since this column isn't public API, we're okay only getting very rough 74 * values in place, and it's not worthwhile to build out complex matching. 75 */ resolveFormatCode(@ullable String mimeType)76 public static int resolveFormatCode(@Nullable String mimeType) { 77 final int mediaType = resolveMediaType(mimeType); 78 switch (mediaType) { 79 case FileColumns.MEDIA_TYPE_AUDIO: 80 return MtpConstants.FORMAT_UNDEFINED_AUDIO; 81 case FileColumns.MEDIA_TYPE_VIDEO: 82 return MtpConstants.FORMAT_UNDEFINED_VIDEO; 83 case FileColumns.MEDIA_TYPE_IMAGE: 84 return MtpConstants.FORMAT_DEFINED; 85 default: 86 return MtpConstants.FORMAT_UNDEFINED; 87 } 88 } 89 extractPrimaryType(@onNull String mimeType)90 public static @NonNull String extractPrimaryType(@NonNull String mimeType) { 91 final int slash = mimeType.indexOf('/'); 92 if (slash == -1) { 93 throw new IllegalArgumentException(); 94 } 95 return mimeType.substring(0, slash); 96 } 97 isAudioMimeType(@ullable String mimeType)98 public static boolean isAudioMimeType(@Nullable String mimeType) { 99 if (mimeType == null) return false; 100 return StringUtils.startsWithIgnoreCase(mimeType, "audio/"); 101 } 102 isVideoMimeType(@ullable String mimeType)103 public static boolean isVideoMimeType(@Nullable String mimeType) { 104 if (mimeType == null) return false; 105 return StringUtils.startsWithIgnoreCase(mimeType, "video/"); 106 } 107 isImageMimeType(@ullable String mimeType)108 public static boolean isImageMimeType(@Nullable String mimeType) { 109 if (mimeType == null) return false; 110 return StringUtils.startsWithIgnoreCase(mimeType, "image/"); 111 } 112 isImageOrVideoMediaType(int mediaType)113 public static boolean isImageOrVideoMediaType(int mediaType) { 114 return FileColumns.MEDIA_TYPE_IMAGE == mediaType 115 || FileColumns.MEDIA_TYPE_VIDEO == mediaType; 116 } 117 isPlaylistMimeType(@ullable String mimeType)118 public static boolean isPlaylistMimeType(@Nullable String mimeType) { 119 if (mimeType == null) return false; 120 switch (mimeType.toLowerCase(Locale.ROOT)) { 121 case "application/vnd.apple.mpegurl": 122 case "application/vnd.ms-wpl": 123 case "application/x-extension-smpl": 124 case "application/x-mpegurl": 125 case "application/xspf+xml": 126 case "audio/mpegurl": 127 case "audio/x-mpegurl": 128 case "audio/x-scpls": 129 return true; 130 default: 131 return false; 132 } 133 } 134 isSubtitleMimeType(@ullable String mimeType)135 public static boolean isSubtitleMimeType(@Nullable String mimeType) { 136 if (mimeType == null) return false; 137 switch (mimeType.toLowerCase(Locale.ROOT)) { 138 case "application/lrc": 139 case "application/smil+xml": 140 case "application/ttml+xml": 141 case "application/x-extension-cap": 142 case "application/x-extension-srt": 143 case "application/x-extension-sub": 144 case "application/x-extension-vtt": 145 case "application/x-subrip": 146 case "text/vtt": 147 return true; 148 default: 149 return false; 150 } 151 } 152 isDocumentMimeType(@ullable String mimeType)153 public static boolean isDocumentMimeType(@Nullable String mimeType) { 154 if (mimeType == null) return false; 155 156 if (StringUtils.startsWithIgnoreCase(mimeType, "text/")) return true; 157 158 switch (mimeType.toLowerCase(Locale.ROOT)) { 159 case "application/epub+zip": 160 case "application/msword": 161 case "application/pdf": 162 case "application/rtf": 163 case "application/vnd.ms-excel": 164 case "application/vnd.ms-excel.addin.macroenabled.12": 165 case "application/vnd.ms-excel.sheet.binary.macroenabled.12": 166 case "application/vnd.ms-excel.sheet.macroenabled.12": 167 case "application/vnd.ms-excel.template.macroenabled.12": 168 case "application/vnd.ms-powerpoint": 169 case "application/vnd.ms-powerpoint.addin.macroenabled.12": 170 case "application/vnd.ms-powerpoint.presentation.macroenabled.12": 171 case "application/vnd.ms-powerpoint.slideshow.macroenabled.12": 172 case "application/vnd.ms-powerpoint.template.macroenabled.12": 173 case "application/vnd.ms-word.document.macroenabled.12": 174 case "application/vnd.ms-word.template.macroenabled.12": 175 case "application/vnd.oasis.opendocument.chart": 176 case "application/vnd.oasis.opendocument.database": 177 case "application/vnd.oasis.opendocument.formula": 178 case "application/vnd.oasis.opendocument.graphics": 179 case "application/vnd.oasis.opendocument.graphics-template": 180 case "application/vnd.oasis.opendocument.presentation": 181 case "application/vnd.oasis.opendocument.presentation-template": 182 case "application/vnd.oasis.opendocument.spreadsheet": 183 case "application/vnd.oasis.opendocument.spreadsheet-template": 184 case "application/vnd.oasis.opendocument.text": 185 case "application/vnd.oasis.opendocument.text-master": 186 case "application/vnd.oasis.opendocument.text-template": 187 case "application/vnd.oasis.opendocument.text-web": 188 case "application/vnd.openxmlformats-officedocument.presentationml.presentation": 189 case "application/vnd.openxmlformats-officedocument.presentationml.slideshow": 190 case "application/vnd.openxmlformats-officedocument.presentationml.template": 191 case "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": 192 case "application/vnd.openxmlformats-officedocument.spreadsheetml.template": 193 case "application/vnd.openxmlformats-officedocument.wordprocessingml.document": 194 case "application/vnd.openxmlformats-officedocument.wordprocessingml.template": 195 case "application/vnd.stardivision.calc": 196 case "application/vnd.stardivision.chart": 197 case "application/vnd.stardivision.draw": 198 case "application/vnd.stardivision.impress": 199 case "application/vnd.stardivision.impress-packed": 200 case "application/vnd.stardivision.mail": 201 case "application/vnd.stardivision.math": 202 case "application/vnd.stardivision.writer": 203 case "application/vnd.stardivision.writer-global": 204 case "application/vnd.sun.xml.calc": 205 case "application/vnd.sun.xml.calc.template": 206 case "application/vnd.sun.xml.draw": 207 case "application/vnd.sun.xml.draw.template": 208 case "application/vnd.sun.xml.impress": 209 case "application/vnd.sun.xml.impress.template": 210 case "application/vnd.sun.xml.math": 211 case "application/vnd.sun.xml.writer": 212 case "application/vnd.sun.xml.writer.global": 213 case "application/vnd.sun.xml.writer.template": 214 case "application/x-mspublisher": 215 return true; 216 default: 217 return false; 218 } 219 } 220 } 221