Here's a list of changes/features: https://github.com/RetroMusicPlayer/RetroMusicPlayer/releases/tag/v5.0

Internal Changes:
1) Migrated to ViewBinding
2) Migrated to Glide V4
3) Migrated to kotlin version of Material Dialogs
This commit is contained in:
Prathamesh More 2021-09-09 00:00:20 +05:30
parent fc42767031
commit bce6dbfa27
421 changed files with 13285 additions and 5757 deletions

View file

@ -0,0 +1,101 @@
/*
* Copyright (c) 2019 Hemanth Savarala.
*
* Licensed under the GNU General Public License v3
*
* This is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*/
package code.name.monkey.retromusic.auto;
import androidx.annotation.NonNull;
/**
* Created by Beesham Sarendranauth (Beesham)
*/
public class AutoMediaIDHelper {
// Media IDs used on browseable items of MediaBrowser
public static final String MEDIA_ID_EMPTY_ROOT = "__EMPTY_ROOT__";
public static final String MEDIA_ID_ROOT = "__ROOT__";
public static final String MEDIA_ID_MUSICS_BY_SEARCH = "__BY_SEARCH__"; // TODO
public static final String MEDIA_ID_MUSICS_BY_HISTORY = "__BY_HISTORY__";
public static final String MEDIA_ID_MUSICS_BY_TOP_TRACKS = "__BY_TOP_TRACKS__";
public static final String MEDIA_ID_MUSICS_BY_SUGGESTIONS = "__BY_SUGGESTIONS__";
public static final String MEDIA_ID_MUSICS_BY_PLAYLIST = "__BY_PLAYLIST__";
public static final String MEDIA_ID_MUSICS_BY_ALBUM = "__BY_ALBUM__";
public static final String MEDIA_ID_MUSICS_BY_ARTIST = "__BY_ARTIST__";
public static final String MEDIA_ID_MUSICS_BY_ALBUM_ARTIST = "__BY_ALBUM_ARTIST__";
public static final String MEDIA_ID_MUSICS_BY_GENRE = "__BY_GENRE__";
public static final String MEDIA_ID_MUSICS_BY_SHUFFLE = "__BY_SHUFFLE__";
public static final String MEDIA_ID_MUSICS_BY_QUEUE = "__BY_QUEUE__";
private static final String CATEGORY_SEPARATOR = "__/__";
private static final String LEAF_SEPARATOR = "__|__";
/**
* Create a String value that represents a playable or a browsable media.
* <p/>
* Encode the media browseable categories, if any, and the unique music ID, if any,
* into a single String mediaID.
* <p/>
* MediaIDs are of the form <categoryType>__/__<categoryValue>__|__<musicUniqueId>, to make it
* easy to find the category (like genre) that a music was selected from, so we
* can correctly build the playing queue. This is specially useful when
* one music can appear in more than one list, like "by genre -> genre_1"
* and "by artist -> artist_1".
*
* @param mediaID Unique ID for playable items, or null for browseable items.
* @param categories Hierarchy of categories representing this item's browsing parents.
* @return A hierarchy-aware media ID.
*/
public static String createMediaID(String mediaID, String... categories) {
StringBuilder sb = new StringBuilder();
if (categories != null) {
for (int i = 0; i < categories.length; i++) {
if (!isValidCategory(categories[i])) {
throw new IllegalArgumentException("Invalid category: " + categories[i]);
}
sb.append(categories[i]);
if (i < categories.length - 1) {
sb.append(CATEGORY_SEPARATOR);
}
}
}
if (mediaID != null) {
sb.append(LEAF_SEPARATOR).append(mediaID);
}
return sb.toString();
}
public static String extractCategory(@NonNull String mediaID) {
int pos = mediaID.indexOf(LEAF_SEPARATOR);
if (pos >= 0) {
return mediaID.substring(0, pos);
}
return mediaID;
}
public static String extractMusicID(@NonNull String mediaID) {
int pos = mediaID.indexOf(LEAF_SEPARATOR);
if (pos >= 0) {
return mediaID.substring(pos + LEAF_SEPARATOR.length());
}
return null;
}
public static boolean isBrowseable(@NonNull String mediaID) {
return !mediaID.contains(LEAF_SEPARATOR);
}
private static boolean isValidCategory(String category) {
return category == null ||
(!category.contains(CATEGORY_SEPARATOR) && !category.contains(LEAF_SEPARATOR));
}
}

