com.cyanogenmod.filemanager.util.MediaHelper.java Source code

Java tutorial

Introduction

Here is the source code for com.cyanogenmod.filemanager.util.MediaHelper.java

Source

/*
 * Copyright (C) 2013 The CyanogenMod Project
 * Copyright (C) 2014 Yaroslav Mytkalyk
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.cyanogenmod.filemanager.util;

import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.content.ContentResolver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.provider.BaseColumns;
import android.provider.MediaStore;
import android.provider.MediaStore.MediaColumns;
import android.text.TextUtils;

import org.apache.commons.io.IOUtils;
import android.support.annotation.NonNull;

import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * A helper class with useful methods to extract media data.
 */
public final class MediaHelper {

    private static final String EMULATED_STORAGE_SOURCE = System.getenv("EMULATED_STORAGE_SOURCE");
    private static final String EMULATED_STORAGE_TARGET = System.getenv("EMULATED_STORAGE_TARGET");
    private static final String EXTERNAL_STORAGE = System.getenv("EXTERNAL_STORAGE");

    private static final String INTERNAL_VOLUME = "internal";
    private static final String EXTERNAL_VOLUME = "external";

    /**
     * URIs that are relevant for determining album art;
     * useful for content observer registration
     */
    public static final Uri[] RELEVANT_URIS = new Uri[] { MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
            MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI };

