modify mime type detection for shared files

Daniel Gultsch created

add support for audiobooks

Change summary

src/main/java/eu/siacs/conversations/ui/adapter/MediaAdapter.java        |  2 
src/main/java/eu/siacs/conversations/ui/adapter/MediaPreviewAdapter.java |  2 
src/main/java/eu/siacs/conversations/utils/MimeUtils.java                | 75 
src/main/java/eu/siacs/conversations/utils/UIHelper.java                 |  2 
src/main/res/drawable/ic_play_lesson_black_24.xml                        |  6 
src/main/res/drawable/ic_play_lesson_white_48dp.xml                      |  6 
src/main/res/values/attrs.xml                                            |  1 
src/main/res/values/strings.xml                                          |  1 
src/main/res/values/themes.xml                                           |  2 
9 files changed, 64 insertions(+), 33 deletions(-)

Detailed changes

src/main/java/eu/siacs/conversations/ui/adapter/MediaAdapter.java 🔗

@@ -74,6 +74,8 @@ public class MediaAdapter extends RecyclerView.Adapter<MediaAdapter.MediaViewHol
             Log.d(Config.LOGTAG, "mime=" + mime);
             if (mime == null) {
                 attr = R.attr.media_preview_unknown;
+            } else if (mime.equals("audio/x-m4b")) {
+                attr = R.attr.media_preview_audiobook;
             } else if (mime.startsWith("audio/")) {
                 attr = R.attr.media_preview_audio;
             } else if (mime.equals("text/calendar") || (mime.equals("text/x-vcalendar"))) {

src/main/java/eu/siacs/conversations/ui/adapter/MediaPreviewAdapter.java 🔗

@@ -151,7 +151,7 @@ public class MediaPreviewAdapter extends RecyclerView.Adapter<MediaPreviewAdapte
         this.mediaPreviews.clear();
     }
 