View file

@ -0,0 +1,285 @@
/*
* Copyright (c) 2019 Hemanth Savarala.
*
* Licensed under the GNU General Public License v3
*
* This is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*/
package code.name.monkey.retromusic.auto
import android.content.Context
import android.content.res.Resources
import android.net.Uri
import android.support.v4.media.MediaBrowserCompat
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.model.CategoryInfo
import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.repository.*
import code.name.monkey.retromusic.service.MusicService
import code.name.monkey.retromusic.util.MusicUtil
import code.name.monkey.retromusic.util.PreferenceUtil
import java.lang.ref.WeakReference
import java.util.*
/**
* Created by Beesham Sarendranauth (Beesham)
*/
class AutoMusicProvider(
val mContext: Context,
private val songsRepository: SongRepository,
private val albumsRepository: AlbumRepository,
private val artistsRepository: ArtistRepository,
private val genresRepository: GenreRepository,
private val playlistsRepository: PlaylistRepository,
private val topPlayedRepository: TopPlayedRepository
) {
private var mMusicService: WeakReference<MusicService>? = null
fun setMusicService(service: MusicService) {
mMusicService = WeakReference(service)
}
fun getChildren(mediaId: String?, resources: Resources): List<MediaBrowserCompat.MediaItem> {
val mediaItems: MutableList<MediaBrowserCompat.MediaItem> = ArrayList()
when (mediaId) {
AutoMediaIDHelper.MEDIA_ID_ROOT -> {
mediaItems.addAll(getRootChildren(resources))
}
AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_PLAYLIST -> for (playlist in playlistsRepository.playlists()) {
mediaItems.add(
AutoMediaItem.with(mContext)
.path(AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_PLAYLIST, playlist.id)
.icon(R.drawable.ic_playlist_play)
.title(playlist.name)
.subTitle(playlist.getInfoString(mContext))
.asPlayable()
.build()
)
}
AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_ALBUM -> for (album in albumsRepository.albums()) {
mediaItems.add(
AutoMediaItem.with(mContext)
.path(mediaId, album.id)
.title(album.title)
.subTitle(album.albumArtist ?: album.artistName)
.icon(MusicUtil.getMediaStoreAlbumCoverUri(album.id))
.asPlayable()
.build()
)
}
AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_ARTIST -> for (artist in artistsRepository.artists()) {
mediaItems.add(
AutoMediaItem.with(mContext)
.asPlayable()
.path(mediaId, artist.id)
.title(artist.name)
.build()
)
}
AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_ALBUM_ARTIST -> for (artist in artistsRepository.albumArtists()) {
mediaItems.add(
AutoMediaItem.with(mContext)
.asPlayable()
// we just pass album id here as we don't have album artist id's
.path(mediaId, artist.safeGetFirstAlbum().id)
.title(artist.name)
.build()
)
}
AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_GENRE -> for (genre in genresRepository.genres()) {
mediaItems.add(
AutoMediaItem.with(mContext)
.asPlayable()
.path(mediaId, genre.id)
.title(genre.name)
.build()
)
}
AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_QUEUE ->
mMusicService?.get()?.playingQueue
?.let {
for (song in it) {
mediaItems.add(
AutoMediaItem.with(mContext)
.asPlayable()
.path(mediaId, song.id)
.title(song.title)
.subTitle(song.artistName)
.icon(MusicUtil.getMediaStoreAlbumCoverUri(song.albumId))
.build()
)
}
}
else -> {
getPlaylistChildren(mediaId, mediaItems)
}
}
return mediaItems
}
private fun getPlaylistChildren(
mediaId: String?,
mediaItems: MutableList<MediaBrowserCompat.MediaItem>
) {
val songs = when (mediaId) {
AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_TOP_TRACKS -> {
topPlayedRepository.topTracks()
}
AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_HISTORY -> {
topPlayedRepository.recentlyPlayedTracks()
}
AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_SUGGESTIONS -> {
topPlayedRepository.notRecentlyPlayedTracks().take(8)
}
else -> {
emptyList()
}
}
songs.forEach { song ->
mediaItems.add(
getPlayableSong(mediaId, song)
)
}
}
private fun getRootChildren(resources: Resources): List<MediaBrowserCompat.MediaItem> {
val mediaItems: MutableList<MediaBrowserCompat.MediaItem> = ArrayList()
val libraryCategories = PreferenceUtil.libraryCategory
libraryCategories.forEach {
if (it.visible) {
when (it.category) {
CategoryInfo.Category.Albums -> {
mediaItems.add(
AutoMediaItem.with(mContext)
.asBrowsable()
.path(AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_ALBUM)
.gridLayout(true)
.icon(R.drawable.ic_album)
.title(resources.getString(R.string.albums)).build()
)
}
CategoryInfo.Category.Artists -> {
if (PreferenceUtil.albumArtistsOnly) {
mediaItems.add(
AutoMediaItem.with(mContext)
.asBrowsable()
.path(AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_ALBUM_ARTIST)
.icon(R.drawable.ic_album_artist)
.title(resources.getString(R.string.album_artist)).build()
)
} else {
mediaItems.add(
AutoMediaItem.with(mContext)
.asBrowsable()
.path(AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_ARTIST)
.icon(R.drawable.ic_artist)
.title(resources.getString(R.string.artists)).build()
)
}
}
CategoryInfo.Category.Genres -> {
mediaItems.add(
AutoMediaItem.with(mContext)
.asBrowsable()
.path(AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_GENRE)
.icon(R.drawable.ic_guitar)
.title(resources.getString(R.string.genres)).build()
)
}
CategoryInfo.Category.Playlists -> {
mediaItems.add(
AutoMediaItem.with(mContext)
.asBrowsable()
.path(AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_PLAYLIST)
.icon(R.drawable.ic_playlist_play)
.title(resources.getString(R.string.playlists)).build()
)
}
else -> {
}
}
}
}
mediaItems.add(
AutoMediaItem.with(mContext)
.asPlayable()
.path(AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_SHUFFLE)
.icon(R.drawable.ic_shuffle)
.title(resources.getString(R.string.action_shuffle_all))
.subTitle(MusicUtil.getPlaylistInfoString(mContext, songsRepository.songs()))
.build()
)
mediaItems.add(
AutoMediaItem.with(mContext)
.asBrowsable()
.path(AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_QUEUE)
.icon(R.drawable.ic_queue_music)
.title(resources.getString(R.string.queue))
.subTitle(MusicUtil.getPlaylistInfoString(mContext, MusicPlayerRemote.playingQueue))
.asBrowsable().build()
)
mediaItems.add(
AutoMediaItem.with(mContext)
.asBrowsable()
.path(AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_TOP_TRACKS)
.icon(R.drawable.ic_trending_up)
.title(resources.getString(R.string.my_top_tracks))
.subTitle(
MusicUtil.getPlaylistInfoString(
mContext,
topPlayedRepository.topTracks()
)
)
.asBrowsable().build()
)
mediaItems.add(
AutoMediaItem.with(mContext)
.asBrowsable()
.path(AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_SUGGESTIONS)
.icon(R.drawable.ic_face)
.title(resources.getString(R.string.suggestion_songs))
.subTitle(
MusicUtil.getPlaylistInfoString(
mContext,
topPlayedRepository.notRecentlyPlayedTracks().takeIf {
it.size > 9
} ?: emptyList()
)
)
.asBrowsable().build()
)
mediaItems.add(
AutoMediaItem.with(mContext)
.asBrowsable()
.path(AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_HISTORY)
.icon(R.drawable.ic_history)
.title(resources.getString(R.string.history))
.subTitle(
MusicUtil.getPlaylistInfoString(
mContext,
topPlayedRepository.recentlyPlayedTracks()
)
)
.asBrowsable().build()
)
return mediaItems
}
private fun getPlayableSong(mediaId: String?, song: Song): MediaBrowserCompat.MediaItem {
return AutoMediaItem.with(mContext)
.asPlayable()
.path(mediaId, song.id)
.title(song.title)
.subTitle(song.artistName)
.icon(MusicUtil.getMediaStoreAlbumCoverUri(song.albumId))
.build()
}
}