    /**
     * Method that returns an array with all the unique albums paths and ids.
     *
     * @param cr The ContentResolver
     * @return The albums map
     */
    public static Map<String, Long> getAllAlbums(ContentResolver cr) {
        final Map<String, Long> albums = new HashMap<>();
        final String[] projection = { "distinct " + MediaStore.Audio.Media.ALBUM_ID,
                "substr(" + MediaStore.Audio.Media.DATA + ", 0, length(" + MediaStore.Audio.Media.DATA
                        + ") - length(" + MediaStore.Audio.Media.DISPLAY_NAME + "))" };
        final String where = MediaStore.Audio.Media.IS_MUSIC + " = ?";
        Cursor c = cr.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, projection, where, new String[] { "1" },
                null);
        if (c != null) {
            try {
                for (c.moveToFirst(); !c.isAfterLast(); c.moveToNext()) {
                    long albumId = c.getLong(0);
                    String albumPath = c.getString(1);
                    albums.put(albumPath, albumId);
                }
            } finally {
                IOUtils.closeQuietly(c);
            }
        }
        return albums;
    }

    /**
     * Method that returns the album thumbnail path by its identifier.
     *
     * @param cr The ContentResolver
     * @param albumId The album identifier to search
     * @return String The album thumbnail path
     */
    public static String getAlbumThumbnailPath(@NonNull final ContentResolver cr, final long albumId) {
        final String[] projection = { MediaStore.Audio.Albums.ALBUM_ART };
        final String where = BaseColumns._ID + "=?";
        Cursor c = cr.query(MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI, projection, where,
                new String[] { String.valueOf(albumId) }, null);
        try {
            if (c != null && c.moveToFirst()) {
                return c.getString(0);
            }
        } finally {
            IOUtils.closeQuietly(c);
        }
        return null;
    }

    /**
     * Method that converts a file reference to a content uri reference
     *
     * @param cr A content resolver
     * @param file The file reference
     * @return Uri The content uri or null if file not exists in the media database
     */
    public static Uri fileToContentUri(ContentResolver cr, File file) {
        // Normalize the path to ensure media search
        final String normalizedPath = normalizeMediaPath(file.getAbsolutePath());

        // Check in external and internal storages
        Uri uri = fileToContentUri(cr, normalizedPath, EXTERNAL_VOLUME);
        if (uri != null) {
            return uri;
        }
        uri = fileToContentUri(cr, normalizedPath, INTERNAL_VOLUME);
        if (uri != null) {
            return uri;
        }
        return null;
    }

    /**
     * Method that converts a file reference to a content uri reference
     *
     * @param cr A content resolver
     * @param path The path to search
     * @param volume The volume
     * @return Uri The content uri or null if file not exists in the media database
     */
    private static Uri fileToContentUri(ContentResolver cr, String path, String volume) {
        final String[] projection = { BaseColumns._ID, MediaStore.Files.FileColumns.MEDIA_TYPE };
        final String where = MediaColumns.DATA + " = ?";
        Uri baseUri = MediaStore.Files.getContentUri(volume);
        Cursor c = cr.query(baseUri, projection, where, new String[] { path }, null);
        try {
            if (c != null && c.moveToNext()) {
                int type = c.getInt(c.getColumnIndexOrThrow(MediaStore.Files.FileColumns.MEDIA_TYPE));
                if (type != 0) {
                    // Do not force to use content uri for no media files
                    long id = c.getLong(c.getColumnIndexOrThrow(BaseColumns._ID));
                    return Uri.withAppendedPath(baseUri, String.valueOf(id));
                }
            }
        } finally {
            IOUtils.closeQuietly(c);
        }
        return null;
    }

    /**
     * Method that converts a content uri to a file system path
     *
     * @param cr The content resolver
     * @param uri The content uri
     * @return File The file reference
     */
    public static File contentUriToFile(ContentResolver cr, Uri uri) {
        // Sanity checks
        if (uri == null || uri.getScheme() == null || uri.getScheme().compareTo("content") != 0) {
            return null;
        }

        // Retrieve the request id
        long id;
        try {
            id = Long.parseLong(new File(uri.getPath()).getName());
        } catch (NumberFormatException nfex) {
            return null;
        }

        // Check in external and internal storages
        File file = mediaIdToFile(cr, id, EXTERNAL_VOLUME);
        if (file != null) {
            return file;
        }
        file = mediaIdToFile(cr, id, INTERNAL_VOLUME);
        if (file != null) {
            return file;
        }
        return null;
    }

    /**
     * Method that converts a content uri to a file system path
     *
     * @param cr The content resolver
     * @param id The media database id
     * @param volume The volume
     * @return File The file reference
     */
    private static File mediaIdToFile(ContentResolver cr, long id, String volume) {
        final String[] projection = { MediaColumns.DATA };
        final String where = MediaColumns._ID + " = ?";
        Uri baseUri = MediaStore.Files.getContentUri(volume);
        Cursor c = cr.query(baseUri, projection, where, new String[] { String.valueOf(id) }, null);
        try {
            if (c != null && c.moveToNext()) {
                return new File(c.getString(c.getColumnIndexOrThrow(MediaColumns.DATA)));
            }
        } finally {
            if (c != null) {
                c.close();
            }
        }
        return null;
    }

    /**
     * Method that converts a not standard media mount path to a standard media path
     *
     * @param path The path to normalize
     * @return String The normalized media path
     */
    public static String normalizeMediaPath(String path) {
        // Retrieve all the paths and check that we have this environment vars
        if (TextUtils.isEmpty(EMULATED_STORAGE_SOURCE) || TextUtils.isEmpty(EMULATED_STORAGE_TARGET)
                || TextUtils.isEmpty(EXTERNAL_STORAGE)) {
            return path;
        }

        // We need to convert EMULATED_STORAGE_SOURCE -> EMULATED_STORAGE_TARGET
        if (path.startsWith(EMULATED_STORAGE_SOURCE)) {
            path = path.replace(EMULATED_STORAGE_SOURCE, EMULATED_STORAGE_TARGET);
        }
        // We need to convert EXTERNAL_STORAGE -> EMULATED_STORAGE_TARGET / userId
        if (path.startsWith(EXTERNAL_STORAGE)) {
            final String userId = String.valueOf(myUserId());
            final String target = new File(EMULATED_STORAGE_TARGET, userId).getAbsolutePath();
            path = path.replace(EXTERNAL_STORAGE, target);
        }
        return path;
    }

    private static int myUserId() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            return UserHandleJellyBeanMR1.myUserId();
        }
        return 0;
    }

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
    private static final class UserHandleJellyBeanMR1 {

        @SuppressLint("NewApi")
        static int myUserId() {
            try {
                final Method myUserIdMethod = android.os.UserHandle.class.getMethod("myUserId");
                myUserIdMethod.setAccessible(true);
                return (Integer) myUserIdMethod.invoke(null);
            } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                e.printStackTrace();
            }
            return 0;
        }
    }
}