-    class MediaPreviewViewHolder extends RecyclerView.ViewHolder {
+    static class MediaPreviewViewHolder extends RecyclerView.ViewHolder {
 
         private final MediaPreviewBinding binding;
 

src/main/java/eu/siacs/conversations/utils/MimeUtils.java 🔗

@@ -28,6 +28,8 @@ import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.Arrays;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -260,7 +262,8 @@ public final class MimeUtils {
         add("audio/mpeg", "mpga");
         add("audio/mpeg", "mpega");
         add("audio/mpeg", "mp2");
-        add("audio/mpeg", "m4a");
+        add("audio/mp4", "m4a");
+        add("audio/x-m4b", "m4b");
         add("audio/mpegurl", "m3u");
         add("audio/ogg", "oga");
         add("audio/opus", "opus");
@@ -413,6 +416,9 @@ public final class MimeUtils {
         applyOverrides();
     }
 
+    // mime types that are more reliant by path
+    private static final Collection<String> PATH_PRECEDENCE_MIME_TYPE = Arrays.asList("audio/x-m4b");
+
     private static void add(String mimeType, String extension) {
         // If we have an existing x -> y mapping, we do not want to
         // override it with another mapping x -> y2.
@@ -537,44 +543,49 @@ public final class MimeUtils {
     }
 
     public static String guessMimeTypeFromUriAndMime(final Context context, final Uri uri, final String mime) {
-        Log.d(Config.LOGTAG, "guessMimeTypeFromUriAndMime " + uri + " and mime=" + mime);
-        if (mime == null || mime.equals("application/octet-stream")) {
-            final String guess = guessMimeTypeFromUri(context, uri);
-            if (guess != null) {
-                return guess;
-            } else {
-                return mime;
-            }
+        Log.d(Config.LOGTAG, "guessMimeTypeFromUriAndMime(" + uri + "," + mime+")");
+        final String mimeFromUri = guessMimeTypeFromUri(context, uri);
+        Log.d(Config.LOGTAG,"mimeFromUri:"+mimeFromUri);
+        if (PATH_PRECEDENCE_MIME_TYPE.contains(mimeFromUri)) {
+            return mimeFromUri;
+        } else if (mime == null || mime.equals("application/octet-stream")) {
+            return mimeFromUri;
+        } else {
+            return mime;
         }
-        return guessMimeTypeFromUri(context, uri);
     }
 
-    public static String guessMimeTypeFromUri(Context context, Uri uri) {
-        // try the content resolver
-        String mimeType;
-        try {
-            mimeType = context.getContentResolver().getType(uri);
-        } catch (final Throwable throwable) {
-            mimeType = null;
+    public static String guessMimeTypeFromUri(final Context context, final Uri uri) {
+        final String mimeTypeContentResolver = guessFromContentResolver(context, uri);
+        final String mimeTypeFromQueryParameter = uri.getQueryParameter("mimeType");
+        final String name = "content".equals(uri.getScheme()) ? getDisplayName(context, uri) : null;
+        final String mimeTypeFromName = Strings.isNullOrEmpty(name) ? null : guessFromPath(name);
+        final String path = uri.getPath();
+        final String mimeTypeFromPath = Strings.isNullOrEmpty(path) ? null : guessFromPath(path);
+        if (PATH_PRECEDENCE_MIME_TYPE.contains(mimeTypeFromName)) {
+            return mimeTypeFromName;
         }
-        // try the extension
-        if (mimeType == null || mimeType.equals("application/octet-stream")) {
-            final String path = uri.getPath();
-            if (path != null) {
-                mimeType = guessFromPath(path);
-            }
+        if (PATH_PRECEDENCE_MIME_TYPE.contains(mimeTypeFromPath)) {
+            return mimeTypeFromPath;
         }
-        if (mimeType == null && "content".equals(uri.getScheme())) {
-            final String name = getDisplayName(context, uri);
-            if (name != null) {
-                mimeType = guessFromPath(name);
-            }
+        if (mimeTypeContentResolver != null && !"application/octet-stream".equals(mimeTypeContentResolver)) {
+            return mimeTypeContentResolver;
         }
-        // sometimes this works (as with the commit content api)
-        if (mimeType == null) {
-            mimeType = uri.getQueryParameter("mimeType");
+        if (mimeTypeFromName != null) {
+            return mimeTypeFromName;
+        }
+        if (mimeTypeFromQueryParameter != null) {
+            return mimeTypeFromQueryParameter;
+        }
+        return mimeTypeFromPath;
+    }
+
+    private static String guessFromContentResolver(final Context context, final Uri uri) {
+        try {
+            return context.getContentResolver().getType(uri);
+        } catch (final Throwable e) {
+            return null;
         }
-        return mimeType;
     }
 
     private static String getDisplayName(final Context context, final Uri uri) {

src/main/java/eu/siacs/conversations/utils/UIHelper.java 🔗

@@ -481,6 +481,8 @@ public class UIHelper {
             return context.getString(R.string.file);
         } else if (MimeUtils.AMBIGUOUS_CONTAINER_FORMATS.contains(mime)) {
             return context.getString(R.string.multimedia_file);
+        } else if (mime.equals("audio/x-m4b")) {
+            return context.getString(R.string.audiobook);
         } else if (mime.startsWith("audio/")) {
             return context.getString(R.string.audio);
         } else if (mime.startsWith("video/")) {

src/main/res/drawable/ic_play_lesson_black_24.xml 🔗

@@ -0,0 +1,6 @@
+<vector android:height="48dp" android:tint="#000000"
+    android:viewportHeight="24" android:viewportWidth="24"
+    android:width="48dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="@android:color/white" android:pathData="M18,11c0.34,0 0.67,0.03 1,0.08V4c0,-1.1 -0.9,-2 -2,-2H5C3.9,2 3,2.9 3,4v16c0,1.1 0.9,2 2,2h7.26C11.47,20.87 11,19.49 11,18C11,14.13 14.13,11 18,11zM7,11V4h5v7L9.5,9.5L7,11z"/>
+    <path android:fillColor="@android:color/white" android:pathData="M18,13c-2.76,0 -5,2.24 -5,5s2.24,5 5,5s5,-2.24 5,-5S20.76,13 18,13zM16.75,20.5v-5l4,2.5L16.75,20.5z"/>
+</vector>

src/main/res/drawable/ic_play_lesson_white_48dp.xml 🔗

@@ -0,0 +1,6 @@
+<vector android:height="48dp" android:tint="#FFFFFF"
+    android:viewportHeight="24" android:viewportWidth="24"
+    android:width="48dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="@android:color/white" android:pathData="M18,11c0.34,0 0.67,0.03 1,0.08V4c0,-1.1 -0.9,-2 -2,-2H5C3.9,2 3,2.9 3,4v16c0,1.1 0.9,2 2,2h7.26C11.47,20.87 11,19.49 11,18C11,14.13 14.13,11 18,11zM7,11V4h5v7L9.5,9.5L7,11z"/>
+    <path android:fillColor="@android:color/white" android:pathData="M18,13c-2.76,0 -5,2.24 -5,5s2.24,5 5,5s5,-2.24 5,-5S20.76,13 18,13zM16.75,20.5v-5l4,2.5L16.75,20.5z"/>
+</vector>

src/main/res/values/attrs.xml 🔗

@@ -70,6 +70,7 @@
     <attr name="media_preview_tour" format="reference" />
     <attr name="media_preview_contact" format="reference" />
     <attr name="media_preview_app" format="reference" />
+    <attr name="media_preview_audiobook" format="reference" />
     <attr name="media_preview_calendar" format="reference" />
     <attr name="media_preview_archive" format="reference" />
     <attr name="media_preview_ebook" format="reference" />

src/main/res/values/strings.xml 🔗

@@ -420,6 +420,7 @@
     <string name="multimedia_file">multimedia file</string>
     <string name="pdf_document">PDF document</string>
     <string name="apk">Android App</string>
+    <string name="audiobook">Audiobook</string>
     <string name="vcard">Contact</string>
     <string name="avatar_has_been_published">Avatar has been published!</string>
     <string name="sending_x_file">Sending %s</string>

src/main/res/values/themes.xml 🔗

@@ -88,6 +88,7 @@
         <item name="media_preview_tour" type="reference">@drawable/baseline_tour_black_48</item>
         <item name="media_preview_contact" type="reference">@drawable/ic_person_black_48dp</item>
         <item name="media_preview_app" type="reference">@drawable/ic_android_black_48dp</item>
+        <item name="media_preview_audiobook" type="reference">@drawable/ic_play_lesson_black_24</item>
         <item name="media_preview_calendar" type="reference">@drawable/ic_event_black_48dp</item>
         <item name="media_preview_archive" type="reference">@drawable/ic_archive_black_48dp</item>
         <item name="media_preview_ebook" type="reference">@drawable/ic_book_black_48dp</item>
@@ -244,6 +245,7 @@
         <item name="media_preview_tour" type="reference">@drawable/baseline_tour_white_48</item>
         <item name="media_preview_contact" type="reference">@drawable/ic_person_white_48dp</item>
         <item name="media_preview_app" type="reference">@drawable/ic_android_white_48dp</item>
+        <item name="media_preview_audiobook" type="reference">@drawable/ic_play_lesson_white_48dp</item>
         <item name="media_preview_calendar" type="reference">@drawable/ic_event_white_48dp</item>
         <item name="media_preview_archive" type="reference">@drawable/ic_archive_white_48dp</item>
         <item name="media_preview_ebook" type="reference">@drawable/ic_book_white_48dp</item>