View file

@ -0,0 +1,102 @@
package code.name.monkey.retromusic.auto
import android.content.Context
import android.net.Uri
import android.os.Bundle
import android.support.v4.media.MediaBrowserCompat
import android.support.v4.media.MediaDescriptionCompat
import code.name.monkey.retromusic.util.ImageUtil
internal object AutoMediaItem {
fun with(context: Context): Builder {
return Builder(context)
}
internal class Builder(val mContext: Context) {
private var mBuilder: MediaDescriptionCompat.Builder?
private var mFlags = 0
fun path(fullPath: String): Builder {
mBuilder?.setMediaId(fullPath)
return this
}
fun path(path: String?, id: Long): Builder {
return path(AutoMediaIDHelper.createMediaID(id.toString(), path))
}
fun title(title: String): Builder {
mBuilder?.setTitle(title)
return this
}
fun subTitle(subTitle: String): Builder {
mBuilder?.setSubtitle(subTitle)
return this
}
fun icon(uri: Uri?): Builder {
mBuilder?.setIconUri(uri)
return this
}
fun icon(iconDrawableId: Int): Builder {
mBuilder?.setIconBitmap(
ImageUtil.createBitmap(
ImageUtil.getVectorDrawable(
mContext.resources,
iconDrawableId,
mContext.theme
)
)
)
return this
}
fun gridLayout(isGrid: Boolean): Builder {
val hints = Bundle()
hints.putBoolean(CONTENT_STYLE_SUPPORTED, true)
hints.putInt(
CONTENT_STYLE_BROWSABLE_HINT,
if (isGrid) CONTENT_STYLE_GRID_ITEM_HINT_VALUE else CONTENT_STYLE_LIST_ITEM_HINT_VALUE
)
hints.putInt(
CONTENT_STYLE_PLAYABLE_HINT,
if (isGrid) CONTENT_STYLE_GRID_ITEM_HINT_VALUE else CONTENT_STYLE_LIST_ITEM_HINT_VALUE
)
mBuilder?.setExtras(hints)
return this
}
fun asBrowsable(): Builder {
mFlags = mFlags or MediaBrowserCompat.MediaItem.FLAG_BROWSABLE
return this
}
fun asPlayable(): Builder {
mFlags = mFlags or MediaBrowserCompat.MediaItem.FLAG_PLAYABLE
return this
}
fun build(): MediaBrowserCompat.MediaItem {
val result = MediaBrowserCompat.MediaItem(mBuilder!!.build(), mFlags)
mBuilder = null
mFlags = 0
return result
}
init {
mBuilder = MediaDescriptionCompat.Builder()
}
companion object{
// Hints - see https://developer.android.com/training/cars/media#default-content-style
const val CONTENT_STYLE_SUPPORTED = "android.media.browse.CONTENT_STYLE_SUPPORTED"
const val CONTENT_STYLE_BROWSABLE_HINT = "android.media.browse.CONTENT_STYLE_BROWSABLE_HINT"
const val CONTENT_STYLE_PLAYABLE_HINT = "android.media.browse.CONTENT_STYLE_PLAYABLE_HINT"
const val CONTENT_STYLE_LIST_ITEM_HINT_VALUE = 1
const val CONTENT_STYLE_GRID_ITEM_HINT_VALUE = 2
}
}
}