Advertisement
Guest User

Untitled

a guest
Dec 11th, 2016
175
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 13.21 KB | None | 0 0
  1. /*
  2. * Copyright (C) 2014 The Android Open Source Project
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16.  
  17. package org.copticchurchlibrary.arabicreader.model;
  18.  
  19. import android.content.res.Resources;
  20. import android.graphics.Bitmap;
  21. import android.net.Uri;
  22. import android.os.AsyncTask;
  23. import android.support.v4.media.MediaBrowserCompat;
  24. import android.support.v4.media.MediaDescriptionCompat;
  25. import android.support.v4.media.MediaMetadataCompat;
  26.  
  27. import org.copticchurchlibrary.arabicreader.R;
  28. import org.copticchurchlibrary.arabicreader.utils.LogHelper;
  29. import org.copticchurchlibrary.arabicreader.utils.MediaIDHelper;
  30.  
  31. import java.util.ArrayList;
  32. import java.util.Collections;
  33. import java.util.Iterator;
  34. import java.util.List;
  35. import java.util.Locale;
  36. import java.util.Set;
  37. import java.util.concurrent.ConcurrentHashMap;
  38. import java.util.concurrent.ConcurrentMap;
  39.  
  40. /**
  41. * Simple data provider for music tracks. The actual metadata source is delegated to a
  42. * MusicProviderSource defined by a constructor argument of this class.
  43. */
  44. public class MusicProvider {
  45.  
  46. private static final String TAG = LogHelper.makeLogTag(MusicProvider.class);
  47.  
  48. private MusicProviderSource mSource;
  49.  
  50. // Categorized caches for music track data:
  51. private ConcurrentMap<String, List<MediaMetadataCompat>> mMusicListByGenre;
  52. private final ConcurrentMap<String, MutableMediaMetadata> mMusicListById;
  53.  
  54. private final Set<String> mFavoriteTracks;
  55.  
  56. enum State {
  57. NON_INITIALIZED, INITIALIZING, INITIALIZED
  58. }
  59.  
  60. private volatile State mCurrentState = State.NON_INITIALIZED;
  61.  
  62. public interface Callback {
  63. void onMusicCatalogReady(boolean success);
  64. }
  65.  
  66. public MusicProvider() {
  67. this(new RemoteJSONSource());
  68. }
  69. public MusicProvider(MusicProviderSource source) {
  70. mSource = source;
  71. mMusicListByGenre = new ConcurrentHashMap<>();
  72. mMusicListById = new ConcurrentHashMap<>();
  73. mFavoriteTracks = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
  74. }
  75.  
  76. /**
  77. * Get an iterator over the list of genres
  78. *
  79. * @return genres
  80. */
  81. public Iterable<String> getGenres() {
  82. if (mCurrentState != State.INITIALIZED) {
  83. return Collections.emptyList();
  84. }
  85. return mMusicListByGenre.keySet();
  86. }
  87.  
  88. /**
  89. * Get an iterator over a shuffled collection of all songs
  90. */
  91. public Iterable<MediaMetadataCompat> getShuffledMusic() {
  92. if (mCurrentState != State.INITIALIZED) {
  93. return Collections.emptyList();
  94. }
  95. List<MediaMetadataCompat> shuffled = new ArrayList<>(mMusicListById.size());
  96. for (MutableMediaMetadata mutableMetadata: mMusicListById.values()) {
  97. shuffled.add(mutableMetadata.metadata);
  98. }
  99. Collections.shuffle(shuffled);
  100. return shuffled;
  101. }
  102.  
  103. /**
  104. * Get music tracks of the given genre
  105. *
  106. */
  107. public Iterable<MediaMetadataCompat> getMusicsByGenre(String genre) {
  108. if (mCurrentState != State.INITIALIZED || !mMusicListByGenre.containsKey(genre)) {
  109. return Collections.emptyList();
  110. }
  111. return mMusicListByGenre.get(genre);
  112. }
  113.  
  114. /**
  115. * Very basic implementation of a search that filter music tracks with title containing
  116. * the given query.
  117. *
  118. */
  119. public Iterable<MediaMetadataCompat> searchMusicBySongTitle(String query) {
  120. return searchMusic(MediaMetadataCompat.METADATA_KEY_TITLE, query);
  121. }
  122.  
  123. /**
  124. * Very basic implementation of a search that filter music tracks with album containing
  125. * the given query.
  126. *
  127. */
  128. public Iterable<MediaMetadataCompat> searchMusicByAlbum(String query) {
  129. return searchMusic(MediaMetadataCompat.METADATA_KEY_ALBUM, query);
  130. }
  131.  
  132. /**
  133. * Very basic implementation of a search that filter music tracks with artist containing
  134. * the given query.
  135. *
  136. */
  137. public Iterable<MediaMetadataCompat> searchMusicByArtist(String query) {
  138. return searchMusic(MediaMetadataCompat.METADATA_KEY_ARTIST, query);
  139. }
  140.  
  141. Iterable<MediaMetadataCompat> searchMusic(String metadataField, String query) {
  142. if (mCurrentState != State.INITIALIZED) {
  143. return Collections.emptyList();
  144. }
  145. ArrayList<MediaMetadataCompat> result = new ArrayList<>();
  146. query = query.toLowerCase(Locale.US);
  147. for (MutableMediaMetadata track : mMusicListById.values()) {
  148. if (track.metadata.getString(metadataField).toLowerCase(Locale.US)
  149. .contains(query)) {
  150. result.add(track.metadata);
  151. }
  152. }
  153. return result;
  154. }
  155.  
  156.  
  157. /**
  158. * Return the MediaMetadataCompat for the given musicID.
  159. *
  160. * @param musicId The unique, non-hierarchical music ID.
  161. */
  162. public MediaMetadataCompat getMusic(String musicId) {
  163. return mMusicListById.containsKey(musicId) ? mMusicListById.get(musicId).metadata : null;
  164. }
  165.  
  166. public synchronized void updateMusicArt(String musicId, Bitmap albumArt, Bitmap icon) {
  167. MediaMetadataCompat metadata = getMusic(musicId);
  168. metadata = new MediaMetadataCompat.Builder(metadata)
  169.  
  170. // set high resolution bitmap in METADATA_KEY_ALBUM_ART. This is used, for
  171. // example, on the lockscreen background when the media session is active.
  172. .putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, albumArt)
  173.  
  174. // set small version of the album art in the DISPLAY_ICON. This is used on
  175. // the MediaDescription and thus it should be small to be serialized if
  176. // necessary
  177. .putBitmap(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON, icon)
  178.  
  179. .build();
  180.  
  181. MutableMediaMetadata mutableMetadata = mMusicListById.get(musicId);
  182. if (mutableMetadata == null) {
  183. throw new IllegalStateException("Unexpected error: Inconsistent data structures in " +
  184. "MusicProvider");
  185. }
  186.  
  187. mutableMetadata.metadata = metadata;
  188. }
  189.  
  190. public void setFavorite(String musicId, boolean favorite) {
  191. if (favorite) {
  192. mFavoriteTracks.add(musicId);
  193. } else {
  194. mFavoriteTracks.remove(musicId);
  195. }
  196. }
  197.  
  198. public boolean isInitialized() {
  199. return mCurrentState == State.INITIALIZED;
  200. }
  201.  
  202. public boolean isFavorite(String musicId) {
  203. return mFavoriteTracks.contains(musicId);
  204. }
  205.  
  206. /**
  207. * Get the list of music tracks from a server and caches the track information
  208. * for future reference, keying tracks by musicId and grouping by genre.
  209. */
  210. public void retrieveMediaAsync(final Callback callback) {
  211. LogHelper.d(TAG, "retrieveMediaAsync called");
  212. if (mCurrentState == State.INITIALIZED) {
  213. if (callback != null) {
  214. // Nothing to do, execute callback immediately
  215. callback.onMusicCatalogReady(true);
  216. }
  217. return;
  218. }
  219.  
  220. // Asynchronously load the music catalog in a separate thread
  221. new AsyncTask<Void, Void, State>() {
  222. @Override
  223. protected State doInBackground(Void... params) {
  224. retrieveMedia();
  225. return mCurrentState;
  226. }
  227.  
  228. @Override
  229. protected void onPostExecute(State current) {
  230. if (callback != null) {
  231. callback.onMusicCatalogReady(current == State.INITIALIZED);
  232. }
  233. }
  234. }.execute();
  235. }
  236.  
  237. private synchronized void buildListsByGenre() {
  238. ConcurrentMap<String, List<MediaMetadataCompat>> newMusicListByGenre = new ConcurrentHashMap<>();
  239.  
  240. for (MutableMediaMetadata m : mMusicListById.values()) {
  241. String genre = m.metadata.getString(MediaMetadataCompat.METADATA_KEY_GENRE);
  242. List<MediaMetadataCompat> list = newMusicListByGenre.get(genre);
  243. if (list == null) {
  244. list = new ArrayList<>();
  245. newMusicListByGenre.put(genre, list);
  246. }
  247. list.add(m.metadata);
  248. }
  249. mMusicListByGenre = newMusicListByGenre;
  250. }
  251.  
  252. private synchronized void retrieveMedia() {
  253. try {
  254. if (mCurrentState == State.NON_INITIALIZED) {
  255. mCurrentState = State.INITIALIZING;
  256.  
  257. Iterator<MediaMetadataCompat> tracks = mSource.iterator();
  258. while (tracks.hasNext()) {
  259. MediaMetadataCompat item = tracks.next();
  260. String musicId = item.getString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID);
  261. mMusicListById.put(musicId, new MutableMediaMetadata(musicId, item));
  262. }
  263. buildListsByGenre();
  264. mCurrentState = State.INITIALIZED;
  265. }
  266. } finally {
  267. if (mCurrentState != State.INITIALIZED) {
  268. // Something bad happened, so we reset state to NON_INITIALIZED to allow
  269. // retries (eg if the network connection is temporary unavailable)
  270. mCurrentState = State.NON_INITIALIZED;
  271. }
  272. }
  273. }
  274.  
  275.  
  276. public List<MediaBrowserCompat.MediaItem> getChildren(String mediaId, Resources resources) {
  277. List<MediaBrowserCompat.MediaItem> mediaItems = new ArrayList<>();
  278.  
  279. if (!MediaIDHelper.isBrowseable(mediaId)) {
  280. return mediaItems;
  281. }
  282.  
  283. if (MediaIDHelper.MEDIA_ID_ROOT.equals(mediaId)) {
  284. mediaItems.add(createBrowsableMediaItemForRoot(resources));
  285.  
  286. } else if (MediaIDHelper.MEDIA_ID_MUSICS_BY_GENRE.equals(mediaId)) {
  287. for (String genre : getGenres()) {
  288. mediaItems.add(createBrowsableMediaItemForGenre(genre, resources));
  289. }
  290.  
  291. } else if (mediaId.startsWith(MediaIDHelper.MEDIA_ID_MUSICS_BY_GENRE)) {
  292. String genre = MediaIDHelper.getHierarchy(mediaId)[1];
  293. for (MediaMetadataCompat metadata : getMusicsByGenre(genre)) {
  294. mediaItems.add(createMediaItem(metadata));
  295. }
  296.  
  297. } else {
  298. LogHelper.w(TAG, "Skipping unmatched mediaId: ", mediaId);
  299. }
  300. return mediaItems;
  301. }
  302.  
  303. private MediaBrowserCompat.MediaItem createBrowsableMediaItemForRoot(Resources resources) {
  304. MediaDescriptionCompat description = new MediaDescriptionCompat.Builder()
  305. .setMediaId(MediaIDHelper.MEDIA_ID_MUSICS_BY_GENRE)
  306. .setTitle(resources.getString(R.string.browse_genres))
  307. .setSubtitle(resources.getString(R.string.browse_genre_subtitle))
  308. .setIconUri(Uri.parse("android.resource://" +
  309. "com.example.android.uamp/drawable/ic_by_genre"))
  310. .build();
  311. return new MediaBrowserCompat.MediaItem(description,
  312. MediaBrowserCompat.MediaItem.FLAG_BROWSABLE);
  313. }
  314.  
  315. private MediaBrowserCompat.MediaItem createBrowsableMediaItemForGenre(String genre,
  316. Resources resources) {
  317. MediaDescriptionCompat description = new MediaDescriptionCompat.Builder()
  318. .setMediaId(MediaIDHelper.createMediaID(null, MediaIDHelper.MEDIA_ID_MUSICS_BY_GENRE, genre))
  319. .setTitle(genre)
  320. .setSubtitle(resources.getString(
  321. R.string.browse_musics_by_genre_subtitle, genre))
  322. .build();
  323. return new MediaBrowserCompat.MediaItem(description,
  324. MediaBrowserCompat.MediaItem.FLAG_BROWSABLE);
  325. }
  326.  
  327. private MediaBrowserCompat.MediaItem createMediaItem(MediaMetadataCompat metadata) {
  328. // Since mediaMetadata fields are immutable, we need to create a copy, so we
  329. // can set a hierarchy-aware mediaID. We will need to know the media hierarchy
  330. // when we get a onPlayFromMusicID call, so we can create the proper queue based
  331. // on where the music was selected from (by artist, by genre, random, etc)
  332. String genre = metadata.getString(MediaMetadataCompat.METADATA_KEY_GENRE);
  333. String hierarchyAwareMediaID = MediaIDHelper.createMediaID(
  334. metadata.getDescription().getMediaId(), MediaIDHelper.MEDIA_ID_MUSICS_BY_GENRE, genre);
  335. MediaMetadataCompat copy = new MediaMetadataCompat.Builder(metadata)
  336. .putString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID, hierarchyAwareMediaID)
  337. .build();
  338. return new MediaBrowserCompat.MediaItem(copy.getDescription(),
  339. MediaBrowserCompat.MediaItem.FLAG_PLAYABLE);
  340.  
  341. }
  342.  
  343. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement