574 lines
23 KiB
Java
574 lines
23 KiB
Java
|
/*
|
||
|
* Copyright 2018 The Android Open Source Project
|
||
|
*
|
||
|
* 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.android.pump.db;
|
||
|
|
||
|
import android.content.ContentResolver;
|
||
|
import android.content.ContentValues;
|
||
|
import android.database.ContentObserver;
|
||
|
import android.database.Cursor;
|
||
|
import android.net.Uri;
|
||
|
import android.provider.MediaStore;
|
||
|
|
||
|
import androidx.annotation.AnyThread;
|
||
|
import androidx.annotation.NonNull;
|
||
|
import androidx.annotation.Nullable;
|
||
|
import androidx.annotation.WorkerThread;
|
||
|
|
||
|
import com.android.pump.util.Clog;
|
||
|
import com.android.pump.util.Collections;
|
||
|
|
||
|
import java.util.ArrayList;
|
||
|
import java.util.Collection;
|
||
|
|
||
|
@WorkerThread
|
||
|
class AudioStore extends ContentObserver {
|
||
|
private static final String TAG = Clog.tag(AudioStore.class);
|
||
|
|
||
|
private final ContentResolver mContentResolver;
|
||
|
private final ChangeListener mChangeListener;
|
||
|
private final MediaProvider mMediaProvider;
|
||
|
|
||
|
interface ChangeListener {
|
||
|
void onAudiosAdded(@NonNull Collection<Audio> audios);
|
||
|
void onArtistsAdded(@NonNull Collection<Artist> artists);
|
||
|
void onAlbumsAdded(@NonNull Collection<Album> albums);
|
||
|
void onGenresAdded(@NonNull Collection<Genre> genres);
|
||
|
void onPlaylistsAdded(@NonNull Collection<Playlist> playlists);
|
||
|
}
|
||
|
|
||
|
@AnyThread
|
||
|
AudioStore(@NonNull ContentResolver contentResolver, @NonNull ChangeListener changeListener,
|
||
|
@NonNull MediaProvider mediaProvider) {
|
||
|
super(null);
|
||
|
|
||
|
Clog.i(TAG, "AudioStore(" + contentResolver + ", " + changeListener
|
||
|
+ ", " + mediaProvider + ")");
|
||
|
mContentResolver = contentResolver;
|
||
|
mChangeListener = changeListener;
|
||
|
mMediaProvider = mediaProvider;
|
||
|
|
||
|
// TODO(123705758) Do we need content observer for other content uris? (E.g. album, artist)
|
||
|
mContentResolver.registerContentObserver(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
|
||
|
true, this);
|
||
|
|
||
|
// TODO(123705758) When to call unregisterContentObserver?
|
||
|
// mContentResolver.unregisterContentObserver(this);
|
||
|
}
|
||
|
|
||
|
void load() {
|
||
|
Clog.i(TAG, "load()");
|
||
|
ArrayList<Artist> artists = new ArrayList<>();
|
||
|
ArrayList<Album> albums = new ArrayList<>();
|
||
|
ArrayList<Audio> audios = new ArrayList<>();
|
||
|
ArrayList<Playlist> playlists = new ArrayList<>();
|
||
|
ArrayList<Genre> genres = new ArrayList<>();
|
||
|
|
||
|
// #1 Load artists
|
||
|
{
|
||
|
Uri contentUri = MediaStore.Audio.Artists.EXTERNAL_CONTENT_URI;
|
||
|
String[] projection = {
|
||
|
MediaStore.Audio.Artists._ID
|
||
|
};
|
||
|
String sortOrder = MediaStore.Audio.Artists._ID;
|
||
|
Cursor cursor = mContentResolver.query(contentUri, projection, null, null, sortOrder);
|
||
|
if (cursor != null) {
|
||
|
try {
|
||
|
int idColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Artists._ID);
|
||
|
|
||
|
for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
|
||
|
long id = cursor.getLong(idColumn);
|
||
|
|
||
|
Artist artist = new Artist(id);
|
||
|
artists.add(artist);
|
||
|
}
|
||
|
} finally {
|
||
|
cursor.close();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// #2 Load albums and connect each to artist
|
||
|
{
|
||
|
Uri contentUri = MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI;
|
||
|
String[] projection = {
|
||
|
MediaStore.Audio.Albums._ID,
|
||
|
MediaStore.Audio.Media.ARTIST_ID // TODO MediaStore.Audio.Albums.ARTIST_ID
|
||
|
};
|
||
|
String sortOrder = MediaStore.Audio.Albums._ID;
|
||
|
Cursor cursor = mContentResolver.query(contentUri, projection, null, null, sortOrder);
|
||
|
if (cursor != null) {
|
||
|
try {
|
||
|
int idColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Albums._ID);
|
||
|
int artistIdColumn = cursor.getColumnIndexOrThrow(
|
||
|
MediaStore.Audio.Media.ARTIST_ID); // TODO MediaStore.Audio.Albums.ARTIST_ID
|
||
|
|
||
|
for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
|
||
|
long id = cursor.getLong(idColumn);
|
||
|
|
||
|
Album album = new Album(id);
|
||
|
albums.add(album);
|
||
|
|
||
|
if (!cursor.isNull(artistIdColumn)) {
|
||
|
long artistId = cursor.getLong(artistIdColumn);
|
||
|
|
||
|
Artist artist = Collections.find(artists, artistId, Artist::getId);
|
||
|
album.setArtist(artist);
|
||
|
}
|
||
|
}
|
||
|
} finally {
|
||
|
cursor.close();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// #3 Load songs and connect each to album and artist
|
||
|
{
|
||
|
Uri contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
|
||
|
String[] projection = {
|
||
|
MediaStore.Audio.Media._ID,
|
||
|
MediaStore.Audio.Media.MIME_TYPE,
|
||
|
MediaStore.Audio.Media.ARTIST_ID,
|
||
|
MediaStore.Audio.Media.ALBUM_ID
|
||
|
};
|
||
|
String selection = MediaStore.Audio.Media.IS_MUSIC + " != 0";
|
||
|
String sortOrder = MediaStore.Audio.Media._ID;
|
||
|
Cursor cursor = mContentResolver.query(contentUri, projection, selection, null, sortOrder);
|
||
|
if (cursor != null) {
|
||
|
try {
|
||
|
int idColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media._ID);
|
||
|
int mimeTypeColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.MIME_TYPE);
|
||
|
int artistIdColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST_ID);
|
||
|
int albumIdColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM_ID);
|
||
|
|
||
|
for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
|
||
|
long id = cursor.getLong(idColumn);
|
||
|
String mimeType = cursor.getString(mimeTypeColumn);
|
||
|
|
||
|
Audio audio = new Audio(id, mimeType);
|
||
|
audios.add(audio);
|
||
|
|
||
|
if (!cursor.isNull(artistIdColumn)) {
|
||
|
long artistId = cursor.getLong(artistIdColumn);
|
||
|
|
||
|
Artist artist = Collections.find(artists, artistId, Artist::getId);
|
||
|
audio.setArtist(artist);
|
||
|
artist.addAudio(audio);
|
||
|
}
|
||
|
if (!cursor.isNull(albumIdColumn)) {
|
||
|
long albumId = cursor.getLong(albumIdColumn);
|
||
|
|
||
|
Album album = Collections.find(albums, albumId, Album::getId);
|
||
|
audio.setAlbum(album);
|
||
|
album.addAudio(audio);
|
||
|
}
|
||
|
}
|
||
|
} finally {
|
||
|
cursor.close();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// #4 Load playlists (optional?)
|
||
|
{
|
||
|
Uri contentUri = MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI;
|
||
|
String[] projection = {
|
||
|
MediaStore.Audio.Playlists._ID
|
||
|
};
|
||
|
String sortOrder = MediaStore.Audio.Playlists._ID;
|
||
|
Cursor cursor = mContentResolver.query(contentUri, projection, null, null, sortOrder);
|
||
|
if (cursor != null) {
|
||
|
try {
|
||
|
int idColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Playlists._ID);
|
||
|
|
||
|
for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
|
||
|
long id = cursor.getLong(idColumn);
|
||
|
|
||
|
Playlist playlist = new Playlist(id);
|
||
|
playlists.add(playlist);
|
||
|
}
|
||
|
} finally {
|
||
|
cursor.close();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// #5 Load genres (optional?)
|
||
|
{
|
||
|
Uri contentUri = MediaStore.Audio.Genres.EXTERNAL_CONTENT_URI;
|
||
|
String[] projection = {
|
||
|
MediaStore.Audio.Genres._ID
|
||
|
};
|
||
|
String sortOrder = MediaStore.Audio.Genres._ID;
|
||
|
Cursor cursor = mContentResolver.query(contentUri, projection, null, null, sortOrder);
|
||
|
if (cursor != null) {
|
||
|
try {
|
||
|
int idColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Genres._ID);
|
||
|
|
||
|
for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
|
||
|
long id = cursor.getLong(idColumn);
|
||
|
|
||
|
Genre genre = new Genre(id);
|
||
|
genres.add(genre);
|
||
|
}
|
||
|
} finally {
|
||
|
cursor.close();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
mChangeListener.onAudiosAdded(audios);
|
||
|
mChangeListener.onArtistsAdded(artists);
|
||
|
mChangeListener.onAlbumsAdded(albums);
|
||
|
mChangeListener.onGenresAdded(genres);
|
||
|
mChangeListener.onPlaylistsAdded(playlists);
|
||
|
}
|
||
|
|
||
|
boolean loadData(@NonNull Audio audio) {
|
||
|
boolean updated = false;
|
||
|
|
||
|
Uri contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
|
||
|
String[] projection = {
|
||
|
MediaStore.Audio.Media.TITLE,
|
||
|
MediaStore.Audio.Media.ARTIST_ID,
|
||
|
MediaStore.Audio.Media.ALBUM_ID
|
||
|
};
|
||
|
String selection = MediaStore.Audio.Media._ID + " = ?";
|
||
|
String[] selectionArgs = { Long.toString(audio.getId()) };
|
||
|
Cursor cursor = mContentResolver.query(
|
||
|
contentUri, projection, selection, selectionArgs, null);
|
||
|
if (cursor != null) {
|
||
|
try {
|
||
|
int titleColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE);
|
||
|
int artistIdColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST_ID);
|
||
|
int albumIdColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM_ID);
|
||
|
|
||
|
if (cursor.moveToFirst()) {
|
||
|
if (!cursor.isNull(titleColumn)) {
|
||
|
String title = cursor.getString(titleColumn);
|
||
|
updated |= audio.setTitle(title);
|
||
|
}
|
||
|
if (!cursor.isNull(artistIdColumn)) {
|
||
|
long artistId = cursor.getLong(artistIdColumn);
|
||
|
Artist artist = mMediaProvider.getArtistById(artistId);
|
||
|
updated |= audio.setArtist(artist);
|
||
|
updated |= loadData(artist); // TODO(b/123707561) Load separate from audio
|
||
|
}
|
||
|
if (!cursor.isNull(albumIdColumn)) {
|
||
|
long albumId = cursor.getLong(albumIdColumn);
|
||
|
Album album = mMediaProvider.getAlbumById(albumId);
|
||
|
updated |= audio.setAlbum(album);
|
||
|
updated |= loadData(album); // TODO(b/123707561) Load separate from audio
|
||
|
}
|
||
|
}
|
||
|
} finally {
|
||
|
cursor.close();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return updated;
|
||
|
}
|
||
|
|
||
|
boolean loadData(@NonNull Artist artist) {
|
||
|
boolean updated = false;
|
||
|
|
||
|
Uri contentUri = MediaStore.Audio.Artists.EXTERNAL_CONTENT_URI;
|
||
|
String[] projection = { MediaStore.Audio.Artists.ARTIST };
|
||
|
String selection = MediaStore.Audio.Artists._ID + " = ?";
|
||
|
String[] selectionArgs = { Long.toString(artist.getId()) };
|
||
|
Cursor cursor = mContentResolver.query(
|
||
|
contentUri, projection, selection, selectionArgs, null);
|
||
|
if (cursor != null) {
|
||
|
try {
|
||
|
int artistColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Artists.ARTIST);
|
||
|
|
||
|
if (cursor.moveToFirst()) {
|
||
|
if (!cursor.isNull(artistColumn)) {
|
||
|
String name = cursor.getString(artistColumn);
|
||
|
updated |= artist.setName(name);
|
||
|
}
|
||
|
}
|
||
|
} finally {
|
||
|
cursor.close();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
updated |= loadAlbums(artist); // TODO(b/123707561) Load separate from artist
|
||
|
|
||
|
return updated;
|
||
|
}
|
||
|
|
||
|
boolean loadData(@NonNull Album album) {
|
||
|
boolean updated = false;
|
||
|
|
||
|
Uri contentUri = MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI;
|
||
|
String[] projection = {
|
||
|
MediaStore.Audio.Albums.ALBUM,
|
||
|
MediaStore.Audio.Media.ARTIST_ID // TODO MediaStore.Audio.Albums.ARTIST_ID
|
||
|
};
|
||
|
String selection = MediaStore.Audio.Albums._ID + " = ?";
|
||
|
String[] selectionArgs = { Long.toString(album.getId()) };
|
||
|
Cursor cursor = mContentResolver.query(
|
||
|
contentUri, projection, selection, selectionArgs, null);
|
||
|
if (cursor != null) {
|
||
|
try {
|
||
|
int albumColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Albums.ALBUM);
|
||
|
int artistIdColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST_ID); // TODO MediaStore.Audio.Albums.ARTIST_ID
|
||
|
|
||
|
if (cursor.moveToFirst()) {
|
||
|
if (!cursor.isNull(albumColumn)) {
|
||
|
String albumTitle = cursor.getString(albumColumn);
|
||
|
updated |= album.setTitle(albumTitle);
|
||
|
}
|
||
|
if (!cursor.isNull(artistIdColumn)) {
|
||
|
long artistId = cursor.getLong(artistIdColumn);
|
||
|
Artist artist = mMediaProvider.getArtistById(artistId);
|
||
|
updated |= album.setArtist(artist);
|
||
|
updated |= loadData(artist); // TODO(b/123707561) Load separate from album
|
||
|
}
|
||
|
|
||
|
// TODO(b/130363861) No need to store the URI -- generate when requested instead
|
||
|
Uri albumArtUri = new Uri.Builder()
|
||
|
.scheme(ContentResolver.SCHEME_CONTENT)
|
||
|
.authority(MediaStore.AUTHORITY)
|
||
|
.appendPath("external").appendPath("audio").appendPath("albumart")
|
||
|
.appendPath(Long.toString(album.getId())).build();
|
||
|
updated |= album.setAlbumArtUri(albumArtUri);
|
||
|
}
|
||
|
} finally {
|
||
|
cursor.close();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return updated;
|
||
|
}
|
||
|
|
||
|
boolean loadData(@NonNull Genre genre) {
|
||
|
boolean updated = false;
|
||
|
|
||
|
Uri contentUri = MediaStore.Audio.Genres.EXTERNAL_CONTENT_URI;
|
||
|
String[] projection = { MediaStore.Audio.Genres.NAME };
|
||
|
String selection = MediaStore.Audio.Genres._ID + " = ?";
|
||
|
String[] selectionArgs = { Long.toString(genre.getId()) };
|
||
|
Cursor cursor = mContentResolver.query(
|
||
|
contentUri, projection, selection, selectionArgs, null);
|
||
|
if (cursor != null) {
|
||
|
try {
|
||
|
int nameColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Genres.NAME);
|
||
|
|
||
|
if (cursor.moveToFirst()) {
|
||
|
if (!cursor.isNull(nameColumn)) {
|
||
|
String name = cursor.getString(nameColumn);
|
||
|
updated |= genre.setName(name);
|
||
|
}
|
||
|
}
|
||
|
} finally {
|
||
|
cursor.close();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
updated |= loadAudios(genre); // TODO(b/123707561) Load separate from genre
|
||
|
|
||
|
return updated;
|
||
|
}
|
||
|
|
||
|
boolean loadData(@NonNull Playlist playlist) {
|
||
|
boolean updated = false;
|
||
|
|
||
|
Uri contentUri = MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI;
|
||
|
String[] projection = { MediaStore.Audio.Playlists.NAME };
|
||
|
String selection = MediaStore.Audio.Playlists._ID + " = ?";
|
||
|
String[] selectionArgs = { Long.toString(playlist.getId()) };
|
||
|
Cursor cursor = mContentResolver.query(
|
||
|
contentUri, projection, selection, selectionArgs, null);
|
||
|
if (cursor != null) {
|
||
|
try {
|
||
|
int nameColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Playlists.NAME);
|
||
|
|
||
|
if (cursor.moveToFirst()) {
|
||
|
if (!cursor.isNull(nameColumn)) {
|
||
|
String name = cursor.getString(nameColumn);
|
||
|
updated |= playlist.setName(name);
|
||
|
}
|
||
|
}
|
||
|
} finally {
|
||
|
cursor.close();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
updated |= loadAudios(playlist); // TODO(b/123707561) Load separate from playlist
|
||
|
|
||
|
return updated;
|
||
|
}
|
||
|
|
||
|
boolean loadAlbums(@NonNull Artist artist) {
|
||
|
boolean updated = false;
|
||
|
|
||
|
// TODO Remove hardcoded value
|
||
|
Uri contentUri = MediaStore.Audio.Artists.Albums.getContentUri("external", artist.getId());
|
||
|
/*
|
||
|
* On some devices MediaStore doesn't use ALBUM_ID as key from Artist to Album, but rather
|
||
|
* _ID. In order to support these devices we don't pass a projection, to avoid the
|
||
|
* IllegalArgumentException(Invalid column) exception, and then resort to _ID.
|
||
|
*/
|
||
|
String[] projection = null; // { MediaStore.Audio.Artists.Albums.ALBUM_ID };
|
||
|
Cursor cursor = mContentResolver.query(contentUri, projection, null, null, null);
|
||
|
if (cursor != null) {
|
||
|
try {
|
||
|
int albumIdColumn = cursor.getColumnIndex(MediaStore.Audio.Artists.Albums.ALBUM_ID);
|
||
|
if (albumIdColumn < 0) {
|
||
|
// On some devices the ALBUM_ID column doesn't exist and _ID is used instead.
|
||
|
albumIdColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media._ID);
|
||
|
}
|
||
|
|
||
|
for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
|
||
|
long albumId = cursor.getLong(albumIdColumn);
|
||
|
Album album = mMediaProvider.getAlbumById(albumId);
|
||
|
updated |= artist.addAlbum(album);
|
||
|
//updated |= loadData(album); // TODO(b/123707561) Load separate from artist
|
||
|
}
|
||
|
} finally {
|
||
|
cursor.close();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return updated;
|
||
|
}
|
||
|
|
||
|
boolean loadAudios(@NonNull Genre genre) {
|
||
|
boolean updated = false;
|
||
|
|
||
|
// TODO Remove hardcoded value
|
||
|
Uri contentUri = MediaStore.Audio.Genres.Members.getContentUri("external", genre.getId());
|
||
|
String[] projection = { MediaStore.Audio.Genres.Members.AUDIO_ID };
|
||
|
Cursor cursor = mContentResolver.query(contentUri, projection, null, null, null);
|
||
|
if (cursor != null) {
|
||
|
try {
|
||
|
int audioIdColumn = cursor.getColumnIndexOrThrow(
|
||
|
MediaStore.Audio.Genres.Members.AUDIO_ID);
|
||
|
|
||
|
for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
|
||
|
long audioId = cursor.getLong(audioIdColumn);
|
||
|
Audio audio = mMediaProvider.getAudioById(audioId);
|
||
|
updated |= genre.addAudio(audio);
|
||
|
updated |= loadData(audio); // TODO(b/123707561) Load separate from genre
|
||
|
}
|
||
|
} finally {
|
||
|
cursor.close();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return updated;
|
||
|
}
|
||
|
|
||
|
boolean loadAudios(@NonNull Playlist playlist) {
|
||
|
boolean updated = false;
|
||
|
|
||
|
// TODO Remove hardcoded value
|
||
|
Uri contentUri = MediaStore.Audio.Playlists.Members.getContentUri(
|
||
|
"external", playlist.getId());
|
||
|
String[] projection = { MediaStore.Audio.Playlists.Members.AUDIO_ID };
|
||
|
Cursor cursor = mContentResolver.query(contentUri, projection, null, null, null);
|
||
|
if (cursor != null) {
|
||
|
try {
|
||
|
int audioIdColumn = cursor.getColumnIndexOrThrow(
|
||
|
MediaStore.Audio.Playlists.Members.AUDIO_ID);
|
||
|
|
||
|
for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
|
||
|
long audioId = cursor.getLong(audioIdColumn);
|
||
|
Audio audio = mMediaProvider.getAudioById(audioId);
|
||
|
updated |= playlist.addAudio(audio);
|
||
|
updated |= loadData(audio); // TODO(b/123707561) Load separate from playlist
|
||
|
}
|
||
|
} finally {
|
||
|
cursor.close();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return updated;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void onChange(boolean selfChange) {
|
||
|
Clog.i(TAG, "onChange(" + selfChange + ")");
|
||
|
onChange(selfChange, null);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void onChange(boolean selfChange, @Nullable Uri uri) {
|
||
|
Clog.i(TAG, "onChange(" + selfChange + ", " + uri + ")");
|
||
|
// TODO(123705758) Figure out what changed
|
||
|
// onChange(false, content://media)
|
||
|
// onChange(false, content://media/external)
|
||
|
// onChange(false, content://media/external/audio/media/444)
|
||
|
// onChange(false, content://media/external/video/media/328?blocking=1&orig_id=328&group_id=0)
|
||
|
|
||
|
// TODO(123705758) Notify listener about changes
|
||
|
// mChangeListener.xxx();
|
||
|
}
|
||
|
|
||
|
// TODO Remove unused methods
|
||
|
private long createPlaylist(@NonNull String name) {
|
||
|
Clog.i(TAG, "createPlaylist(" + name + ")");
|
||
|
Uri contentUri = MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI;
|
||
|
ContentValues contentValues = new ContentValues(1);
|
||
|
contentValues.put(MediaStore.Audio.Playlists.NAME, name);
|
||
|
Uri uri = mContentResolver.insert(contentUri, contentValues);
|
||
|
return Long.parseLong(uri.getLastPathSegment());
|
||
|
}
|
||
|
|
||
|
private void addToPlaylist(@NonNull Playlist playlist, @NonNull Audio audio) {
|
||
|
Clog.i(TAG, "addToPlaylist(" + playlist + ", " + audio + ")");
|
||
|
long base = getLastPlayOrder(playlist);
|
||
|
|
||
|
// TODO Remove hardcoded value
|
||
|
Uri contentUri = MediaStore.Audio.Playlists.Members.getContentUri(
|
||
|
"external", playlist.getId());
|
||
|
ContentValues contentValues = new ContentValues(2);
|
||
|
contentValues.put(MediaStore.Audio.Playlists.Members.AUDIO_ID, audio.getId());
|
||
|
contentValues.put(MediaStore.Audio.Playlists.Members.PLAY_ORDER, base + 1);
|
||
|
mContentResolver.insert(contentUri, contentValues);
|
||
|
}
|
||
|
|
||
|
private long getLastPlayOrder(@NonNull Playlist playlist) {
|
||
|
Clog.i(TAG, "getLastPlayOrder(" + playlist + ")");
|
||
|
|
||
|
long playOrder = -1;
|
||
|
|
||
|
// TODO Remove hardcoded value
|
||
|
Uri contentUri = MediaStore.Audio.Playlists.Members.getContentUri(
|
||
|
"external", playlist.getId());
|
||
|
String[] projection = { MediaStore.Audio.Playlists.Members.PLAY_ORDER };
|
||
|
String sortOrder = MediaStore.Audio.Playlists.Members.PLAY_ORDER + " DESC LIMIT 1";
|
||
|
Cursor cursor = mContentResolver.query(
|
||
|
contentUri, projection, null, null, sortOrder);
|
||
|
if (cursor != null) {
|
||
|
try {
|
||
|
int playOrderColumn = cursor.getColumnIndexOrThrow(
|
||
|
MediaStore.Audio.Playlists.Members.PLAY_ORDER);
|
||
|
|
||
|
if (cursor.moveToFirst()) {
|
||
|
playOrder = cursor.getLong(playOrderColumn);
|
||
|
}
|
||
|
} finally {
|
||
|
cursor.close();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return playOrder;
|
||
|
}
|
||
|
}
|