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.radiofarda.istgah.model; import android.content.res.Resources; import android.graphics.Bitmap; import android.os.AsyncTask; import android.support.v4.media.MediaBrowserCompat; import android.support.v4.media.MediaMetadataCompat; import com.radiofarda.istgah.bejbej.models.Episode; import com.radiofarda.istgah.utils.LogHelper; 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 io.realm.RealmResults; /** * 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); private final ConcurrentMap<String, MutableMediaMetadata> mMusicListById; private final Set<String> mFavoriteTracks; private MusicProviderSource mSource; private volatile State mCurrentState = State.NON_INITIALIZED; public MusicProvider() { this(new BejbejSource()); } public MusicProvider(MusicProviderSource source) { mSource = source; mMusicListById = new ConcurrentHashMap<>(); mFavoriteTracks = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>()); } /** * Get an iterator over a shuffled collection of all songs */ 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; } /** * Very basic implementation of a search that filter music tracks with title containing * the given query. */ public Iterable<MediaMetadataCompat> searchMusicBySongTitle(String query) { return searchMusic(MediaMetadataCompat.METADATA_KEY_TITLE, query); } /** * Very basic implementation of a search that filter music tracks with album containing * the given query. */ public Iterable<MediaMetadataCompat> searchMusicByAlbum(String query) { return searchMusic(MediaMetadataCompat.METADATA_KEY_ALBUM, query); } /** * Very basic implementation of a search that filter music tracks with artist containing * the given 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; } /** * Return the MediaMetadataCompat for the given musicID. * * @param musicId The unique, non-hierarchical music ID. */ public MediaMetadataCompat getMusic(String musicId) { return mMusicListById.containsKey(musicId) ? mMusicListById.get(musicId).metadata : null; } public synchronized void updateMusicArt(String musicId, Bitmap albumArt, Bitmap icon) { MediaMetadataCompat metadata = getMusic(musicId); metadata = new MediaMetadataCompat.Builder(metadata) // set high resolution bitmap in METADATA_KEY_ALBUM_ART. This is used, for // radiofarda, on the lockscreen background when the media session is active. .putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, albumArt) // set small version of the album art in the DISPLAY_ICON. This is used on // the MediaDescription and thus it should be small to be serialized if // necessary .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; } // Asynchronously load the music catalog in a separate thread new AsyncTask<Void, Void, State>() { @Override protected State doInBackground(Void... params) { retrieveMedia(); return mCurrentState; } @Override protected void onPostExecute(State current) { if (callback != null) { callback.onMusicCatalogReady(current == State.INITIALIZED); } } }.execute(); } private synchronized void retrieveMedia() { try { if (mCurrentState == State.NON_INITIALIZED) { mCurrentState = State.INITIALIZING; Iterator<MediaMetadataCompat> tracks = mSource.iterator(); while (tracks.hasNext()) { MediaMetadataCompat item = tracks.next(); String musicId = item.getString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID); mMusicListById.put(musicId, new MutableMediaMetadata(musicId, item)); } mCurrentState = State.INITIALIZED; } } finally { if (mCurrentState != State.INITIALIZED) { // Something bad happened, so we reset state to NON_INITIALIZED to allow // retries (eg if the network connection is temporary unavailable) mCurrentState = State.NON_INITIALIZED; } } } public List<MediaBrowserCompat.MediaItem> getChildren(String mediaId, Resources resources) { List<MediaBrowserCompat.MediaItem> mediaItems = new ArrayList<>(); RealmResults<Episode> allEpisodes = Episode.findAll(); for (Episode episode : allEpisodes) { if (mMusicListById.containsKey(episode.getId())) { mediaItems.add(createMediaItem(mMusicListById.get(episode.getId()).metadata)); } } return mediaItems; } private MediaBrowserCompat.MediaItem createMediaItem(MediaMetadataCompat metadata) { return new MediaBrowserCompat.MediaItem(metadata.getDescription(), MediaBrowserCompat.MediaItem.FLAG_PLAYABLE); } enum State { NON_INITIALIZED, INITIALIZING, INITIALIZED } public interface Callback { void onMusicCatalogReady(boolean success); } }