Руководство добавляет песню в Mediastore как музыкальный трек

Я хочу создать музыкальный плеер, который может скачать песню онлайн и добавить его в MediaStore. Я использую диспетчер загрузки и разрешаю MediaScanner сканировать этот файл после завершения загрузки.

DownloadManager.Request request ....
request.allowScanningByMediaScanner();
...
downloadManager.enqueue(request);

Он отлично работает в Android 5.0 и выше.
Но песня была загружена с помощью кодека (opus), который не поддерживается в android ниже версии lollipop, поэтому MediaScanner не добавляет этот файл в MediaStore.

Что моя проблема, мое приложение может играть opus codec, но песня не существовала в MediaStore после ее загрузки, поэтому мое приложение не может найти эту песню.

Как заставить MediaScanner добавить загруженный файл в MediaStore.Audio в качестве музыкальной дорожки. Если не удается, как я могу вручную добавить эту песню в MediaStore.Audio после завершения загрузки:

public class BroadcastDownloadComplete extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals("android.intent.action.DOWNLOAD_COMPLETE")) {

            //addSongToMediaStore(intent);
        }
    }
}

Ответ 1

Из исходного кода здесь, мы видим, что окончательная реализация сканера имеет два шага для сканирования аудиофайла. Если любой из этих двух шагов завершится неудачно, аудиофайл не будет вставляться в медиа-провайдер.

шаг 1 проверить расширение файла

static bool FileHasAcceptableExtension(const char *extension) {
    static const char *kValidExtensions[] = {
        ".mp3", ".mp4", ".m4a", ".3gp", ".3gpp", ".3g2", ".3gpp2",
        ".mpeg", ".ogg", ".mid", ".smf", ".imy", ".wma", ".aac",
        ".wav", ".amr", ".midi", ".xmf", ".rtttl", ".rtx", ".ota",
        ".mkv", ".mka", ".webm", ".ts", ".fl", ".flac", ".mxmf",
        ".avi", ".mpeg", ".mpg"
    };
    static const size_t kNumValidExtensions =
         sizeof(kValidExtensions) / sizeof(kValidExtensions[0]);
    for (size_t i = 0; i < kNumValidExtensions; ++i) {
        if (!strcasecmp(extension, kValidExtensions[i])) {
            return true;
        }
    }
    return false;
}

Дополнительные расширения добавлены с Android 5.0. Общий контейнер для opus codec ogg, это расширение существует до Android 5.0. Предположим, что расширение вашего аудиофайла ogg, процесс сканирования на этом этапе прекрасен.

step2 получить метаданные

После первого шага сканер должен получить метаданные мультимедиа для последующей вставки базы данных. Я думаю, что сканер выполнит проверку уровня кодека на этом шаге.

sp<MediaMetadataRetriever> mRetriever(new MediaMetadataRetriever);
int fd = open(path, O_RDONLY | O_LARGEFILE);
status_t status;
if (fd < 0) {
    // couldn't open it locally, maybe the media server can?
    status = mRetriever->setDataSource(path);
} else {
    status = mRetriever->setDataSource(fd, 0, 0x7ffffffffffffffL);
    close(fd);
}
if (status) {
    return MEDIA_SCAN_RESULT_ERROR;
}

Для версии Android до 5.0 сканер может не работать на этом этапе. Из-за отсутствия встроенной поддержки opus-кодека, setDataSource наконец-то провалится. Медиа файл не будет добавлен в медиа-провайдер наконец.

предлагаемое решение

Поскольку мы знаем, что аудиофайл будет добавлен в

MediaStore.Audio.Media.EXTERNAL_CONTENT_URI

мы можем работать с базой данных вручную. Если вы хотите, чтобы ваш аудиофайл соответствовал другим аудиофайлам в базе данных, вы должны сами получить все метаданные. Поскольку вы можете воспроизводить файл opus, я думаю, что легко получить метаданные.

// retrieve more metadata, duration etc.
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.Audio.AudioColumns.DATA, "/mnt/sdcard/Music/example.opus");
contentValues.put(MediaStore.Audio.AudioColumns.TITLE, "Example track");
contentValues.put(MediaStore.Audio.AudioColumns.DISPLAY_NAME, "example");
// more columns should be filled from here
Uri uri = getContentResolver().insert(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, contentValues);
Log.d(TAG, uri.toString());

После этого приложение может найти аудиофайл.

getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI...

Ответ 2

Вы можете использовать MediaScannerConnection, чтобы попросить Android отсканировать файл, который будет включен в качестве носителя. Вы захотите использовать статический метод scanFile().