Added suggestions & Updated libs.
This commit is contained in:
parent
865e13a536
commit
547e49507e
20 changed files with 568 additions and 186 deletions
|
@ -133,7 +133,7 @@ dependencies {
|
||||||
|
|
||||||
implementation 'com.google.android.material:material:1.3.0-alpha01'
|
implementation 'com.google.android.material:material:1.3.0-alpha01'
|
||||||
|
|
||||||
def retrofit_version = '2.8.1'
|
def retrofit_version = '2.9.0'
|
||||||
implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
|
implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
|
||||||
implementation "com.squareup.retrofit2:converter-gson:$retrofit_version"
|
implementation "com.squareup.retrofit2:converter-gson:$retrofit_version"
|
||||||
|
|
||||||
|
@ -176,7 +176,7 @@ dependencies {
|
||||||
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
|
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
|
||||||
kapt "androidx.lifecycle:lifecycle-compiler:$lifecycle_version"
|
kapt "androidx.lifecycle:lifecycle-compiler:$lifecycle_version"
|
||||||
|
|
||||||
implementation 'com.google.android.play:core:1.7.2'
|
implementation 'com.google.android.play:core:1.7.3'
|
||||||
implementation 'me.jorgecastillo:androidcolorx:0.2.0'
|
implementation 'me.jorgecastillo:androidcolorx:0.2.0'
|
||||||
debugImplementation 'com.amitshekhar.android:debug-db:1.0.4'
|
debugImplementation 'com.amitshekhar.android:debug-db:1.0.4'
|
||||||
implementation 'com.github.dhaval2404:imagepicker:1.7.1'
|
implementation 'com.github.dhaval2404:imagepicker:1.7.1'
|
||||||
|
|
|
@ -116,6 +116,7 @@ const val IGNORE_MEDIA_STORE_ARTWORK = "ignore_media_store_artwork"
|
||||||
const val LAST_CHANGELOG_VERSION = "last_changelog_version"
|
const val LAST_CHANGELOG_VERSION = "last_changelog_version"
|
||||||
const val AUTO_DOWNLOAD_IMAGES_POLICY = "auto_download_images_policy"
|
const val AUTO_DOWNLOAD_IMAGES_POLICY = "auto_download_images_policy"
|
||||||
const val START_DIRECTORY = "start_directory"
|
const val START_DIRECTORY = "start_directory"
|
||||||
|
const val RECENTLY_PLAYED_CUTOFF = "recently_played_interval"
|
||||||
const val LOCK_SCREEN = "lock_screen"
|
const val LOCK_SCREEN = "lock_screen"
|
||||||
const val ALBUM_DETAIL_SONG_SORT_ORDER = "album_detail_song_sort_order"
|
const val ALBUM_DETAIL_SONG_SORT_ORDER = "album_detail_song_sort_order"
|
||||||
const val LYRICS_OPTIONS = "lyrics_tab_position"
|
const val LYRICS_OPTIONS = "lyrics_tab_position"
|
||||||
|
|
|
@ -4,24 +4,25 @@ import android.util.DisplayMetrics
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import android.widget.TextView
|
||||||
import androidx.annotation.IntDef
|
import androidx.annotation.IntDef
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.appcompat.widget.AppCompatTextView
|
import androidx.appcompat.widget.AppCompatTextView
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import code.name.monkey.appthemehelper.ThemeStore
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.adapter.album.AlbumFullWidthAdapter
|
import code.name.monkey.retromusic.adapter.album.AlbumFullWidthAdapter
|
||||||
import code.name.monkey.retromusic.adapter.artist.ArtistAdapter
|
import code.name.monkey.retromusic.adapter.artist.ArtistAdapter
|
||||||
import code.name.monkey.retromusic.adapter.song.SongAdapter
|
import code.name.monkey.retromusic.adapter.song.SongAdapter
|
||||||
import code.name.monkey.retromusic.extensions.show
|
import code.name.monkey.retromusic.extensions.show
|
||||||
|
import code.name.monkey.retromusic.glide.SongGlideRequest
|
||||||
|
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||||
import code.name.monkey.retromusic.loaders.PlaylistSongsLoader
|
import code.name.monkey.retromusic.loaders.PlaylistSongsLoader
|
||||||
import code.name.monkey.retromusic.model.Album
|
import code.name.monkey.retromusic.model.*
|
||||||
import code.name.monkey.retromusic.model.Artist
|
|
||||||
import code.name.monkey.retromusic.model.Home
|
|
||||||
import code.name.monkey.retromusic.model.Playlist
|
|
||||||
|
|
||||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||||
|
import com.bumptech.glide.Glide
|
||||||
|
|
||||||
class HomeAdapter(
|
class HomeAdapter(
|
||||||
private val activity: AppCompatActivity,
|
private val activity: AppCompatActivity,
|
||||||
|
@ -40,6 +41,15 @@ class HomeAdapter(
|
||||||
return when (viewType) {
|
return when (viewType) {
|
||||||
RECENT_ARTISTS, TOP_ARTISTS -> ArtistViewHolder(layout)
|
RECENT_ARTISTS, TOP_ARTISTS -> ArtistViewHolder(layout)
|
||||||
PLAYLISTS -> PlaylistViewHolder(layout)
|
PLAYLISTS -> PlaylistViewHolder(layout)
|
||||||
|
SUGGESTIONS -> {
|
||||||
|
SuggestionsViewHolder(
|
||||||
|
LayoutInflater.from(activity).inflate(
|
||||||
|
R.layout.item_suggestions,
|
||||||
|
parent,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
else -> {
|
else -> {
|
||||||
AlbumViewHolder(
|
AlbumViewHolder(
|
||||||
LayoutInflater.from(activity).inflate(
|
LayoutInflater.from(activity).inflate(
|
||||||
|
@ -56,23 +66,41 @@ class HomeAdapter(
|
||||||
when (getItemViewType(position)) {
|
when (getItemViewType(position)) {
|
||||||
RECENT_ALBUMS -> {
|
RECENT_ALBUMS -> {
|
||||||
val viewHolder = holder as AlbumViewHolder
|
val viewHolder = holder as AlbumViewHolder
|
||||||
viewHolder.bindView(list[position].arrayList.toAlbums(), R.string.recent_albums)
|
viewHolder.bindView(
|
||||||
|
list[position].arrayList as List<Album>,
|
||||||
|
R.string.recent_albums
|
||||||
|
)
|
||||||
}
|
}
|
||||||
TOP_ALBUMS -> {
|
TOP_ALBUMS -> {
|
||||||
val viewHolder = holder as AlbumViewHolder
|
val viewHolder = holder as AlbumViewHolder
|
||||||
viewHolder.bindView(list[position].arrayList.toAlbums(), R.string.top_albums)
|
viewHolder.bindView(
|
||||||
|
list[position].arrayList as List<Album>,
|
||||||
|
R.string.top_albums
|
||||||
|
)
|
||||||
}
|
}
|
||||||
RECENT_ARTISTS -> {
|
RECENT_ARTISTS -> {
|
||||||
val viewHolder = holder as ArtistViewHolder
|
val viewHolder = holder as ArtistViewHolder
|
||||||
viewHolder.bindView(list[position].arrayList.toArtists(), R.string.recent_artists)
|
viewHolder.bindView(
|
||||||
|
list[position].arrayList as List<Artist>,
|
||||||
|
R.string.recent_artists
|
||||||
|
)
|
||||||
}
|
}
|
||||||
TOP_ARTISTS -> {
|
TOP_ARTISTS -> {
|
||||||
val viewHolder = holder as ArtistViewHolder
|
val viewHolder = holder as ArtistViewHolder
|
||||||
viewHolder.bindView(list[position].arrayList.toArtists(), R.string.top_artists)
|
viewHolder.bindView(list[position].arrayList as List<Artist>, R.string.top_artists)
|
||||||
}
|
}
|
||||||
PLAYLISTS -> {
|
PLAYLISTS -> {
|
||||||
val viewHolder = holder as PlaylistViewHolder
|
val viewHolder = holder as PlaylistViewHolder
|
||||||
viewHolder.bindView(list[position].arrayList.toPlaylist(), R.string.favorites)
|
viewHolder.bindView(
|
||||||
|
list[position].arrayList as List<Playlist>,
|
||||||
|
R.string.favorites
|
||||||
|
)
|
||||||
|
}
|
||||||
|
SUGGESTIONS -> {
|
||||||
|
val viewHolder = holder as SuggestionsViewHolder
|
||||||
|
viewHolder.bindView(
|
||||||
|
list[position].arrayList as List<Song>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,7 +116,7 @@ class HomeAdapter(
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
@IntDef(RECENT_ALBUMS, TOP_ALBUMS, RECENT_ARTISTS, TOP_ARTISTS, PLAYLISTS)
|
@IntDef(RECENT_ALBUMS, TOP_ALBUMS, RECENT_ARTISTS, TOP_ARTISTS, PLAYLISTS, SUGGESTIONS)
|
||||||
@Retention(AnnotationRetention.SOURCE)
|
@Retention(AnnotationRetention.SOURCE)
|
||||||
annotation class HomeSection
|
annotation class HomeSection
|
||||||
|
|
||||||
|
@ -96,11 +124,12 @@ class HomeAdapter(
|
||||||
const val TOP_ALBUMS = 1
|
const val TOP_ALBUMS = 1
|
||||||
const val RECENT_ARTISTS = 2
|
const val RECENT_ARTISTS = 2
|
||||||
const val TOP_ARTISTS = 0
|
const val TOP_ARTISTS = 0
|
||||||
const val PLAYLISTS = 4
|
const val SUGGESTIONS = 4
|
||||||
|
const val PLAYLISTS = 5
|
||||||
}
|
}
|
||||||
|
|
||||||
private inner class AlbumViewHolder(view: View) : AbsHomeViewItem(view) {
|
private inner class AlbumViewHolder(view: View) : AbsHomeViewItem(view) {
|
||||||
fun bindView(list: ArrayList<Album>, titleRes: Int) {
|
fun bindView(list: List<Album>, titleRes: Int) {
|
||||||
if (list.isNotEmpty()) {
|
if (list.isNotEmpty()) {
|
||||||
recyclerView.apply {
|
recyclerView.apply {
|
||||||
show()
|
show()
|
||||||
|
@ -112,7 +141,7 @@ class HomeAdapter(
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class ArtistViewHolder(view: View) : AbsHomeViewItem(view) {
|
inner class ArtistViewHolder(view: View) : AbsHomeViewItem(view) {
|
||||||
fun bindView(list: ArrayList<Artist>, titleRes: Int) {
|
fun bindView(list: List<Artist>, titleRes: Int) {
|
||||||
if (list.isNotEmpty()) {
|
if (list.isNotEmpty()) {
|
||||||
val manager = LinearLayoutManager(activity, LinearLayoutManager.HORIZONTAL, false)
|
val manager = LinearLayoutManager(activity, LinearLayoutManager.HORIZONTAL, false)
|
||||||
val artistAdapter = ArtistAdapter(
|
val artistAdapter = ArtistAdapter(
|
||||||
|
@ -131,8 +160,37 @@ class HomeAdapter(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private inner class SuggestionsViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||||
|
private val images = listOf(
|
||||||
|
R.id.image1,
|
||||||
|
R.id.image2,
|
||||||
|
R.id.image3,
|
||||||
|
R.id.image4,
|
||||||
|
R.id.image5,
|
||||||
|
R.id.image6,
|
||||||
|
R.id.image7,
|
||||||
|
R.id.image8
|
||||||
|
)
|
||||||
|
|
||||||
|
fun bindView(arrayList: List<Song>) {
|
||||||
|
val color = ThemeStore.accentColor(activity)
|
||||||
|
itemView.findViewById<TextView>(R.id.text).setTextColor(color)
|
||||||
|
|
||||||
|
images.forEachIndexed { index, i ->
|
||||||
|
itemView.findViewById<View>(i).setOnClickListener {
|
||||||
|
MusicPlayerRemote.playNext(arrayList[index])
|
||||||
|
}
|
||||||
|
SongGlideRequest.Builder.from(Glide.with(activity), arrayList[index])
|
||||||
|
.asBitmap()
|
||||||
|
.build()
|
||||||
|
.into(itemView.findViewById(i))
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private inner class PlaylistViewHolder(view: View) : AbsHomeViewItem(view) {
|
private inner class PlaylistViewHolder(view: View) : AbsHomeViewItem(view) {
|
||||||
fun bindView(arrayList: ArrayList<Playlist>, titleRes: Int) {
|
fun bindView(arrayList: List<Playlist>, titleRes: Int) {
|
||||||
if (arrayList.isNotEmpty()) {
|
if (arrayList.isNotEmpty()) {
|
||||||
val songs = PlaylistSongsLoader.getPlaylistSongList(activity, arrayList[0])
|
val songs = PlaylistSongsLoader.getPlaylistSongList(activity, arrayList[0])
|
||||||
if (songs.isNotEmpty()) {
|
if (songs.isNotEmpty()) {
|
||||||
|
@ -155,27 +213,3 @@ class HomeAdapter(
|
||||||
val title: AppCompatTextView = itemView.findViewById(R.id.title)
|
val title: AppCompatTextView = itemView.findViewById(R.id.title)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun <E> ArrayList<E>.toAlbums(): ArrayList<Album> {
|
|
||||||
val arrayList = ArrayList<Album>()
|
|
||||||
for (x in this) {
|
|
||||||
arrayList.add(x as Album)
|
|
||||||
}
|
|
||||||
return arrayList
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun <E> ArrayList<E>.toArtists(): ArrayList<Artist> {
|
|
||||||
val arrayList = ArrayList<Artist>()
|
|
||||||
for (x in this) {
|
|
||||||
arrayList.add(x as Artist)
|
|
||||||
}
|
|
||||||
return arrayList
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun <E> ArrayList<E>.toPlaylist(): ArrayList<Playlist> {
|
|
||||||
val arrayList = ArrayList<Playlist>()
|
|
||||||
for (x in this) {
|
|
||||||
arrayList.add(x as Playlist)
|
|
||||||
}
|
|
||||||
return arrayList
|
|
||||||
}
|
|
|
@ -55,7 +55,8 @@ class LibraryViewModel(application: Application) :
|
||||||
_repository.topAlbums(),
|
_repository.topAlbums(),
|
||||||
_repository.recentArtists(),
|
_repository.recentArtists(),
|
||||||
_repository.recentAlbums(),
|
_repository.recentAlbums(),
|
||||||
_repository.favoritePlaylist()
|
_repository.favoritePlaylist(),
|
||||||
|
_repository.suggestions()
|
||||||
)
|
)
|
||||||
for (r in result) {
|
for (r in result) {
|
||||||
if (r != null) {
|
if (r != null) {
|
||||||
|
|
|
@ -55,13 +55,6 @@ class BannerHomeFragment : AbsMainActivityFragment(), MainActivityFragmentCallba
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadImageFromStorage() {
|
|
||||||
UserProfileGlideRequest.Builder.from(
|
|
||||||
Glide.with(requireActivity()),
|
|
||||||
UserProfileGlideRequest.getUserModel()
|
|
||||||
).build().into(userImage)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val displayMetrics: DisplayMetrics
|
private val displayMetrics: DisplayMetrics
|
||||||
get() {
|
get() {
|
||||||
val display = mainActivity.windowManager.defaultDisplay
|
val display = mainActivity.windowManager.defaultDisplay
|
||||||
|
@ -111,8 +104,7 @@ class BannerHomeFragment : AbsMainActivityFragment(), MainActivityFragmentCallba
|
||||||
)
|
)
|
||||||
NavigationUtil.goToUserInfo(requireActivity(), options)
|
NavigationUtil.goToUserInfo(requireActivity(), options)
|
||||||
}
|
}
|
||||||
titleWelcome?.text =
|
titleWelcome?.text = String.format("%s", PreferenceUtil.userName)
|
||||||
String.format("%s", PreferenceUtil.userName)
|
|
||||||
|
|
||||||
homeAdapter = HomeAdapter(mainActivity, displayMetrics)
|
homeAdapter = HomeAdapter(mainActivity, displayMetrics)
|
||||||
recyclerView.apply {
|
recyclerView.apply {
|
||||||
|
|
|
@ -279,6 +279,10 @@ class GradientPlayerFragment : AbsPlayerFragment(), MusicProgressViewUpdateHelpe
|
||||||
updateQueuePosition()
|
updateQueuePosition()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onQueueChanged() {
|
||||||
|
super.onQueueChanged()
|
||||||
|
updateLabel()
|
||||||
|
}
|
||||||
private fun updateSong() {
|
private fun updateSong() {
|
||||||
val song = MusicPlayerRemote.currentSong
|
val song = MusicPlayerRemote.currentSong
|
||||||
title.text = song.title
|
title.text = song.title
|
||||||
|
|
|
@ -17,14 +17,18 @@ package code.name.monkey.retromusic.loaders
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.database.Cursor
|
import android.database.Cursor
|
||||||
import android.provider.BaseColumns
|
import android.provider.BaseColumns
|
||||||
|
import android.provider.MediaStore
|
||||||
import code.name.monkey.retromusic.Constants.NUMBER_OF_TOP_TRACKS
|
import code.name.monkey.retromusic.Constants.NUMBER_OF_TOP_TRACKS
|
||||||
|
import code.name.monkey.retromusic.loaders.SongLoader.makeSongCursor
|
||||||
import code.name.monkey.retromusic.model.Album
|
import code.name.monkey.retromusic.model.Album
|
||||||
import code.name.monkey.retromusic.model.Artist
|
import code.name.monkey.retromusic.model.Artist
|
||||||
import code.name.monkey.retromusic.model.Song
|
import code.name.monkey.retromusic.model.Song
|
||||||
import code.name.monkey.retromusic.providers.HistoryStore
|
import code.name.monkey.retromusic.providers.HistoryStore
|
||||||
import code.name.monkey.retromusic.providers.SongPlayCountStore
|
import code.name.monkey.retromusic.providers.SongPlayCountStore
|
||||||
|
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by hemanths on 16/08/17.
|
* Created by hemanths on 16/08/17.
|
||||||
*/
|
*/
|
||||||
|
@ -39,19 +43,23 @@ object TopAndRecentlyPlayedTracksLoader {
|
||||||
return SongLoader.getSongs(makeTopTracksCursorAndClearUpDatabase(context))
|
return SongLoader.getSongs(makeTopTracksCursorAndClearUpDatabase(context))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun makeRecentTracksCursorAndClearUpDatabase(context: Context): Cursor? {
|
fun getNotRecentlyPlayedTracks(context: Context): ArrayList<Song> {
|
||||||
val retCursor = makeRecentTracksCursorImpl(context)
|
val allSongs = SongLoader.getSongs(
|
||||||
|
makeSongCursor(
|
||||||
// clean up the databases with any ids not found
|
context,
|
||||||
if (retCursor != null) {
|
null, null,
|
||||||
val missingIds = retCursor.missingIds
|
MediaStore.Audio.Media.DATE_ADDED + " ASC"
|
||||||
if (missingIds != null && missingIds.size > 0) {
|
)
|
||||||
for (id in missingIds) {
|
)
|
||||||
HistoryStore.getInstance(context).removeSongId(id)
|
val playedSongs = SongLoader.getSongs(
|
||||||
}
|
makePlayedTracksCursorAndClearUpDatabase(context)
|
||||||
}
|
)
|
||||||
}
|
val notRecentlyPlayedSongs = SongLoader.getSongs(
|
||||||
return retCursor
|
makeNotRecentTracksCursorAndClearUpDatabase(context)
|
||||||
|
)
|
||||||
|
allSongs.removeAll(playedSongs)
|
||||||
|
allSongs.addAll(notRecentlyPlayedSongs)
|
||||||
|
return allSongs
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun makeTopTracksCursorAndClearUpDatabase(context: Context): Cursor? {
|
private fun makeTopTracksCursorAndClearUpDatabase(context: Context): Cursor? {
|
||||||
|
@ -72,21 +80,19 @@ object TopAndRecentlyPlayedTracksLoader {
|
||||||
private fun makeRecentTracksCursorImpl(context: Context): SortedLongCursor? {
|
private fun makeRecentTracksCursorImpl(context: Context): SortedLongCursor? {
|
||||||
// first get the top results ids from the internal database
|
// first get the top results ids from the internal database
|
||||||
val songs = HistoryStore.getInstance(context).queryRecentIds()
|
val songs = HistoryStore.getInstance(context).queryRecentIds()
|
||||||
|
songs.use {
|
||||||
try {
|
|
||||||
return makeSortedCursor(
|
return makeSortedCursor(
|
||||||
context, songs,
|
context,
|
||||||
songs!!.getColumnIndex(HistoryStore.RecentStoreColumns.ID)
|
it,
|
||||||
|
it.getColumnIndex(HistoryStore.RecentStoreColumns.ID)
|
||||||
)
|
)
|
||||||
} finally {
|
|
||||||
songs?.close()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun makeTopTracksCursorImpl(context: Context): SortedLongCursor? {
|
private fun makeTopTracksCursorImpl(context: Context): SortedLongCursor? {
|
||||||
// first get the top results ids from the internal database
|
// first get the top results ids from the internal database
|
||||||
val songs = SongPlayCountStore.getInstance(context)
|
val songs =
|
||||||
.getTopPlayedResults(NUMBER_OF_TOP_TRACKS)
|
SongPlayCountStore.getInstance(context).getTopPlayedResults(NUMBER_OF_TOP_TRACKS)
|
||||||
|
|
||||||
songs.use { localSongs ->
|
songs.use { localSongs ->
|
||||||
return makeSortedCursor(
|
return makeSortedCursor(
|
||||||
|
@ -145,4 +151,57 @@ object TopAndRecentlyPlayedTracksLoader {
|
||||||
fun getTopArtists(context: Context): ArrayList<Artist> {
|
fun getTopArtists(context: Context): ArrayList<Artist> {
|
||||||
return ArtistLoader.splitIntoArtists(getTopAlbums(context))
|
return ArtistLoader.splitIntoArtists(getTopAlbums(context))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun makeRecentTracksCursorAndClearUpDatabase(context: Context): Cursor? {
|
||||||
|
return makeRecentTracksCursorAndClearUpDatabaseImpl(context, false, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun makePlayedTracksCursorAndClearUpDatabase(context: Context): Cursor? {
|
||||||
|
return makeRecentTracksCursorAndClearUpDatabaseImpl(context, true, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun makeNotRecentTracksCursorAndClearUpDatabase(context: Context): Cursor? {
|
||||||
|
return makeRecentTracksCursorAndClearUpDatabaseImpl(context, false, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun makeRecentTracksCursorAndClearUpDatabaseImpl(
|
||||||
|
context: Context,
|
||||||
|
ignoreCutoffTime: Boolean,
|
||||||
|
reverseOrder: Boolean
|
||||||
|
): SortedLongCursor? {
|
||||||
|
val retCursor = makeRecentTracksCursorImpl(context, ignoreCutoffTime, reverseOrder)
|
||||||
|
// clean up the databases with any ids not found
|
||||||
|
// clean up the databases with any ids not found
|
||||||
|
if (retCursor != null) {
|
||||||
|
val missingIds = retCursor.missingIds
|
||||||
|
if (missingIds != null && missingIds.size > 0) {
|
||||||
|
for (id in missingIds) {
|
||||||
|
HistoryStore.getInstance(context).removeSongId(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return retCursor
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun makeRecentTracksCursorImpl(
|
||||||
|
context: Context,
|
||||||
|
ignoreCutoffTime: Boolean,
|
||||||
|
reverseOrder: Boolean
|
||||||
|
): SortedLongCursor? {
|
||||||
|
val cutoff =
|
||||||
|
(if (ignoreCutoffTime) 0 else PreferenceUtil.getRecentlyPlayedCutoffTimeMillis()).toLong()
|
||||||
|
val songs =
|
||||||
|
HistoryStore.getInstance(context).queryRecentIds(cutoff * if (reverseOrder) -1 else 1)
|
||||||
|
return songs.use {
|
||||||
|
makeSortedCursor(
|
||||||
|
context,
|
||||||
|
it,
|
||||||
|
it.getColumnIndex(HistoryStore.RecentStoreColumns.ID)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,13 +15,10 @@
|
||||||
package code.name.monkey.retromusic.model
|
package code.name.monkey.retromusic.model
|
||||||
|
|
||||||
import androidx.annotation.DrawableRes
|
import androidx.annotation.DrawableRes
|
||||||
import androidx.annotation.StringRes
|
|
||||||
import code.name.monkey.retromusic.adapter.HomeAdapter.Companion.HomeSection
|
import code.name.monkey.retromusic.adapter.HomeAdapter.Companion.HomeSection
|
||||||
|
|
||||||
class Home(
|
class Home(
|
||||||
val priority: Int,
|
val arrayList: List<*>,
|
||||||
@StringRes val title: Int,
|
|
||||||
val arrayList: ArrayList<*>,
|
|
||||||
@HomeSection
|
@HomeSection
|
||||||
val homeSection: Int,
|
val homeSection: Int,
|
||||||
@DrawableRes
|
@DrawableRes
|
||||||
|
|
|
@ -23,6 +23,7 @@ import androidx.annotation.NonNull;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
import code.name.monkey.retromusic.loaders.PlaylistSongsLoader;
|
import code.name.monkey.retromusic.loaders.PlaylistSongsLoader;
|
||||||
|
import code.name.monkey.retromusic.util.MusicUtil;
|
||||||
|
|
||||||
|
|
||||||
public class Playlist implements Parcelable {
|
public class Playlist implements Parcelable {
|
||||||
|
@ -106,5 +107,14 @@ public class Playlist implements Parcelable {
|
||||||
dest.writeString(this.name);
|
dest.writeString(this.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public String getInfoString(@NonNull Context context) {
|
||||||
|
int songCount = getSongs(context).size();
|
||||||
|
String songCountString = MusicUtil.getSongCountString(context, songCount);
|
||||||
|
|
||||||
|
return MusicUtil.buildInfoString(
|
||||||
|
songCountString,
|
||||||
|
""
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
package code.name.monkey.retromusic.model.smartplaylist;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Parcel;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import code.name.monkey.retromusic.R;
|
||||||
|
import code.name.monkey.retromusic.loaders.TopAndRecentlyPlayedTracksLoader;
|
||||||
|
import code.name.monkey.retromusic.model.Song;
|
||||||
|
import code.name.monkey.retromusic.util.MusicUtil;
|
||||||
|
import code.name.monkey.retromusic.util.PreferenceUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author SC (soncaokim)
|
||||||
|
*/
|
||||||
|
public class NotRecentlyPlayedPlaylist extends AbsSmartPlaylist {
|
||||||
|
|
||||||
|
public static final Creator<NotRecentlyPlayedPlaylist> CREATOR = new Creator<NotRecentlyPlayedPlaylist>() {
|
||||||
|
public NotRecentlyPlayedPlaylist createFromParcel(Parcel source) {
|
||||||
|
return new NotRecentlyPlayedPlaylist(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
public NotRecentlyPlayedPlaylist[] newArray(int size) {
|
||||||
|
return new NotRecentlyPlayedPlaylist[size];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public NotRecentlyPlayedPlaylist(@NonNull Context context) {
|
||||||
|
super(context.getString(R.string.not_recently_played), R.drawable.ic_watch_later_white_24dp);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected NotRecentlyPlayedPlaylist(Parcel in) {
|
||||||
|
super(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public String getInfoString(@NonNull Context context) {
|
||||||
|
String cutoff = PreferenceUtil.INSTANCE.getRecentlyPlayedCutoffText(context);
|
||||||
|
|
||||||
|
return MusicUtil.buildInfoString(
|
||||||
|
cutoff,
|
||||||
|
super.getInfoString(context)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public ArrayList<Song> getSongs(@NonNull Context context) {
|
||||||
|
return TopAndRecentlyPlayedTracksLoader.INSTANCE.getNotRecentlyPlayedTracks(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear(@NonNull Context context) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isClearable() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int describeContents() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,66 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.mvp.presenter
|
|
||||||
|
|
||||||
import code.name.monkey.retromusic.model.Home
|
|
||||||
import code.name.monkey.retromusic.mvp.BaseView
|
|
||||||
import code.name.monkey.retromusic.mvp.Presenter
|
|
||||||
import code.name.monkey.retromusic.mvp.PresenterImpl
|
|
||||||
import code.name.monkey.retromusic.providers.interfaces.Repository
|
|
||||||
import kotlinx.coroutines.*
|
|
||||||
|
|
||||||
import kotlin.coroutines.CoroutineContext
|
|
||||||
|
|
||||||
interface HomeView : BaseView {
|
|
||||||
fun sections(sections: List<Home>)
|
|
||||||
}
|
|
||||||
|
|
||||||
interface HomePresenter : Presenter<HomeView> {
|
|
||||||
fun loadSections()
|
|
||||||
|
|
||||||
class HomePresenterImpl constructor(
|
|
||||||
private val repository: Repository
|
|
||||||
) : PresenterImpl<HomeView>(), HomePresenter, CoroutineScope {
|
|
||||||
|
|
||||||
private val job = Job()
|
|
||||||
|
|
||||||
override val coroutineContext: CoroutineContext
|
|
||||||
get() = Dispatchers.IO + job
|
|
||||||
|
|
||||||
override fun detachView() {
|
|
||||||
super.detachView()
|
|
||||||
job.cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun loadSections() {
|
|
||||||
launch {
|
|
||||||
val list = ArrayList<Home>()
|
|
||||||
val recentArtistResult = listOf(
|
|
||||||
repository.topArtists(),
|
|
||||||
repository.topAlbums(),
|
|
||||||
repository.recentArtists(),
|
|
||||||
repository.recentAlbums(),
|
|
||||||
repository.favoritePlaylist()
|
|
||||||
)
|
|
||||||
for (r in recentArtistResult) {
|
|
||||||
r?.let { list.add(it) }
|
|
||||||
}
|
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
if (list.isNotEmpty()) view?.sections(list) else view?.showEmptyView()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -141,6 +141,21 @@ public class HistoryStore extends SQLiteOpenHelper {
|
||||||
RecentStoreColumns.TIME_PLAYED + " DESC");
|
RecentStoreColumns.TIME_PLAYED + " DESC");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Cursor queryRecentIds(long cutoff) {
|
||||||
|
final boolean noCutoffTime = (cutoff == 0);
|
||||||
|
final boolean reverseOrder = (cutoff < 0);
|
||||||
|
if (reverseOrder) cutoff = -cutoff;
|
||||||
|
|
||||||
|
final SQLiteDatabase database = getReadableDatabase();
|
||||||
|
|
||||||
|
return database.query(RecentStoreColumns.NAME,
|
||||||
|
new String[]{RecentStoreColumns.ID},
|
||||||
|
noCutoffTime ? null : RecentStoreColumns.TIME_PLAYED + (reverseOrder ? "<?" : ">?"),
|
||||||
|
noCutoffTime ? null : new String[]{String.valueOf(cutoff)},
|
||||||
|
null, null,
|
||||||
|
RecentStoreColumns.TIME_PLAYED + (reverseOrder ? " ASC" : " DESC"));
|
||||||
|
}
|
||||||
|
|
||||||
public interface RecentStoreColumns {
|
public interface RecentStoreColumns {
|
||||||
String NAME = "recent_history";
|
String NAME = "recent_history";
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.adapter.HomeAdapter
|
import code.name.monkey.retromusic.adapter.HomeAdapter
|
||||||
import code.name.monkey.retromusic.loaders.*
|
import code.name.monkey.retromusic.loaders.*
|
||||||
import code.name.monkey.retromusic.model.*
|
import code.name.monkey.retromusic.model.*
|
||||||
|
import code.name.monkey.retromusic.model.smartplaylist.NotRecentlyPlayedPlaylist
|
||||||
import code.name.monkey.retromusic.providers.interfaces.Repository
|
import code.name.monkey.retromusic.providers.interfaces.Repository
|
||||||
import code.name.monkey.retromusic.rest.LastFmClient
|
import code.name.monkey.retromusic.rest.LastFmClient
|
||||||
import code.name.monkey.retromusic.rest.model.LastFmAlbum
|
import code.name.monkey.retromusic.rest.model.LastFmAlbum
|
||||||
|
@ -41,6 +42,18 @@ class RepositoryImpl constructor(private val context: Context) : Repository {
|
||||||
override suspend fun artistById(artistId: Int): Artist =
|
override suspend fun artistById(artistId: Int): Artist =
|
||||||
ArtistLoader.getArtist(context, artistId)
|
ArtistLoader.getArtist(context, artistId)
|
||||||
|
|
||||||
|
override suspend fun suggestions(): Home? {
|
||||||
|
val songs = NotRecentlyPlayedPlaylist(context).getSongs(context).shuffled().subList(0, 9)
|
||||||
|
if (songs.isNotEmpty()) {
|
||||||
|
return Home(
|
||||||
|
songs,
|
||||||
|
HomeAdapter.SUGGESTIONS,
|
||||||
|
R.drawable.ic_audiotrack_white_24dp
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun search(query: String?): MutableList<Any> =
|
override suspend fun search(query: String?): MutableList<Any> =
|
||||||
SearchLoader.searchAll(context, query)
|
SearchLoader.searchAll(context, query)
|
||||||
|
|
||||||
|
@ -58,8 +71,6 @@ class RepositoryImpl constructor(private val context: Context) : Repository {
|
||||||
override suspend fun recentArtists(): Home? {
|
override suspend fun recentArtists(): Home? {
|
||||||
val artists = LastAddedSongsLoader.getLastAddedArtists(context)
|
val artists = LastAddedSongsLoader.getLastAddedArtists(context)
|
||||||
return if (artists.isNotEmpty()) Home(
|
return if (artists.isNotEmpty()) Home(
|
||||||
0,
|
|
||||||
R.string.recent_artists,
|
|
||||||
artists,
|
artists,
|
||||||
HomeAdapter.RECENT_ARTISTS,
|
HomeAdapter.RECENT_ARTISTS,
|
||||||
R.drawable.ic_artist_white_24dp
|
R.drawable.ic_artist_white_24dp
|
||||||
|
@ -68,56 +79,40 @@ class RepositoryImpl constructor(private val context: Context) : Repository {
|
||||||
|
|
||||||
override suspend fun recentAlbums(): Home? {
|
override suspend fun recentAlbums(): Home? {
|
||||||
val albums = LastAddedSongsLoader.getLastAddedAlbums(context)
|
val albums = LastAddedSongsLoader.getLastAddedAlbums(context)
|
||||||
return if (albums.isNotEmpty()) {
|
return if (albums.isNotEmpty()) Home(
|
||||||
Home(
|
albums,
|
||||||
1,
|
HomeAdapter.RECENT_ALBUMS,
|
||||||
R.string.recent_albums,
|
R.drawable.ic_album_white_24dp
|
||||||
albums,
|
) else null
|
||||||
HomeAdapter.RECENT_ALBUMS,
|
|
||||||
R.drawable.ic_album_white_24dp
|
|
||||||
)
|
|
||||||
} else null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun topAlbums(): Home? {
|
override suspend fun topAlbums(): Home? {
|
||||||
val albums = TopAndRecentlyPlayedTracksLoader.getTopAlbums(context)
|
val albums = TopAndRecentlyPlayedTracksLoader.getTopAlbums(context)
|
||||||
return if (albums.isNotEmpty()) {
|
return if (albums.isNotEmpty()) Home(
|
||||||
Home(
|
albums,
|
||||||
3,
|
HomeAdapter.TOP_ALBUMS,
|
||||||
R.string.top_albums,
|
R.drawable.ic_album_white_24dp
|
||||||
albums,
|
) else null
|
||||||
HomeAdapter.TOP_ALBUMS,
|
|
||||||
R.drawable.ic_album_white_24dp
|
|
||||||
)
|
|
||||||
} else null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun topArtists(): Home? {
|
override suspend fun topArtists(): Home? {
|
||||||
|
|
||||||
val artists = TopAndRecentlyPlayedTracksLoader.getTopArtists(context)
|
val artists = TopAndRecentlyPlayedTracksLoader.getTopArtists(context)
|
||||||
return if (artists.isNotEmpty()) {
|
return if (artists.isNotEmpty()) Home(
|
||||||
Home(
|
artists,
|
||||||
2,
|
HomeAdapter.TOP_ARTISTS,
|
||||||
R.string.top_artists,
|
R.drawable.ic_artist_white_24dp
|
||||||
artists,
|
) else null
|
||||||
HomeAdapter.TOP_ARTISTS,
|
|
||||||
R.drawable.ic_artist_white_24dp
|
|
||||||
)
|
|
||||||
} else null
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun favoritePlaylist(): Home? {
|
override suspend fun favoritePlaylist(): Home? {
|
||||||
val playlists = PlaylistLoader.getFavoritePlaylist(context)
|
val playlists = PlaylistLoader.getFavoritePlaylist(context)
|
||||||
return if (playlists.isNotEmpty()) {
|
return if (playlists.isNotEmpty()) Home(
|
||||||
Home(
|
playlists,
|
||||||
4,
|
HomeAdapter.PLAYLISTS,
|
||||||
R.string.favorites,
|
R.drawable.ic_favorite_white_24dp
|
||||||
playlists,
|
) else null
|
||||||
HomeAdapter.PLAYLISTS,
|
|
||||||
R.drawable.ic_favorite_white_24dp
|
|
||||||
)
|
|
||||||
} else null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun artistInfo(
|
override suspend fun artistInfo(
|
||||||
|
|
|
@ -42,6 +42,11 @@ interface Repository {
|
||||||
|
|
||||||
suspend fun getGenre(genreId: Int): ArrayList<Song>
|
suspend fun getGenre(genreId: Int): ArrayList<Song>
|
||||||
|
|
||||||
|
suspend fun artistInfo(name: String, lang: String?, cache: String?): LastFmArtist
|
||||||
|
|
||||||
|
suspend fun albumInfo(artist: String, album: String): LastFmAlbum
|
||||||
|
|
||||||
|
suspend fun artistById(artistId: Int): Artist
|
||||||
suspend fun recentArtists(): Home?
|
suspend fun recentArtists(): Home?
|
||||||
|
|
||||||
suspend fun topArtists(): Home?
|
suspend fun topArtists(): Home?
|
||||||
|
@ -52,9 +57,5 @@ interface Repository {
|
||||||
|
|
||||||
suspend fun favoritePlaylist(): Home?
|
suspend fun favoritePlaylist(): Home?
|
||||||
|
|
||||||
suspend fun artistInfo(name: String, lang: String?, cache: String?): LastFmArtist
|
suspend fun suggestions(): Home?
|
||||||
|
|
||||||
suspend fun albumInfo(artist: String, album: String): LastFmAlbum
|
|
||||||
|
|
||||||
suspend fun artistById(artistId: Int): Artist
|
|
||||||
}
|
}
|
|
@ -127,4 +127,16 @@ public class CalendarUtil {
|
||||||
final Calendar monthCal = new GregorianCalendar(calendar.get(Calendar.YEAR), month, 1);
|
final Calendar monthCal = new GregorianCalendar(calendar.get(Calendar.YEAR), month, 1);
|
||||||
return monthCal.getActualMaximum(Calendar.DAY_OF_MONTH);
|
return monthCal.getActualMaximum(Calendar.DAY_OF_MONTH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the time elapsed so far last N days in milliseconds.
|
||||||
|
*
|
||||||
|
* @return Time elapsed since N days in milliseconds.
|
||||||
|
*/
|
||||||
|
public long getElapsedDays(int numDays) {
|
||||||
|
long elapsed = getElapsedToday();
|
||||||
|
elapsed += numDays * MS_PER_DAY;
|
||||||
|
|
||||||
|
return elapsed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package code.name.monkey.retromusic.util
|
package code.name.monkey.retromusic.util
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
|
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
|
||||||
import android.net.ConnectivityManager
|
import android.net.ConnectivityManager
|
||||||
import android.net.NetworkInfo
|
import android.net.NetworkInfo
|
||||||
|
@ -23,6 +24,7 @@ import com.google.gson.JsonSyntaxException
|
||||||
import com.google.gson.reflect.TypeToken
|
import com.google.gson.reflect.TypeToken
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
|
|
||||||
object PreferenceUtil {
|
object PreferenceUtil {
|
||||||
private val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(App.getContext())
|
private val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(App.getContext())
|
||||||
|
|
||||||
|
@ -537,6 +539,44 @@ object PreferenceUtil {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getRecentlyPlayedCutoffTimeMillis(): Long {
|
||||||
|
return getCutoffTimeMillis(RECENTLY_PLAYED_CUTOFF)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getRecentlyPlayedCutoffText(context: Context): String? {
|
||||||
|
return getCutoffText(RECENTLY_PLAYED_CUTOFF, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getCutoffText(
|
||||||
|
cutoff: String,
|
||||||
|
context: Context
|
||||||
|
): String? {
|
||||||
|
return when (sharedPreferences.getString(cutoff, "")) {
|
||||||
|
"today" -> context.getString(R.string.today)
|
||||||
|
"this_week" -> context.getString(R.string.this_week)
|
||||||
|
"past_seven_days" -> context.getString(R.string.past_seven_days)
|
||||||
|
"past_three_months" -> context.getString(R.string.past_three_months)
|
||||||
|
"this_year" -> context.getString(R.string.this_year)
|
||||||
|
"this_month" -> context.getString(R.string.this_month)
|
||||||
|
else -> context.getString(R.string.this_month)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getCutoffTimeMillis(cutoff: String): Long {
|
||||||
|
val calendarUtil = CalendarUtil()
|
||||||
|
val interval: Long
|
||||||
|
interval = when (sharedPreferences.getString(cutoff, "")) {
|
||||||
|
"today" -> calendarUtil.elapsedToday
|
||||||
|
"this_week" -> calendarUtil.elapsedWeek
|
||||||
|
"past_seven_days" -> calendarUtil.getElapsedDays(7)
|
||||||
|
"past_three_months" -> calendarUtil.getElapsedMonths(3)
|
||||||
|
"this_year" -> calendarUtil.elapsedYear
|
||||||
|
"this_month" -> calendarUtil.elapsedMonth
|
||||||
|
else -> calendarUtil.elapsedMonth
|
||||||
|
}
|
||||||
|
return System.currentTimeMillis() - interval
|
||||||
|
}
|
||||||
|
|
||||||
val lastAddedCutoff: Long
|
val lastAddedCutoff: Long
|
||||||
get() {
|
get() {
|
||||||
val calendarUtil = CalendarUtil()
|
val calendarUtil = CalendarUtil()
|
||||||
|
|
9
app/src/main/res/drawable/ic_watch_later_white_24dp.xml
Normal file
9
app/src/main/res/drawable/ic_watch_later_white_24dp.xml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@color/md_white_1000"
|
||||||
|
android:pathData="M4.27 3L3 4.27L12 13.27V13.55C11.41 13.21 10.73 13 10 13C7.79 13 6 14.79 6 17S7.79 21 10 21 14 19.21 14 17V15.27L19.73 21L21 19.73L4.27 3M14 7H18V3H12V8.18L14 10.18Z" />
|
||||||
|
</vector>
|
207
app/src/main/res/layout/item_suggestions.xml
Normal file
207
app/src/main/res/layout/item_suggestions.xml
Normal file
|
@ -0,0 +1,207 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="4dp">
|
||||||
|
|
||||||
|
<com.google.android.material.textview.MaterialTextView
|
||||||
|
android:id="@+id/title"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingHorizontal="16dp"
|
||||||
|
android:paddingVertical="12dp"
|
||||||
|
android:text="@string/suggestion_songs"
|
||||||
|
android:textAppearance="@style/TextViewHeadline6"
|
||||||
|
android:textStyle="bold"
|
||||||
|
app:layout_constrainedWidth="true"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:id="@+id/card1"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
app:cardCornerRadius="8dp"
|
||||||
|
app:cardUseCompatPadding="true"
|
||||||
|
app:layout_constraintDimensionRatio="1,1"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/card2"
|
||||||
|
app:layout_constraintHorizontal_weight="2"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/card6">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
|
android:id="@+id/image4"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
tools:src="@tools:sample/avatars" />
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:id="@+id/card2"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
app:cardCornerRadius="8dp"
|
||||||
|
app:cardUseCompatPadding="true"
|
||||||
|
app:layout_constraintDimensionRatio="1,1"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/card3"
|
||||||
|
app:layout_constraintHorizontal_weight="2"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/card1"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/card6">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
|
android:id="@+id/image5"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
tools:src="@tools:sample/avatars" />
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:id="@+id/card3"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
app:cardCornerRadius="8dp"
|
||||||
|
app:cardUseCompatPadding="true"
|
||||||
|
app:layout_constraintDimensionRatio="1,1"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/card4"
|
||||||
|
app:layout_constraintHorizontal_weight="2"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/card2"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/card6">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
|
android:id="@+id/image6"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
tools:src="@tools:sample/avatars" />
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:id="@+id/card4"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
app:cardCornerRadius="8dp"
|
||||||
|
app:cardUseCompatPadding="true"
|
||||||
|
app:layout_constraintDimensionRatio="1,1"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/card5"
|
||||||
|
app:layout_constraintHorizontal_weight="2"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/card3"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/card6">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
|
android:id="@+id/image7"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
tools:src="@tools:sample/avatars" />
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:id="@+id/card5"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
app:cardCornerRadius="8dp"
|
||||||
|
app:cardUseCompatPadding="true"
|
||||||
|
app:layout_constraintDimensionRatio="1,1"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHorizontal_weight="2"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/card4"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/card6">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
|
android:id="@+id/image8"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
tools:src="@tools:sample/avatars" />
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:id="@+id/card6"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
app:cardBackgroundColor="?attr/colorSurface"
|
||||||
|
app:cardCornerRadius="8dp"
|
||||||
|
app:cardUseCompatPadding="true"
|
||||||
|
app:layout_constraintDimensionRatio="1,1"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/card7"
|
||||||
|
app:layout_constraintHorizontal_weight="4"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/title">
|
||||||
|
|
||||||
|
<com.google.android.material.textview.MaterialTextView
|
||||||
|
android:id="@+id/text"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:text="New music mix"
|
||||||
|
android:textAppearance="@style/TextViewHeadline4"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:id="@+id/card7"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
app:cardCornerRadius="8dp"
|
||||||
|
app:cardUseCompatPadding="true"
|
||||||
|
app:layout_constraintDimensionRatio="1,1"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/card8"
|
||||||
|
app:layout_constraintHorizontal_weight="2"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/card6"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/title">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
|
android:id="@+id/image1"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
tools:src="@tools:sample/avatars" />
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:id="@+id/card8"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
app:cardCornerRadius="8dp"
|
||||||
|
app:cardUseCompatPadding="true"
|
||||||
|
app:layout_constraintDimensionRatio="1,1"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHorizontal_weight="4"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/card7"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/title">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
|
android:id="@+id/image2"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
tools:src="@tools:sample/avatars" />
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:id="@+id/card9"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
app:cardCornerRadius="8dp"
|
||||||
|
app:cardUseCompatPadding="true"
|
||||||
|
app:layout_constraintDimensionRatio="1,1"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/card8"
|
||||||
|
app:layout_constraintHorizontal_weight="2"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/card6"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/card7">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
|
android:id="@+id/image3"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
tools:src="@tools:sample/avatars" />
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -857,6 +857,8 @@
|
||||||
<string name="help_summary">Need more help?</string>
|
<string name="help_summary">Need more help?</string>
|
||||||
<string name="gradient">Gradient</string>
|
<string name="gradient">Gradient</string>
|
||||||
<string name="user_name">User Name</string>
|
<string name="user_name">User Name</string>
|
||||||
|
<string name="not_recently_played">Not recently played</string>
|
||||||
|
<string name="past_seven_days">Past 7 days</string>
|
||||||
|
|
||||||
<plurals name="albumSongs">
|
<plurals name="albumSongs">
|
||||||
<item quantity="one">Song</item>
|
<item quantity="one">Song</item>
|
||||||
|
|
|
@ -28,7 +28,7 @@ dependencies {
|
||||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||||
implementation 'androidx.appcompat:appcompat:1.1.0'
|
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||||
implementation 'com.google.android.material:material:1.2.0-alpha05'
|
implementation 'com.google.android.material:material:1.2.0-alpha05'
|
||||||
implementation 'androidx.preference:preference:1.1.0'
|
implementation 'androidx.preference:preference:1.1.1'
|
||||||
implementation 'androidx.cardview:cardview:1.0.0'
|
implementation 'androidx.cardview:cardview:1.0.0'
|
||||||
// Used for the list preference classes
|
// Used for the list preference classes
|
||||||
def material_dialog_version = "0.9.6.0"
|
def material_dialog_version = "0.9.6.0"
|
||||||
|
|
Loading…
Reference in a new issue