1f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He/*
2f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He * Copyright (C) 2017 The Android Open Source Project
3f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He *
4f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He * Licensed under the Apache License, Version 2.0 (the "License");
5f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He * you may not use this file except in compliance with the License.
6f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He * You may obtain a copy of the License at
7f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He *
8f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He *      http://www.apache.org/licenses/LICENSE-2.0
9f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He *
10f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He * Unless required by applicable law or agreed to in writing, software
11f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He * distributed under the License is distributed on an "AS IS" BASIS,
12f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He * See the License for the specific language governing permissions and
14f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He * limitations under the License.
15f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He */
16f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He
17f02d3c605f5d1d091ca7e27170010ad23cd22134Jack Hepackage com.android.music.utils;
18f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He
1996716cec6a26e31c41d514d81afe669e8824bd5cAjay Panickerimport static android.Manifest.permission.READ_EXTERNAL_STORAGE;
2096716cec6a26e31c41d514d81afe669e8824bd5cAjay Panicker
21f02d3c605f5d1d091ca7e27170010ad23cd22134Jack Heimport android.content.ContentResolver;
22f02d3c605f5d1d091ca7e27170010ad23cd22134Jack Heimport android.content.ContentUris;
23f02d3c605f5d1d091ca7e27170010ad23cd22134Jack Heimport android.content.ContentValues;
24f02d3c605f5d1d091ca7e27170010ad23cd22134Jack Heimport android.content.Context;
2596716cec6a26e31c41d514d81afe669e8824bd5cAjay Panickerimport android.content.pm.PackageManager;
26f02d3c605f5d1d091ca7e27170010ad23cd22134Jack Heimport android.database.Cursor;
27f02d3c605f5d1d091ca7e27170010ad23cd22134Jack Heimport android.graphics.Bitmap;
28f02d3c605f5d1d091ca7e27170010ad23cd22134Jack Heimport android.graphics.BitmapFactory;
29f02d3c605f5d1d091ca7e27170010ad23cd22134Jack Heimport android.graphics.drawable.BitmapDrawable;
30f02d3c605f5d1d091ca7e27170010ad23cd22134Jack Heimport android.media.MediaActionSound;
31f02d3c605f5d1d091ca7e27170010ad23cd22134Jack Heimport android.media.MediaMetadata;
32f02d3c605f5d1d091ca7e27170010ad23cd22134Jack Heimport android.media.MediaMetadataRetriever;
33f02d3c605f5d1d091ca7e27170010ad23cd22134Jack Heimport android.net.Uri;
34f02d3c605f5d1d091ca7e27170010ad23cd22134Jack Heimport android.os.AsyncTask;
35f02d3c605f5d1d091ca7e27170010ad23cd22134Jack Heimport android.provider.MediaStore;
36f02d3c605f5d1d091ca7e27170010ad23cd22134Jack Heimport android.util.Log;
37f02d3c605f5d1d091ca7e27170010ad23cd22134Jack Heimport com.android.music.MediaPlaybackService;
38f02d3c605f5d1d091ca7e27170010ad23cd22134Jack Heimport com.android.music.MusicUtils;
39f02d3c605f5d1d091ca7e27170010ad23cd22134Jack Heimport com.android.music.R;
40f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He
41f02d3c605f5d1d091ca7e27170010ad23cd22134Jack Heimport java.io.File;
42f02d3c605f5d1d091ca7e27170010ad23cd22134Jack Heimport java.util.*;
43f02d3c605f5d1d091ca7e27170010ad23cd22134Jack Heimport java.util.concurrent.ConcurrentHashMap;
44f02d3c605f5d1d091ca7e27170010ad23cd22134Jack Heimport java.util.concurrent.ConcurrentMap;
45f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He
46f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He/*
47f02d3c605f5d1d091ca7e27170010ad23cd22134Jack HeA provider of music contents to the music application, it reads external storage for any music
48f02d3c605f5d1d091ca7e27170010ad23cd22134Jack Hefiles, parse them and
49f02d3c605f5d1d091ca7e27170010ad23cd22134Jack Hestore them in this class for future use.
50f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He */
51f02d3c605f5d1d091ca7e27170010ad23cd22134Jack Hepublic class MusicProvider {
52f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    private static final String TAG = "MusicProvider";
53f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He
54f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    // Public constants
55f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    public static final String UNKOWN = "UNKNOWN";
56f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    // Uri source of this track
57f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    public static final String CUSTOM_METADATA_TRACK_SOURCE = "__SOURCE__";
58f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    // Sort key for this tack
59f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    public static final String CUSTOM_METADATA_SORT_KEY = "__SORT_KEY__";
60f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He
61f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    // Content select criteria
62f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    private static final String MUSIC_SELECT_FILTER = MediaStore.Audio.Media.IS_MUSIC + " != 0";
63f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    private static final String MUSIC_SORT_ORDER = MediaStore.Audio.Media.TITLE + " ASC";
64f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He
65f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    // Categorized caches for music track data:
66f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    private Context mContext;
67f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    // Album Name --> list of Metadata
68f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    private ConcurrentMap<String, List<MediaMetadata>> mMusicListByAlbum;
69f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    // Playlist Name --> list of Metadata
70f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    private ConcurrentMap<String, List<MediaMetadata>> mMusicListByPlaylist;
71f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    // Artist Name --> Map of (album name --> album metadata)
72f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    private ConcurrentMap<String, Map<String, MediaMetadata>> mArtistAlbumDb;
73f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    private List<MediaMetadata> mMusicList;
74f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    private final ConcurrentMap<Long, Song> mMusicListById;
75f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    private final ConcurrentMap<String, Song> mMusicListByMediaId;
76f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He
77f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    enum State { NON_INITIALIZED, INITIALIZING, INITIALIZED }
78f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He
79f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    private volatile State mCurrentState = State.NON_INITIALIZED;
80f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He
81f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    public MusicProvider(Context context) {
82f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        mContext = context;
83f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        mArtistAlbumDb = new ConcurrentHashMap<>();
84f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        mMusicListByAlbum = new ConcurrentHashMap<>();
85f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        mMusicListByPlaylist = new ConcurrentHashMap<>();
86f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        mMusicListById = new ConcurrentHashMap<>();
87f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        mMusicList = new ArrayList<>();
88f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        mMusicListByMediaId = new ConcurrentHashMap<>();
89f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        mMusicListByPlaylist.put(MediaIDHelper.MEDIA_ID_NOW_PLAYING, new ArrayList<>());
90f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    }
91f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He
92f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    public boolean isInitialized() {
93f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        return mCurrentState == State.INITIALIZED;
94f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    }
95f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He
96f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    /**
97f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He     * Get an iterator over the list of artists
98f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He     *
99f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He     * @return list of artists
100f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He     */
101f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    public Iterable<String> getArtists() {
102f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        if (mCurrentState != State.INITIALIZED) {
103f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            return Collections.emptyList();
104f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        }
105f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        return mArtistAlbumDb.keySet();
106f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    }
107f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He
108f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    /**
109f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He     * Get an iterator over the list of albums
110f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He     *
111f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He     * @return list of albums
112f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He     */
113f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    public Iterable<MediaMetadata> getAlbums() {
114f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        if (mCurrentState != State.INITIALIZED) {
115f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            return Collections.emptyList();
116f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        }
117f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        ArrayList<MediaMetadata> albumList = new ArrayList<>();
118f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        for (Map<String, MediaMetadata> artist_albums : mArtistAlbumDb.values()) {
119f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            albumList.addAll(artist_albums.values());
120f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        }
121f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        return albumList;
122f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    }
123f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He
124f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    /**
125f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He     * Get an iterator over the list of playlists
126f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He     *
127f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He     * @return list of playlists
128f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He     */
129f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    public Iterable<String> getPlaylists() {
130f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        if (mCurrentState != State.INITIALIZED) {
131f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            return Collections.emptyList();
132f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        }
133f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        return mMusicListByPlaylist.keySet();
134f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    }
135f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He
136f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    public Iterable<MediaMetadata> getMusicList() {
137f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        return mMusicList;
138f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    }
139f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He
140f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    /**
141f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He     * Get albums of a certain artist
142f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He     *
143f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He     */
144f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    public Iterable<MediaMetadata> getAlbumByArtist(String artist) {
145f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        if (mCurrentState != State.INITIALIZED || !mArtistAlbumDb.containsKey(artist)) {
146f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            return Collections.emptyList();
147f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        }
148f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        return mArtistAlbumDb.get(artist).values();
149f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    }
150f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He
151f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    /**
152f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He     * Get music tracks of the given album
153f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He     *
154f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He     */
155f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    public Iterable<MediaMetadata> getMusicsByAlbum(String album) {
156f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        if (mCurrentState != State.INITIALIZED || !mMusicListByAlbum.containsKey(album)) {
157f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            return Collections.emptyList();
158f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        }
159f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        return mMusicListByAlbum.get(album);
160f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    }
161f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He
162f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    /**
163f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He     * Get music tracks of the given playlist
164f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He     *
165f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He     */
166f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    public Iterable<MediaMetadata> getMusicsByPlaylist(String playlist) {
167f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        if (mCurrentState != State.INITIALIZED || !mMusicListByPlaylist.containsKey(playlist)) {
168f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            return Collections.emptyList();
169f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        }
170f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        return mMusicListByPlaylist.get(playlist);
171f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    }
172f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He
173f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    /**
174f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He     * Return the MediaMetadata for the given musicID.
175f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He     *
176f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He     * @param musicId The unique, non-hierarchical music ID.
177f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He     */
178f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    public Song getMusicById(long musicId) {
179f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        return mMusicListById.containsKey(musicId) ? mMusicListById.get(musicId) : null;
180f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    }
181f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He
182f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    /**
183f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He     * Return the MediaMetadata for the given musicID.
184f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He     *
185f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He     * @param musicId The unique, non-hierarchical music ID.
186f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He     */
187f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    public Song getMusicByMediaId(String musicId) {
188f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        return mMusicListByMediaId.containsKey(musicId) ? mMusicListByMediaId.get(musicId) : null;
189f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    }
190f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He
191f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    /**
192f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He     * Very basic implementation of a search that filter music tracks which title containing
193f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He     * the given query.
194f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He     *
195f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He     */
196f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    public Iterable<MediaMetadata> searchMusic(String titleQuery) {
197f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        if (mCurrentState != State.INITIALIZED) {
198f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            return Collections.emptyList();
199f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        }
200f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        ArrayList<MediaMetadata> result = new ArrayList<>();
201f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        titleQuery = titleQuery.toLowerCase();
202f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        for (Song song : mMusicListByMediaId.values()) {
203f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            if (song.getMetadata()
204f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He                            .getString(MediaMetadata.METADATA_KEY_TITLE)
205f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He                            .toLowerCase()
206f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He                            .contains(titleQuery)) {
207f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He                result.add(song.getMetadata());
208f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            }
209f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        }
210f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        return result;
211f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    }
212f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He
213f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    public interface MusicProviderCallback { void onMusicCatalogReady(boolean success); }
214f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He
215f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    /**
216f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He     * Get the list of music tracks from disk and caches the track information
217f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He     * for future reference, keying tracks by musicId and grouping by genre.
218f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He     */
219f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    public void retrieveMediaAsync(final MusicProviderCallback callback) {
220f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        Log.d(TAG, "retrieveMediaAsync called");
221f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        if (mCurrentState == State.INITIALIZED) {
222f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            // Nothing to do, execute callback immediately
223f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            callback.onMusicCatalogReady(true);
224f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            return;
225f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        }
226f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He
227f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        // Asynchronously load the music catalog in a separate thread
228f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        new AsyncTask<Void, Void, State>() {
229f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            @Override
230f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            protected State doInBackground(Void... params) {
2312d11f2cdcab1246f3db0438e061811d40e483205Jaekyun Seok                if (mCurrentState == State.INITIALIZED) {
2322d11f2cdcab1246f3db0438e061811d40e483205Jaekyun Seok                    return mCurrentState;
2332d11f2cdcab1246f3db0438e061811d40e483205Jaekyun Seok                }
234f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He                mCurrentState = State.INITIALIZING;
235f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He                if (retrieveMedia()) {
236f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He                    mCurrentState = State.INITIALIZED;
237f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He                } else {
238f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He                    mCurrentState = State.NON_INITIALIZED;
239f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He                }
240f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He                return mCurrentState;
241f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            }
242f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He
243f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            @Override
244f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            protected void onPostExecute(State current) {
245f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He                if (callback != null) {
246f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He                    callback.onMusicCatalogReady(current == State.INITIALIZED);
247f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He                }
248f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            }
249f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        }
250f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He                .execute();
251f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    }
252f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He
253f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    public synchronized boolean retrieveAllPlayLists() {
254f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        Cursor cursor = mContext.getContentResolver().query(
255f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He                MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI, null, null, null, null);
256f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        if (cursor == null) {
257f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            Log.e(TAG, "Failed to retreive playlist: cursor is null");
258f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            return false;
259f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        }
260f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        if (!cursor.moveToFirst()) {
261f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            Log.d(TAG, "Failed to move cursor to first row (no query result)");
262f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            cursor.close();
263f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            return true;
264f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        }
265f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        int idColumn = cursor.getColumnIndex(MediaStore.Audio.Playlists._ID);
266f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        int nameColumn = cursor.getColumnIndex(MediaStore.Audio.Playlists.NAME);
267f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        int pathColumn = cursor.getColumnIndex(MediaStore.Audio.Playlists.DATA);
268f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        do {
269f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            long thisId = cursor.getLong(idColumn);
270f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            String thisPath = cursor.getString(pathColumn);
271f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            String thisName = cursor.getString(nameColumn);
272f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            Log.i(TAG, "PlayList ID: " + thisId + " Name: " + thisName);
273f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            List<MediaMetadata> songList = retreivePlaylistMetadata(thisId, thisPath);
274f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            LogHelper.i(TAG, "Found ", songList.size(), " items for playlist name: ", thisName);
275f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            mMusicListByPlaylist.put(thisName, songList);
276f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        } while (cursor.moveToNext());
277f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        cursor.close();
278f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        return true;
279f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    }
280f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He
281f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    public synchronized List<MediaMetadata> retreivePlaylistMetadata(
282f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            long playlistId, String playlistPath) {
283f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        Cursor cursor = mContext.getContentResolver().query(Uri.parse(playlistPath), null,
284f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He                MediaStore.Audio.Playlists.Members.PLAYLIST_ID + " == " + playlistId, null, null);
285f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        if (cursor == null) {
286f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            Log.e(TAG, "Failed to retreive individual playlist: cursor is null");
287f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            return null;
288f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        }
289f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        if (!cursor.moveToFirst()) {
290f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            Log.d(TAG, "Failed to move cursor to first row (no query result for playlist)");
291f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            cursor.close();
292f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            return null;
293f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        }
294f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        List<Song> songList = new ArrayList<>();
295f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        int idColumn = cursor.getColumnIndex(MediaStore.Audio.Playlists.Members._ID);
296f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        int audioIdColumn = cursor.getColumnIndex(MediaStore.Audio.Playlists.Members.AUDIO_ID);
297f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        int orderColumn = cursor.getColumnIndex(MediaStore.Audio.Playlists.Members.PLAY_ORDER);
298f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        int audioPathColumn = cursor.getColumnIndex(MediaStore.Audio.Playlists.Members.DATA);
299f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        int audioNameColumn = cursor.getColumnIndex(MediaStore.Audio.Playlists.Members.TITLE);
300f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        do {
301f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            long thisId = cursor.getLong(idColumn);
302f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            long thisAudioId = cursor.getLong(audioIdColumn);
303f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            long thisOrder = cursor.getLong(orderColumn);
304f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            String thisAudioPath = cursor.getString(audioPathColumn);
305f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            Log.i(TAG,
306f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He                    "Playlist ID: " + playlistId + " Music ID: " + thisAudioId
307f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He                            + " Name: " + audioNameColumn);
308f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            if (!mMusicListById.containsKey(thisAudioId)) {
309f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He                LogHelper.d(TAG, "Music does not exist");
310f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He                continue;
311f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            }
312f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            Song song = mMusicListById.get(thisAudioId);
313f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            song.setSortKey(thisOrder);
314f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            songList.add(song);
315f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        } while (cursor.moveToNext());
316f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        cursor.close();
317f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        songList.sort(new Comparator<Song>() {
318f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            @Override
319f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            public int compare(Song s1, Song s2) {
320f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He                long key1 = s1.getSortKey();
321f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He                long key2 = s2.getSortKey();
322f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He                if (key1 < key2) {
323f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He                    return -1;
324f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He                } else if (key1 == key2) {
325f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He                    return 0;
326f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He                } else {
327f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He                    return 1;
328f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He                }
329f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            }
330f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        });
331f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        List<MediaMetadata> metadataList = new ArrayList<>();
332f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        for (Song song : songList) {
333f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            metadataList.add(song.getMetadata());
334f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        }
335f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        return metadataList;
336f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    }
337f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He
338f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    private synchronized boolean retrieveMedia() {
33996716cec6a26e31c41d514d81afe669e8824bd5cAjay Panicker        if (mContext.checkSelfPermission(READ_EXTERNAL_STORAGE)
34096716cec6a26e31c41d514d81afe669e8824bd5cAjay Panicker                != PackageManager.PERMISSION_GRANTED) {
34196716cec6a26e31c41d514d81afe669e8824bd5cAjay Panicker            return false;
34296716cec6a26e31c41d514d81afe669e8824bd5cAjay Panicker        }
34396716cec6a26e31c41d514d81afe669e8824bd5cAjay Panicker
344f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        Cursor cursor =
345f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He                mContext.getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
346f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He                        null, MUSIC_SELECT_FILTER, null, MUSIC_SORT_ORDER);
347f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        if (cursor == null) {
348f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            Log.e(TAG, "Failed to retreive music: cursor is null");
349f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            mCurrentState = State.NON_INITIALIZED;
350f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            return false;
351f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        }
352f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        if (!cursor.moveToFirst()) {
353f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            Log.d(TAG, "Failed to move cursor to first row (no query result)");
354f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            cursor.close();
355f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            return true;
356f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        }
357f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        int idColumn = cursor.getColumnIndex(MediaStore.Audio.Media._ID);
358f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        int titleColumn = cursor.getColumnIndex(MediaStore.Audio.Media.TITLE);
359f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        int pathColumn = cursor.getColumnIndex(MediaStore.Audio.Media.DATA);
360f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        do {
361f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            Log.i(TAG,
362f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He                    "Music ID: " + cursor.getString(idColumn)
363f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He                            + " Title: " + cursor.getString(titleColumn));
364f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            long thisId = cursor.getLong(idColumn);
365f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            String thisPath = cursor.getString(pathColumn);
366f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            MediaMetadata metadata = retrievMediaMetadata(thisId, thisPath);
367f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            Log.i(TAG, "MediaMetadata: " + metadata);
368f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            if (metadata == null) {
369f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He                continue;
370f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            }
371f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            Song thisSong = new Song(thisId, metadata, null);
372f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            // Construct per feature database
373f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            mMusicList.add(metadata);
374f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            mMusicListById.put(thisId, thisSong);
375f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            mMusicListByMediaId.put(String.valueOf(thisId), thisSong);
376f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            addMusicToAlbumList(metadata);
377f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            addMusicToArtistList(metadata);
378f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        } while (cursor.moveToNext());
379f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        cursor.close();
380f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        return true;
381f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    }
382f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He
383f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    private synchronized MediaMetadata retrievMediaMetadata(long musicId, String musicPath) {
384f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        LogHelper.d(TAG, "getting metadata for music: ", musicPath);
385f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        MediaMetadataRetriever retriever = new MediaMetadataRetriever();
386f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        Uri contentUri = ContentUris.withAppendedId(
387f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He                android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, musicId);
388f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        if (!(new File(musicPath).exists())) {
389f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            LogHelper.d(TAG, "Does not exist, deleting item");
390f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            mContext.getContentResolver().delete(contentUri, null, null);
391f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            return null;
392f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        }
393f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        retriever.setDataSource(mContext, contentUri);
394f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        String title = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE);
395f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        String album = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ALBUM);
396f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        String artist = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ARTIST);
397f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        String durationString =
398f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He                retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
399f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        long duration = durationString != null ? Long.parseLong(durationString) : 0;
400f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        MediaMetadata.Builder metadataBuilder =
401f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He                new MediaMetadata.Builder()
402f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He                        .putString(MediaMetadata.METADATA_KEY_MEDIA_ID, String.valueOf(musicId))
403f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He                        .putString(CUSTOM_METADATA_TRACK_SOURCE, musicPath)
404f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He                        .putString(MediaMetadata.METADATA_KEY_TITLE, title != null ? title : UNKOWN)
405f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He                        .putString(MediaMetadata.METADATA_KEY_ALBUM, album != null ? album : UNKOWN)
406f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He                        .putString(
407f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He                                MediaMetadata.METADATA_KEY_ARTIST, artist != null ? artist : UNKOWN)
408f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He                        .putLong(MediaMetadata.METADATA_KEY_DURATION, duration);
409f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        byte[] albumArtData = retriever.getEmbeddedPicture();
410f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        Bitmap bitmap;
411f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        if (albumArtData != null) {
412f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            bitmap = BitmapFactory.decodeByteArray(albumArtData, 0, albumArtData.length);
413f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            bitmap = MusicUtils.resizeBitmap(bitmap, getDefaultAlbumArt());
414f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            metadataBuilder.putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, bitmap);
415f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        }
416f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        retriever.release();
417f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        return metadataBuilder.build();
418f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    }
419f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He
420f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    private Bitmap getDefaultAlbumArt() {
421f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        BitmapFactory.Options opts = new BitmapFactory.Options();
422f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        opts.inPreferredConfig = Bitmap.Config.ARGB_8888;
423f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        return BitmapFactory.decodeStream(
424f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He                mContext.getResources().openRawResource(R.drawable.albumart_mp_unknown), null,
425f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He                opts);
426f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    }
427f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He
428f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    private void addMusicToAlbumList(MediaMetadata metadata) {
429f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        String thisAlbum = metadata.getString(MediaMetadata.METADATA_KEY_ALBUM);
430f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        if (thisAlbum == null) {
431f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            thisAlbum = UNKOWN;
432f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        }
433f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        if (!mMusicListByAlbum.containsKey(thisAlbum)) {
434f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            mMusicListByAlbum.put(thisAlbum, new ArrayList<>());
435f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        }
436f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        mMusicListByAlbum.get(thisAlbum).add(metadata);
437f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    }
438f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He
439f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    private void addMusicToArtistList(MediaMetadata metadata) {
440f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        String thisArtist = metadata.getString(MediaMetadata.METADATA_KEY_ARTIST);
441f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        if (thisArtist == null) {
442f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            thisArtist = UNKOWN;
443f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        }
444f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        String thisAlbum = metadata.getString(MediaMetadata.METADATA_KEY_ALBUM);
445f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        if (thisAlbum == null) {
446f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            thisAlbum = UNKOWN;
447f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        }
448f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        if (!mArtistAlbumDb.containsKey(thisArtist)) {
449f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            mArtistAlbumDb.put(thisArtist, new ConcurrentHashMap<>());
450f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        }
451f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        Map<String, MediaMetadata> albumsMap = mArtistAlbumDb.get(thisArtist);
452f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        MediaMetadata.Builder builder;
453f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        long count = 0;
454f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        Bitmap thisAlbumArt = metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
455f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        if (albumsMap.containsKey(thisAlbum)) {
456f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            MediaMetadata album_metadata = albumsMap.get(thisAlbum);
457f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            count = album_metadata.getLong(MediaMetadata.METADATA_KEY_NUM_TRACKS);
458f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            Bitmap nAlbumArt = album_metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
459f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            builder = new MediaMetadata.Builder(album_metadata);
460f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            if (nAlbumArt != null) {
461f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He                thisAlbumArt = null;
462f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            }
463f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        } else {
464f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            builder = new MediaMetadata.Builder();
465f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            builder.putString(MediaMetadata.METADATA_KEY_ALBUM, thisAlbum)
466f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He                    .putString(MediaMetadata.METADATA_KEY_ARTIST, thisArtist);
467f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        }
468f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        if (thisAlbumArt != null) {
469f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            builder.putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, thisAlbumArt);
470f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        }
471f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        builder.putLong(MediaMetadata.METADATA_KEY_NUM_TRACKS, count + 1);
472f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        albumsMap.put(thisAlbum, builder.build());
473f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    }
474f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He
475f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    public synchronized void updateMusic(String musicId, MediaMetadata metadata) {
476f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        Song song = mMusicListByMediaId.get(musicId);
477f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        if (song == null) {
478f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            return;
479f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        }
480f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He
481f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        String oldGenre = song.getMetadata().getString(MediaMetadata.METADATA_KEY_GENRE);
482f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        String newGenre = metadata.getString(MediaMetadata.METADATA_KEY_GENRE);
483f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He
484f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        song.setMetadata(metadata);
485f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He
486f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        // if genre has changed, we need to rebuild the list by genre
487f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        if (!oldGenre.equals(newGenre)) {
488f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He            //            buildListsByGenre();
489f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He        }
490f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He    }
4912d11f2cdcab1246f3db0438e061811d40e483205Jaekyun Seok}
492