Java tutorial
/* * Copyright (C) 2014 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.phearom.um.model; import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; import android.net.Uri; import android.support.v4.media.MediaBrowserCompat; import android.support.v4.media.MediaDescriptionCompat; import android.support.v4.media.MediaMetadataCompat; import com.phearom.um.R; import com.phearom.um.server.OnResultCallback; import com.phearom.um.server.RequestGetNewSong; import com.phearom.um.utils.LogHelper; import com.phearom.um.utils.MediaIDHelper; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import static com.phearom.um.utils.MediaIDHelper.MEDIA_ID_MUSICS_BY_GENRE; import static com.phearom.um.utils.MediaIDHelper.createMediaID; /** * Simple data provider for music tracks. The actual metadata source is delegated to a * MusicProviderSource defined by a constructor argument of this class. */ public class MusicProvider { private static final String TAG = LogHelper.makeLogTag(MusicProvider.class); // Categorized caches for music track data: private ConcurrentMap<String, List<MediaMetadataCompat>> mMusicListByGenre; private final ConcurrentMap<String, MutableMediaMetadata> mMusicListById; private final Set<String> mFavoriteTracks; private Context mContext; enum State { NON_INITIALIZED, INITIALIZING, INITIALIZED } private volatile State mCurrentState = State.NON_INITIALIZED; public interface Callback { void onMusicCatalogReady(boolean success); } public MusicProvider(Context context) { this.mContext = context; mMusicListByGenre = new ConcurrentHashMap<>(); mMusicListById = new ConcurrentHashMap<>(); mFavoriteTracks = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>()); } public Iterable<String> getGenres() { if (mCurrentState != State.INITIALIZED) { return Collections.emptyList(); } return mMusicListByGenre.keySet(); } public Iterable<MediaMetadataCompat> getShuffledMusic() { if (mCurrentState != State.INITIALIZED) { return Collections.emptyList(); } List<MediaMetadataCompat> shuffled = new ArrayList<>(mMusicListById.size()); for (MutableMediaMetadata mutableMetadata : mMusicListById.values()) { shuffled.add(mutableMetadata.metadata); } Collections.shuffle(shuffled); return shuffled; } public Iterable<MediaMetadataCompat> getMusicsByGenre(String genre) { if (mCurrentState != State.INITIALIZED || !mMusicListByGenre.containsKey(genre)) { return Collections.emptyList(); } return mMusicListByGenre.get(genre); } public Iterable<MediaMetadataCompat> searchMusicBySongTitle(String query) { return searchMusic(MediaMetadataCompat.METADATA_KEY_TITLE, query); } public Iterable<MediaMetadataCompat> searchMusicByAlbum(String query) { return searchMusic(MediaMetadataCompat.METADATA_KEY_ALBUM, query); } public Iterable<MediaMetadataCompat> searchMusicByArtist(String query) { return searchMusic(MediaMetadataCompat.METADATA_KEY_ARTIST, query); } Iterable<MediaMetadataCompat> searchMusic(String metadataField, String query) { if (mCurrentState != State.INITIALIZED) { return Collections.emptyList(); } ArrayList<MediaMetadataCompat> result = new ArrayList<>(); query = query.toLowerCase(Locale.US); for (MutableMediaMetadata track : mMusicListById.values()) { if (track.metadata.getString(metadataField).toLowerCase(Locale.US).contains(query)) { result.add(track.metadata); } } return result; } public MediaMetadataCompat getMusic(String musicId) { return mMusicListById.containsKey(musicId) ? mMusicListById.get(musicId).metadata : null; } public void updateMusicArt(String musicId, Bitmap albumArt, Bitmap icon) { MediaMetadataCompat metadata = getMusic(musicId); metadata = new MediaMetadataCompat.Builder(metadata) .putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, albumArt) .putBitmap(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON, icon).build(); MutableMediaMetadata mutableMetadata = mMusicListById.get(musicId); if (mutableMetadata == null) { throw new IllegalStateException("Unexpected error: Inconsistent data structures in " + "MusicProvider"); } mutableMetadata.metadata = metadata; } public void setFavorite(String musicId, boolean favorite) { if (favorite) { mFavoriteTracks.add(musicId); } else { mFavoriteTracks.remove(musicId); } } public boolean isInitialized() { return mCurrentState == State.INITIALIZED; } public boolean isFavorite(String musicId) { return mFavoriteTracks.contains(musicId); } /** * Get the list of music tracks from a server and caches the track information * for future reference, keying tracks by musicId and grouping by genre. */ public void retrieveMediaAsync(final Callback callback) { LogHelper.d(TAG, "retrieveMediaAsync called"); if (mCurrentState == State.INITIALIZED) { if (callback != null) { // Nothing to do, execute callback immediately callback.onMusicCatalogReady(true); } return; } RequestGetNewSong getNewSong = new RequestGetNewSong(mContext); getNewSong.setOnResultCallback(new OnResultCallback<Iterator<MediaMetadataCompat>>() { @Override public void onResult(Iterator<MediaMetadataCompat> result) { if (result != null) retrieveMedia(result); if (callback != null) callback.onMusicCatalogReady(true); } }); getNewSong.execute(); } private void buildListsByGenre() { ConcurrentMap<String, List<MediaMetadataCompat>> newMusicListByGenre = new ConcurrentHashMap<>(); for (MutableMediaMetadata m : mMusicListById.values()) { String genre = m.metadata.getString(MediaMetadataCompat.METADATA_KEY_GENRE); List<MediaMetadataCompat> list = newMusicListByGenre.get(genre); if (list == null) { list = new ArrayList<>(); newMusicListByGenre.put(genre, list); } list.add(m.metadata); } mMusicListByGenre = newMusicListByGenre; } private void retrieveMedia(Iterator<MediaMetadataCompat> tracks) { try { if (mCurrentState == State.NON_INITIALIZED) { mCurrentState = State.INITIALIZING; while (tracks.hasNext()) { MediaMetadataCompat item = tracks.next(); String musicId = item.getString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID); mMusicListById.put(musicId, new MutableMediaMetadata(musicId, item)); } buildListsByGenre(); mCurrentState = State.INITIALIZED; } } finally { if (mCurrentState != State.INITIALIZED) { mCurrentState = State.NON_INITIALIZED; } } } public List<MediaBrowserCompat.MediaItem> getChildren(String mediaId) { List<MediaBrowserCompat.MediaItem> mediaItems = new ArrayList<>(); if (!MediaIDHelper.isBrowseable(mediaId)) { return mediaItems; } for (String key : mMusicListById.keySet()) { mediaItems.add(createMediaItem(mMusicListById.get(key).metadata)); } return mediaItems; } private MediaBrowserCompat.MediaItem createBrowsableMediaItemForRoot(Resources resources) { MediaDescriptionCompat description = new MediaDescriptionCompat.Builder() .setMediaId(MEDIA_ID_MUSICS_BY_GENRE).setTitle(resources.getString(R.string.browse_genres)) .setSubtitle(resources.getString(R.string.browse_genre_subtitle)) .setIconUri(Uri.parse("android.resource://" + "com.phearom.um/drawable/ic_by_genre")).build(); return new MediaBrowserCompat.MediaItem(description, MediaBrowserCompat.MediaItem.FLAG_BROWSABLE); } private MediaBrowserCompat.MediaItem createBrowsableMediaItemForGenre(String genre, Resources resources) { MediaDescriptionCompat description = new MediaDescriptionCompat.Builder() .setMediaId(createMediaID(null, MEDIA_ID_MUSICS_BY_GENRE, genre)).setTitle(genre) .setSubtitle(resources.getString(R.string.browse_musics_by_genre_subtitle, genre)).build(); return new MediaBrowserCompat.MediaItem(description, MediaBrowserCompat.MediaItem.FLAG_BROWSABLE); } private MediaBrowserCompat.MediaItem createMediaItem(MediaMetadataCompat metadata) { String genre = metadata.getString(MediaMetadataCompat.METADATA_KEY_GENRE); String hierarchyAwareMediaID = MediaIDHelper.createMediaID(metadata.getDescription().getMediaId(), MEDIA_ID_MUSICS_BY_GENRE, genre); MediaMetadataCompat copy = new MediaMetadataCompat.Builder(metadata) .putString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID, hierarchyAwareMediaID).build(); return new MediaBrowserCompat.MediaItem(copy.getDescription(), MediaBrowserCompat.MediaItem.FLAG_PLAYABLE); } }