From 547e49507e6ff02f5f055ef05f157edb09120169 Mon Sep 17 00:00:00 2001 From: Hemanth S Date: Tue, 14 Jul 2020 02:09:47 +0530 Subject: [PATCH] Added suggestions & Updated libs. --- app/build.gradle | 4 +- .../code/name/monkey/retromusic/Constants.kt | 1 + .../monkey/retromusic/adapter/HomeAdapter.kt | 112 ++++++---- .../retromusic/fragments/LibraryViewModel.kt | 3 +- .../fragments/home/BannerHomeFragment.kt | 10 +- .../player/gradient/GradientPlayerFragment.kt | 4 + .../TopAndRecentlyPlayedTracksLoader.kt | 101 +++++++-- .../code/name/monkey/retromusic/model/Home.kt | 5 +- .../monkey/retromusic/model/Playlist.java | 10 + .../NotRecentlyPlayedPlaylist.java | 69 ++++++ .../retromusic/mvp/presenter/HomePresenter.kt | 66 ------ .../retromusic/providers/HistoryStore.java | 15 ++ .../retromusic/providers/RepositoryImpl.kt | 71 +++--- .../providers/interfaces/Repository.kt | 11 +- .../monkey/retromusic/util/CalendarUtil.java | 12 + .../monkey/retromusic/util/PreferenceUtil.kt | 40 ++++ .../drawable/ic_watch_later_white_24dp.xml | 9 + app/src/main/res/layout/item_suggestions.xml | 207 ++++++++++++++++++ app/src/main/res/values/strings.xml | 2 + appthemehelper/build.gradle | 2 +- 20 files changed, 568 insertions(+), 186 deletions(-) create mode 100644 app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/NotRecentlyPlayedPlaylist.java delete mode 100644 app/src/main/java/code/name/monkey/retromusic/mvp/presenter/HomePresenter.kt create mode 100644 app/src/main/res/drawable/ic_watch_later_white_24dp.xml create mode 100644 app/src/main/res/layout/item_suggestions.xml diff --git a/app/build.gradle b/app/build.gradle index d4726633..528f9b57 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -133,7 +133,7 @@ dependencies { 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:converter-gson:$retrofit_version" @@ -176,7 +176,7 @@ dependencies { implementation "androidx.lifecycle:lifecycle-common-java8:$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' debugImplementation 'com.amitshekhar.android:debug-db:1.0.4' implementation 'com.github.dhaval2404:imagepicker:1.7.1' diff --git a/app/src/main/java/code/name/monkey/retromusic/Constants.kt b/app/src/main/java/code/name/monkey/retromusic/Constants.kt index 8ae0d403..8388eb2e 100644 --- a/app/src/main/java/code/name/monkey/retromusic/Constants.kt +++ b/app/src/main/java/code/name/monkey/retromusic/Constants.kt @@ -116,6 +116,7 @@ const val IGNORE_MEDIA_STORE_ARTWORK = "ignore_media_store_artwork" const val LAST_CHANGELOG_VERSION = "last_changelog_version" const val AUTO_DOWNLOAD_IMAGES_POLICY = "auto_download_images_policy" const val START_DIRECTORY = "start_directory" +const val RECENTLY_PLAYED_CUTOFF = "recently_played_interval" const val LOCK_SCREEN = "lock_screen" const val ALBUM_DETAIL_SONG_SORT_ORDER = "album_detail_song_sort_order" const val LYRICS_OPTIONS = "lyrics_tab_position" diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/HomeAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/HomeAdapter.kt index e881b1a9..0e0b79f4 100644 --- a/app/src/main/java/code/name/monkey/retromusic/adapter/HomeAdapter.kt +++ b/app/src/main/java/code/name/monkey/retromusic/adapter/HomeAdapter.kt @@ -4,24 +4,25 @@ import android.util.DisplayMetrics import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.TextView import androidx.annotation.IntDef import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.AppCompatTextView import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView +import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.retromusic.R import code.name.monkey.retromusic.adapter.album.AlbumFullWidthAdapter import code.name.monkey.retromusic.adapter.artist.ArtistAdapter import code.name.monkey.retromusic.adapter.song.SongAdapter 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.model.Album -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.model.* import code.name.monkey.retromusic.util.PreferenceUtil +import com.bumptech.glide.Glide class HomeAdapter( private val activity: AppCompatActivity, @@ -40,6 +41,15 @@ class HomeAdapter( return when (viewType) { RECENT_ARTISTS, TOP_ARTISTS -> ArtistViewHolder(layout) PLAYLISTS -> PlaylistViewHolder(layout) + SUGGESTIONS -> { + SuggestionsViewHolder( + LayoutInflater.from(activity).inflate( + R.layout.item_suggestions, + parent, + false + ) + ) + } else -> { AlbumViewHolder( LayoutInflater.from(activity).inflate( @@ -56,23 +66,41 @@ class HomeAdapter( when (getItemViewType(position)) { RECENT_ALBUMS -> { val viewHolder = holder as AlbumViewHolder - viewHolder.bindView(list[position].arrayList.toAlbums(), R.string.recent_albums) + viewHolder.bindView( + list[position].arrayList as List, + R.string.recent_albums + ) } TOP_ALBUMS -> { val viewHolder = holder as AlbumViewHolder - viewHolder.bindView(list[position].arrayList.toAlbums(), R.string.top_albums) + viewHolder.bindView( + list[position].arrayList as List, + R.string.top_albums + ) } RECENT_ARTISTS -> { val viewHolder = holder as ArtistViewHolder - viewHolder.bindView(list[position].arrayList.toArtists(), R.string.recent_artists) + viewHolder.bindView( + list[position].arrayList as List, + R.string.recent_artists + ) } TOP_ARTISTS -> { val viewHolder = holder as ArtistViewHolder - viewHolder.bindView(list[position].arrayList.toArtists(), R.string.top_artists) + viewHolder.bindView(list[position].arrayList as List, R.string.top_artists) } PLAYLISTS -> { val viewHolder = holder as PlaylistViewHolder - viewHolder.bindView(list[position].arrayList.toPlaylist(), R.string.favorites) + viewHolder.bindView( + list[position].arrayList as List, + R.string.favorites + ) + } + SUGGESTIONS -> { + val viewHolder = holder as SuggestionsViewHolder + viewHolder.bindView( + list[position].arrayList as List + ) } } } @@ -88,7 +116,7 @@ class HomeAdapter( 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) annotation class HomeSection @@ -96,11 +124,12 @@ class HomeAdapter( const val TOP_ALBUMS = 1 const val RECENT_ARTISTS = 2 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) { - fun bindView(list: ArrayList, titleRes: Int) { + fun bindView(list: List, titleRes: Int) { if (list.isNotEmpty()) { recyclerView.apply { show() @@ -112,7 +141,7 @@ class HomeAdapter( } inner class ArtistViewHolder(view: View) : AbsHomeViewItem(view) { - fun bindView(list: ArrayList, titleRes: Int) { + fun bindView(list: List, titleRes: Int) { if (list.isNotEmpty()) { val manager = LinearLayoutManager(activity, LinearLayoutManager.HORIZONTAL, false) 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) { + val color = ThemeStore.accentColor(activity) + itemView.findViewById(R.id.text).setTextColor(color) + + images.forEachIndexed { index, i -> + itemView.findViewById(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) { - fun bindView(arrayList: ArrayList, titleRes: Int) { + fun bindView(arrayList: List, titleRes: Int) { if (arrayList.isNotEmpty()) { val songs = PlaylistSongsLoader.getPlaylistSongList(activity, arrayList[0]) if (songs.isNotEmpty()) { @@ -155,27 +213,3 @@ class HomeAdapter( val title: AppCompatTextView = itemView.findViewById(R.id.title) } } - -private fun ArrayList.toAlbums(): ArrayList { - val arrayList = ArrayList() - for (x in this) { - arrayList.add(x as Album) - } - return arrayList -} - -private fun ArrayList.toArtists(): ArrayList { - val arrayList = ArrayList() - for (x in this) { - arrayList.add(x as Artist) - } - return arrayList -} - -private fun ArrayList.toPlaylist(): ArrayList { - val arrayList = ArrayList() - for (x in this) { - arrayList.add(x as Playlist) - } - return arrayList -} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/LibraryViewModel.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/LibraryViewModel.kt index cf259e87..1e842402 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/LibraryViewModel.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/LibraryViewModel.kt @@ -55,7 +55,8 @@ class LibraryViewModel(application: Application) : _repository.topAlbums(), _repository.recentArtists(), _repository.recentAlbums(), - _repository.favoritePlaylist() + _repository.favoritePlaylist(), + _repository.suggestions() ) for (r in result) { if (r != null) { diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/home/BannerHomeFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/home/BannerHomeFragment.kt index 5c1af339..1a67cbd9 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/home/BannerHomeFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/home/BannerHomeFragment.kt @@ -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 get() { val display = mainActivity.windowManager.defaultDisplay @@ -111,8 +104,7 @@ class BannerHomeFragment : AbsMainActivityFragment(), MainActivityFragmentCallba ) NavigationUtil.goToUserInfo(requireActivity(), options) } - titleWelcome?.text = - String.format("%s", PreferenceUtil.userName) + titleWelcome?.text = String.format("%s", PreferenceUtil.userName) homeAdapter = HomeAdapter(mainActivity, displayMetrics) recyclerView.apply { diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/gradient/GradientPlayerFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/gradient/GradientPlayerFragment.kt index cb953f40..208bbafc 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/player/gradient/GradientPlayerFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/gradient/GradientPlayerFragment.kt @@ -279,6 +279,10 @@ class GradientPlayerFragment : AbsPlayerFragment(), MusicProgressViewUpdateHelpe updateQueuePosition() } + override fun onQueueChanged() { + super.onQueueChanged() + updateLabel() + } private fun updateSong() { val song = MusicPlayerRemote.currentSong title.text = song.title diff --git a/app/src/main/java/code/name/monkey/retromusic/loaders/TopAndRecentlyPlayedTracksLoader.kt b/app/src/main/java/code/name/monkey/retromusic/loaders/TopAndRecentlyPlayedTracksLoader.kt index 0948d517..da1c21e8 100644 --- a/app/src/main/java/code/name/monkey/retromusic/loaders/TopAndRecentlyPlayedTracksLoader.kt +++ b/app/src/main/java/code/name/monkey/retromusic/loaders/TopAndRecentlyPlayedTracksLoader.kt @@ -17,14 +17,18 @@ package code.name.monkey.retromusic.loaders import android.content.Context import android.database.Cursor import android.provider.BaseColumns +import android.provider.MediaStore 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.Artist import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.providers.HistoryStore import code.name.monkey.retromusic.providers.SongPlayCountStore +import code.name.monkey.retromusic.util.PreferenceUtil import java.util.* + /** * Created by hemanths on 16/08/17. */ @@ -39,19 +43,23 @@ object TopAndRecentlyPlayedTracksLoader { return SongLoader.getSongs(makeTopTracksCursorAndClearUpDatabase(context)) } - private fun makeRecentTracksCursorAndClearUpDatabase(context: Context): Cursor? { - val retCursor = makeRecentTracksCursorImpl(context) - - // 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 + fun getNotRecentlyPlayedTracks(context: Context): ArrayList { + val allSongs = SongLoader.getSongs( + makeSongCursor( + context, + null, null, + MediaStore.Audio.Media.DATE_ADDED + " ASC" + ) + ) + val playedSongs = SongLoader.getSongs( + makePlayedTracksCursorAndClearUpDatabase(context) + ) + val notRecentlyPlayedSongs = SongLoader.getSongs( + makeNotRecentTracksCursorAndClearUpDatabase(context) + ) + allSongs.removeAll(playedSongs) + allSongs.addAll(notRecentlyPlayedSongs) + return allSongs } private fun makeTopTracksCursorAndClearUpDatabase(context: Context): Cursor? { @@ -72,21 +80,19 @@ object TopAndRecentlyPlayedTracksLoader { private fun makeRecentTracksCursorImpl(context: Context): SortedLongCursor? { // first get the top results ids from the internal database val songs = HistoryStore.getInstance(context).queryRecentIds() - - try { + songs.use { return makeSortedCursor( - context, songs, - songs!!.getColumnIndex(HistoryStore.RecentStoreColumns.ID) + context, + it, + it.getColumnIndex(HistoryStore.RecentStoreColumns.ID) ) - } finally { - songs?.close() } } private fun makeTopTracksCursorImpl(context: Context): SortedLongCursor? { // first get the top results ids from the internal database - val songs = SongPlayCountStore.getInstance(context) - .getTopPlayedResults(NUMBER_OF_TOP_TRACKS) + val songs = + SongPlayCountStore.getInstance(context).getTopPlayedResults(NUMBER_OF_TOP_TRACKS) songs.use { localSongs -> return makeSortedCursor( @@ -145,4 +151,57 @@ object TopAndRecentlyPlayedTracksLoader { fun getTopArtists(context: Context): ArrayList { 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) + ) + } + } } diff --git a/app/src/main/java/code/name/monkey/retromusic/model/Home.kt b/app/src/main/java/code/name/monkey/retromusic/model/Home.kt index 8bb4efed..3c015e1a 100644 --- a/app/src/main/java/code/name/monkey/retromusic/model/Home.kt +++ b/app/src/main/java/code/name/monkey/retromusic/model/Home.kt @@ -15,13 +15,10 @@ package code.name.monkey.retromusic.model import androidx.annotation.DrawableRes -import androidx.annotation.StringRes import code.name.monkey.retromusic.adapter.HomeAdapter.Companion.HomeSection class Home( - val priority: Int, - @StringRes val title: Int, - val arrayList: ArrayList<*>, + val arrayList: List<*>, @HomeSection val homeSection: Int, @DrawableRes diff --git a/app/src/main/java/code/name/monkey/retromusic/model/Playlist.java b/app/src/main/java/code/name/monkey/retromusic/model/Playlist.java index 2b0af369..107b287d 100644 --- a/app/src/main/java/code/name/monkey/retromusic/model/Playlist.java +++ b/app/src/main/java/code/name/monkey/retromusic/model/Playlist.java @@ -23,6 +23,7 @@ import androidx.annotation.NonNull; import java.util.ArrayList; import code.name.monkey.retromusic.loaders.PlaylistSongsLoader; +import code.name.monkey.retromusic.util.MusicUtil; public class Playlist implements Parcelable { @@ -106,5 +107,14 @@ public class Playlist implements Parcelable { 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, + "" + ); + } } diff --git a/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/NotRecentlyPlayedPlaylist.java b/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/NotRecentlyPlayedPlaylist.java new file mode 100644 index 00000000..3164907d --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/NotRecentlyPlayedPlaylist.java @@ -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 CREATOR = new Creator() { + 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 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; + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/mvp/presenter/HomePresenter.kt b/app/src/main/java/code/name/monkey/retromusic/mvp/presenter/HomePresenter.kt deleted file mode 100644 index d32fb785..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/mvp/presenter/HomePresenter.kt +++ /dev/null @@ -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) -} - -interface HomePresenter : Presenter { - fun loadSections() - - class HomePresenterImpl constructor( - private val repository: Repository - ) : PresenterImpl(), 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() - 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() - } - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/providers/HistoryStore.java b/app/src/main/java/code/name/monkey/retromusic/providers/HistoryStore.java index 0e283f3d..e981aead 100644 --- a/app/src/main/java/code/name/monkey/retromusic/providers/HistoryStore.java +++ b/app/src/main/java/code/name/monkey/retromusic/providers/HistoryStore.java @@ -141,6 +141,21 @@ public class HistoryStore extends SQLiteOpenHelper { 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 { String NAME = "recent_history"; diff --git a/app/src/main/java/code/name/monkey/retromusic/providers/RepositoryImpl.kt b/app/src/main/java/code/name/monkey/retromusic/providers/RepositoryImpl.kt index 55c00ce2..315b62bb 100644 --- a/app/src/main/java/code/name/monkey/retromusic/providers/RepositoryImpl.kt +++ b/app/src/main/java/code/name/monkey/retromusic/providers/RepositoryImpl.kt @@ -19,6 +19,7 @@ import code.name.monkey.retromusic.R import code.name.monkey.retromusic.adapter.HomeAdapter import code.name.monkey.retromusic.loaders.* 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.rest.LastFmClient 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 = 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 = SearchLoader.searchAll(context, query) @@ -58,8 +71,6 @@ class RepositoryImpl constructor(private val context: Context) : Repository { override suspend fun recentArtists(): Home? { val artists = LastAddedSongsLoader.getLastAddedArtists(context) return if (artists.isNotEmpty()) Home( - 0, - R.string.recent_artists, artists, HomeAdapter.RECENT_ARTISTS, R.drawable.ic_artist_white_24dp @@ -68,56 +79,40 @@ class RepositoryImpl constructor(private val context: Context) : Repository { override suspend fun recentAlbums(): Home? { val albums = LastAddedSongsLoader.getLastAddedAlbums(context) - return if (albums.isNotEmpty()) { - Home( - 1, - R.string.recent_albums, - albums, - HomeAdapter.RECENT_ALBUMS, - R.drawable.ic_album_white_24dp - ) - } else null + return if (albums.isNotEmpty()) Home( + albums, + HomeAdapter.RECENT_ALBUMS, + R.drawable.ic_album_white_24dp + ) else null } override suspend fun topAlbums(): Home? { val albums = TopAndRecentlyPlayedTracksLoader.getTopAlbums(context) - return if (albums.isNotEmpty()) { - Home( - 3, - R.string.top_albums, - albums, - HomeAdapter.TOP_ALBUMS, - R.drawable.ic_album_white_24dp - ) - } else null + return if (albums.isNotEmpty()) Home( + albums, + HomeAdapter.TOP_ALBUMS, + R.drawable.ic_album_white_24dp + ) else null } override suspend fun topArtists(): Home? { val artists = TopAndRecentlyPlayedTracksLoader.getTopArtists(context) - return if (artists.isNotEmpty()) { - Home( - 2, - R.string.top_artists, - artists, - HomeAdapter.TOP_ARTISTS, - R.drawable.ic_artist_white_24dp - ) - } else null + return if (artists.isNotEmpty()) Home( + artists, + HomeAdapter.TOP_ARTISTS, + R.drawable.ic_artist_white_24dp + ) else null } override suspend fun favoritePlaylist(): Home? { val playlists = PlaylistLoader.getFavoritePlaylist(context) - return if (playlists.isNotEmpty()) { - Home( - 4, - R.string.favorites, - playlists, - HomeAdapter.PLAYLISTS, - R.drawable.ic_favorite_white_24dp - ) - } else null + return if (playlists.isNotEmpty()) Home( + playlists, + HomeAdapter.PLAYLISTS, + R.drawable.ic_favorite_white_24dp + ) else null } override suspend fun artistInfo( diff --git a/app/src/main/java/code/name/monkey/retromusic/providers/interfaces/Repository.kt b/app/src/main/java/code/name/monkey/retromusic/providers/interfaces/Repository.kt index a3b61341..95eada80 100644 --- a/app/src/main/java/code/name/monkey/retromusic/providers/interfaces/Repository.kt +++ b/app/src/main/java/code/name/monkey/retromusic/providers/interfaces/Repository.kt @@ -42,6 +42,11 @@ interface Repository { suspend fun getGenre(genreId: Int): ArrayList + 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 topArtists(): Home? @@ -52,9 +57,5 @@ interface Repository { suspend fun favoritePlaylist(): Home? - 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 suggestions(): Home? } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/util/CalendarUtil.java b/app/src/main/java/code/name/monkey/retromusic/util/CalendarUtil.java index cfbc1ce4..e126a37c 100644 --- a/app/src/main/java/code/name/monkey/retromusic/util/CalendarUtil.java +++ b/app/src/main/java/code/name/monkey/retromusic/util/CalendarUtil.java @@ -127,4 +127,16 @@ public class CalendarUtil { final Calendar monthCal = new GregorianCalendar(calendar.get(Calendar.YEAR), month, 1); 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; + } } diff --git a/app/src/main/java/code/name/monkey/retromusic/util/PreferenceUtil.kt b/app/src/main/java/code/name/monkey/retromusic/util/PreferenceUtil.kt index 292f01aa..2b4b5c9e 100644 --- a/app/src/main/java/code/name/monkey/retromusic/util/PreferenceUtil.kt +++ b/app/src/main/java/code/name/monkey/retromusic/util/PreferenceUtil.kt @@ -1,5 +1,6 @@ package code.name.monkey.retromusic.util +import android.content.Context import android.content.SharedPreferences.OnSharedPreferenceChangeListener import android.net.ConnectivityManager import android.net.NetworkInfo @@ -23,6 +24,7 @@ import com.google.gson.JsonSyntaxException import com.google.gson.reflect.TypeToken import java.io.File + object PreferenceUtil { 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 get() { val calendarUtil = CalendarUtil() diff --git a/app/src/main/res/drawable/ic_watch_later_white_24dp.xml b/app/src/main/res/drawable/ic_watch_later_white_24dp.xml new file mode 100644 index 00000000..09739534 --- /dev/null +++ b/app/src/main/res/drawable/ic_watch_later_white_24dp.xml @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_suggestions.xml b/app/src/main/res/layout/item_suggestions.xml new file mode 100644 index 00000000..f216843f --- /dev/null +++ b/app/src/main/res/layout/item_suggestions.xml @@ -0,0 +1,207 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index bef72a12..5ef8c08a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -857,6 +857,8 @@ Need more help? Gradient User Name + Not recently played + Past 7 days Song diff --git a/appthemehelper/build.gradle b/appthemehelper/build.gradle index f593b1c3..3ca1e596 100644 --- a/appthemehelper/build.gradle +++ b/appthemehelper/build.gradle @@ -28,7 +28,7 @@ dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') implementation 'androidx.appcompat:appcompat:1.1.0' 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' // Used for the list preference classes def material_dialog_version = "0.9.6.0"