From e05f218d1d01ea944915df425a17ec90aa6d5afd Mon Sep 17 00:00:00 2001 From: Nguyen Duc Tuan Minh Date: Sun, 1 Sep 2024 20:32:23 +0700 Subject: [PATCH] Fixed #515, Migrated from Dagger Hilt to Koin --- .idea/ktlint-plugin.xml | 1 - ...otlin-compiler-13053588321658305948.salive | 0 app/build.gradle.kts | 19 +- .../maxrave/simpmusic/SimpMusicApplication.kt | 43 +- .../com/maxrave/simpmusic/common/Config.kt | 297 +- .../data/dataStore/DataStoreManager.kt | 5 +- .../simpmusic/data/db/LocalDataSource.kt | 5 +- .../data/repository/MainRepository.kt | 3022 ++++++++++------- .../maxrave/simpmusic/di/DatabaseModule.kt | 132 + .../simpmusic/di/LocalServiceModule.kt | 130 - .../simpmusic/di/MediaServiceModule.kt | 70 + .../simpmusic/di/MusicServiceModule.kt | 73 - .../simpmusic/service/SimpleMediaService.kt | 308 +- .../service/SimpleMediaSessionCallback.kt | 12 +- .../service/test/download/DownloadUtils.kt | 18 +- .../test/download/MusicDownloadService.kt | 7 +- .../service/test/notification/NotifyWork.kt | 25 +- .../com/maxrave/simpmusic/ui/MainActivity.kt | 249 +- .../simpmusic/ui/fragment/SearchFragment.kt | 8 - .../ui/fragment/home/GenreFragment.kt | 4 +- .../ui/fragment/home/HomeFragment.kt | 4 +- .../ui/fragment/home/MoodFragment.kt | 3 - .../ui/fragment/home/NotificationFragment.kt | 4 +- .../ui/fragment/home/RecentlySongsFragment.kt | 4 +- .../ui/fragment/home/SettingsFragment.kt | 1123 +++--- .../ui/fragment/library/DownloadedFragment.kt | 4 +- .../ui/fragment/library/FavoriteFragment.kt | 9 +- .../ui/fragment/library/FollowedFragment.kt | 4 +- .../ui/fragment/library/LibraryFragment.kt | 4 +- .../ui/fragment/library/MostPlayedFragment.kt | 4 +- .../ui/fragment/login/LogInFragment.kt | 4 +- .../ui/fragment/login/MusixmatchFragment.kt | 4 +- .../ui/fragment/login/SpotifyLogInFragment.kt | 4 +- .../ui/fragment/other/AlbumFragment.kt | 4 +- .../ui/fragment/other/ArtistFragment.kt | 4 +- .../ui/fragment/other/CreditFragment.kt | 8 +- .../fragment/other/LocalPlaylistFragment.kt | 6 +- .../ui/fragment/other/MoreAlbumsFragment.kt | 4 +- .../ui/fragment/other/PlaylistFragment.kt | 718 ++-- .../ui/fragment/other/PodcastFragment.kt | 2 - .../ui/fragment/player/FullscreenFragment.kt | 3 +- .../ui/fragment/player/InfoFragment.kt | 4 +- .../ui/fragment/player/NowPlayingFragment.kt | 1209 +++---- .../ui/fragment/player/QueueFragment.kt | 291 +- .../simpmusic/viewModel/AlbumViewModel.kt | 23 +- .../simpmusic/viewModel/ArtistViewModel.kt | 15 +- .../viewModel/DownloadedViewModel.kt | 20 +- .../simpmusic/viewModel/FavoriteViewModel.kt | 25 +- .../simpmusic/viewModel/FollowedViewModel.kt | 14 +- .../simpmusic/viewModel/GenreViewModel.kt | 13 +- .../simpmusic/viewModel/HomeViewModel.kt | 33 +- .../simpmusic/viewModel/LibraryViewModel.kt | 24 +- .../viewModel/LocalPlaylistViewModel.kt | 23 +- .../simpmusic/viewModel/LogInViewModel.kt | 21 +- .../simpmusic/viewModel/MoodViewModel.kt | 23 +- .../viewModel/MoreAlbumsViewModel.kt | 14 +- .../viewModel/MostPlayedViewModel.kt | 19 +- .../viewModel/MusixmatchViewModel.kt | 34 +- .../viewModel/NotificationViewModel.kt | 19 +- .../simpmusic/viewModel/PlaylistViewModel.kt | 784 ++--- .../simpmusic/viewModel/PodcastViewModel.kt | 19 +- .../viewModel/RecentlySongsViewModel.kt | 19 +- .../simpmusic/viewModel/SearchViewModel.kt | 199 +- .../simpmusic/viewModel/SettingsViewModel.kt | 282 +- .../simpmusic/viewModel/SharedViewModel.kt | 72 +- .../simpmusic/viewModel/base/BaseViewModel.kt | 31 + app/src/main/res/values-ar/strings.xml | 2 +- .../main/res/values-b+zh+Hant+TW/strings.xml | 2 +- app/src/main/res/values-de/strings.xml | 2 +- app/src/main/res/values-es/strings.xml | 2 +- app/src/main/res/values-fi/strings.xml | 2 +- app/src/main/res/values-fr/strings.xml | 2 +- app/src/main/res/values-in/strings.xml | 2 +- app/src/main/res/values-it/strings.xml | 2 +- app/src/main/res/values-iw/strings.xml | 2 +- app/src/main/res/values-ja/strings.xml | 2 +- app/src/main/res/values-pl/strings.xml | 2 +- app/src/main/res/values-pt/strings.xml | 2 +- app/src/main/res/values-ru/strings.xml | 2 +- app/src/main/res/values-tr/strings.xml | 2 +- app/src/main/res/values-uk/strings.xml | 2 +- app/src/main/res/values-vi/strings.xml | 2 +- app/src/main/res/values-zh/strings.xml | 2 +- app/src/main/res/values/strings.xml | 2 +- build.gradle.kts | 1 - gradle/libs.versions.toml | 23 +- 86 files changed, 5157 insertions(+), 4485 deletions(-) create mode 100644 .kotlin/sessions/kotlin-compiler-13053588321658305948.salive create mode 100644 app/src/main/java/com/maxrave/simpmusic/di/DatabaseModule.kt delete mode 100644 app/src/main/java/com/maxrave/simpmusic/di/LocalServiceModule.kt create mode 100644 app/src/main/java/com/maxrave/simpmusic/di/MediaServiceModule.kt delete mode 100644 app/src/main/java/com/maxrave/simpmusic/di/MusicServiceModule.kt create mode 100644 app/src/main/java/com/maxrave/simpmusic/viewModel/base/BaseViewModel.kt diff --git a/.idea/ktlint-plugin.xml b/.idea/ktlint-plugin.xml index 52c6220f..bee5678e 100644 --- a/.idea/ktlint-plugin.xml +++ b/.idea/ktlint-plugin.xml @@ -2,6 +2,5 @@ DISTRACT_FREE - false \ No newline at end of file diff --git a/.kotlin/sessions/kotlin-compiler-13053588321658305948.salive b/.kotlin/sessions/kotlin-compiler-13053588321658305948.salive new file mode 100644 index 00000000..e69de29b diff --git a/app/build.gradle.kts b/app/build.gradle.kts index d8bcce3b..4896cea2 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -5,7 +5,6 @@ plugins { alias(libs.plugins.kotlin.android) alias(libs.plugins.compose.compiler) alias(libs.plugins.navigation.safeargs) - alias(libs.plugins.hilt.android) alias(libs.plugins.ksp) alias(libs.plugins.aboutlibraries) } @@ -24,6 +23,7 @@ android { ksp { arg("room.schemaLocation", "$projectDir/schemas") + arg("KOIN_CONFIG_CHECK", "true") } resourceConfigurations += @@ -256,11 +256,6 @@ dependencies { // Fragment KTX implementation(libs.fragment.ktx) - // Hilt - implementation(libs.hilt.android) - implementation(libs.hilt.work) - ksp(libs.hilt.compiler) - ksp(libs.dagger.hilt.compiler) ksp(libs.kotlinx.metadata.jvm) // DataStore implementation(libs.datastore.preferences) @@ -302,9 +297,15 @@ dependencies { implementation(libs.insetsx) coreLibraryDesugaring(libs.desugaring) -} -hilt { - enableAggregatingTask = true + + //Koin + implementation(platform(libs.koin.bom)) + implementation(libs.koin.core) + implementation(libs.koin.android) + implementation(libs.koin.workmanager) + implementation(libs.koin.androidx.compose) + implementation(libs.koin.annotations) + ksp(libs.koin.ksp) } aboutLibraries { prettyPrint = true diff --git a/app/src/main/java/com/maxrave/simpmusic/SimpMusicApplication.kt b/app/src/main/java/com/maxrave/simpmusic/SimpMusicApplication.kt index 10cb4284..40a952f8 100644 --- a/app/src/main/java/com/maxrave/simpmusic/SimpMusicApplication.kt +++ b/app/src/main/java/com/maxrave/simpmusic/SimpMusicApplication.kt @@ -4,31 +4,36 @@ import android.app.Application import android.content.Context import android.util.Log import androidx.appcompat.app.AppCompatDelegate -import androidx.hilt.work.HiltWorkerFactory import androidx.media3.common.util.UnstableApi -import androidx.work.Configuration import cat.ereza.customactivityoncrash.config.CaocConfig +import com.maxrave.simpmusic.di.databaseModule +import com.maxrave.simpmusic.di.mediaServiceModule import com.maxrave.simpmusic.ui.MainActivity -import dagger.hilt.android.HiltAndroidApp -import javax.inject.Inject - -@HiltAndroidApp -class SimpMusicApplication : Application(), Configuration.Provider { - @Inject - lateinit var workerFactory: HiltWorkerFactory - - override val workManagerConfiguration: Configuration - get() = - Configuration.Builder() - .setWorkerFactory(workerFactory) - .setMinimumLoggingLevel(Log.WARN) - .build() +import org.koin.android.ext.koin.androidContext +import org.koin.android.ext.koin.androidLogger +import org.koin.androidx.workmanager.koin.workManagerFactory +import org.koin.core.component.KoinComponent +import org.koin.core.context.startKoin +import org.koin.core.logger.Level +class SimpMusicApplication : + Application(), + KoinComponent { @UnstableApi override fun onCreate() { super.onCreate() AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES) - CaocConfig.Builder.create() + startKoin { + androidLogger(level = Level.DEBUG) + androidContext(this@SimpMusicApplication) + modules( + databaseModule, + mediaServiceModule, + ) + workManagerFactory() + } + CaocConfig.Builder + .create() .backgroundMode(CaocConfig.BACKGROUND_MODE_SILENT) // default: CaocConfig.BACKGROUND_MODE_SHOW_CUSTOM .enabled(true) // default: true .showErrorDetails(true) // default: true @@ -59,8 +64,6 @@ class SimpMusicApplication : Application(), Configuration.Provider { companion object { private var instance: SimpMusicApplication? = null - fun applicationContext(): Context { - return instance!!.applicationContext - } + fun applicationContext(): Context = instance!!.applicationContext } } \ No newline at end of file diff --git a/app/src/main/java/com/maxrave/simpmusic/common/Config.kt b/app/src/main/java/com/maxrave/simpmusic/common/Config.kt index 5f883042..11b6b800 100644 --- a/app/src/main/java/com/maxrave/simpmusic/common/Config.kt +++ b/app/src/main/java/com/maxrave/simpmusic/common/Config.kt @@ -5,7 +5,6 @@ import java.time.LocalDateTime import java.time.Month object Config { - const val SPOTIFY_LOG_IN_URL: String = "https://accounts.spotify.com/en/login" const val SPOTIFY_ACCOUNT_URL = "https://accounts.spotify.com/en/status" const val YOUTUBE_MUSIC_MAIN_URL = "https://music.youtube.com/" @@ -21,22 +20,26 @@ object Config { const val SHARE = "SHARE" const val RECOVER_TRACK_QUEUE = "RECOVER_TRACK_QUEUE" - val REMOVED_SONG_DATE_TIME = LocalDateTime.of(2003, Month.AUGUST, 26, 3, 0) + const val PLAYER_CACHE = "playerCache" + const val DOWNLOAD_CACHE = "downloadCache" + const val CANVAS_CACHE = "canvasCache" - val listOfHomeChip = listOf( - R.string.all, - R.string.relax, - R.string.sleep, - R.string.energize, - R.string.sad, - R.string.romance, - R.string.feel_good, - R.string.workout, - R.string.party, - R.string.commute, - R.string.focus - ) + val REMOVED_SONG_DATE_TIME = LocalDateTime.of(2003, Month.AUGUST, 26, 3, 0) + val listOfHomeChip = + listOf( + R.string.all, + R.string.relax, + R.string.sleep, + R.string.energize, + R.string.sad, + R.string.romance, + R.string.feel_good, + R.string.workout, + R.string.party, + R.string.commute, + R.string.focus, + ) } object DownloadState { @@ -184,7 +187,7 @@ object SUPPORTED_LANGUAGE { "日本語", "繁體中文", "Українська", - "עברית" + "עברית", ) val codes: Array = arrayOf( @@ -205,7 +208,7 @@ object SUPPORTED_LANGUAGE { "ja-JP", "zh-Hant-TW", "uk-UA", - "iw-IL" + "iw-IL", ) } @@ -241,139 +244,141 @@ object SPONSOR_BLOCK { R.string.preview, R.string.music_off_topic, R.string.poi_highlight, - R.string.filler, + R.string.filter, ) } object CHART_SUPPORTED_COUNTRY { - val items = arrayOf( - "US", - "ZZ", - "AR", - "AU", - "AT", - "BE", - "BO", - "BR", - "CA", - "CL", - "CO", - "CR", - "CZ", - "DK", - "DO", - "EC", - "EG", - "SV", - "EE", - "FI", - "FR", - "DE", - "GT", - "HN", - "HU", - "IS", - "IN", - "ID", - "IE", - "IL", - "IT", - "JP", - "KE", - "LU", - "MX", - "NL", - "NZ", - "NI", - "NG", - "NO", - "PA", - "PY", - "PE", - "PL", - "PT", - "RO", - "RU", - "SA", - "RS", - "ZA", - "KR", - "ES", - "SE", - "CH", - "TZ", - "TR", - "UG", - "UA", - "AE", - "GB", - "UY", - "ZW" - ) - val itemsData = arrayOf( - "United States", - "Global", - "Argentina", - "Australia", - "Austria", - "Belgium", - "Bolivia", - "Brazil", - "Canada", - "Chile", - "Colombia", - "Costa Rica", - "Czech Republic", - "Denmark", - "Dominican Republic", - "Ecuador", - "Egypt", - "El Salvador", - "Estonia", - "Finland", - "France", - "Germany", - "Guatemala", - "Honduras", - "Hungary", - "Iceland", - "India", - "Indonesia", - "Ireland", - "Israel", - "Italy", - "Japan", - "Kenya", - "Luxembourg", - "Mexico", - "Netherlands", - "New Zealand", - "Nicaragua", - "Nigeria", - "Norway", - "Panama", - "Paraguay", - "Peru", - "Poland", - "Portugal", - "Romania", - "Russia", - "Saudi Arabia", - "Serbia", - "South Africa", - "South Korea", - "Spain", - "Sweden", - "Switzerland", - "Tanzania", - "Turkey", - "Uganda", - "Ukraine", - "United Arab Emirates", - "United Kingdom", - "Uruguay", - "Zimbabwe" - ) + val items = + arrayOf( + "US", + "ZZ", + "AR", + "AU", + "AT", + "BE", + "BO", + "BR", + "CA", + "CL", + "CO", + "CR", + "CZ", + "DK", + "DO", + "EC", + "EG", + "SV", + "EE", + "FI", + "FR", + "DE", + "GT", + "HN", + "HU", + "IS", + "IN", + "ID", + "IE", + "IL", + "IT", + "JP", + "KE", + "LU", + "MX", + "NL", + "NZ", + "NI", + "NG", + "NO", + "PA", + "PY", + "PE", + "PL", + "PT", + "RO", + "RU", + "SA", + "RS", + "ZA", + "KR", + "ES", + "SE", + "CH", + "TZ", + "TR", + "UG", + "UA", + "AE", + "GB", + "UY", + "ZW", + ) + val itemsData = + arrayOf( + "United States", + "Global", + "Argentina", + "Australia", + "Austria", + "Belgium", + "Bolivia", + "Brazil", + "Canada", + "Chile", + "Colombia", + "Costa Rica", + "Czech Republic", + "Denmark", + "Dominican Republic", + "Ecuador", + "Egypt", + "El Salvador", + "Estonia", + "Finland", + "France", + "Germany", + "Guatemala", + "Honduras", + "Hungary", + "Iceland", + "India", + "Indonesia", + "Ireland", + "Israel", + "Italy", + "Japan", + "Kenya", + "Luxembourg", + "Mexico", + "Netherlands", + "New Zealand", + "Nicaragua", + "Nigeria", + "Norway", + "Panama", + "Paraguay", + "Peru", + "Poland", + "Portugal", + "Romania", + "Russia", + "Saudi Arabia", + "Serbia", + "South Africa", + "South Korea", + "Spain", + "Sweden", + "Switzerland", + "Tanzania", + "Turkey", + "Uganda", + "Ukraine", + "United Arab Emirates", + "United Kingdom", + "Uruguay", + "Zimbabwe", + ) } object MEDIA_CUSTOM_COMMAND { diff --git a/app/src/main/java/com/maxrave/simpmusic/data/dataStore/DataStoreManager.kt b/app/src/main/java/com/maxrave/simpmusic/data/dataStore/DataStoreManager.kt index b92f6d5f..e895ca01 100644 --- a/app/src/main/java/com/maxrave/simpmusic/data/dataStore/DataStoreManager.kt +++ b/app/src/main/java/com/maxrave/simpmusic/data/dataStore/DataStoreManager.kt @@ -16,12 +16,9 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map import kotlinx.coroutines.withContext -import javax.inject.Inject import com.maxrave.simpmusic.common.QUALITY as COMMON_QUALITY -class DataStoreManager - @Inject - constructor(private val settingsDataStore: DataStore) { +class DataStoreManager(private val settingsDataStore: DataStore) { val location: Flow = settingsDataStore.data.map { preferences -> preferences[LOCATION] ?: "VN" diff --git a/app/src/main/java/com/maxrave/simpmusic/data/db/LocalDataSource.kt b/app/src/main/java/com/maxrave/simpmusic/data/db/LocalDataSource.kt index 11e75f1a..3536104b 100644 --- a/app/src/main/java/com/maxrave/simpmusic/data/db/LocalDataSource.kt +++ b/app/src/main/java/com/maxrave/simpmusic/data/db/LocalDataSource.kt @@ -18,11 +18,8 @@ import com.maxrave.simpmusic.data.db.entities.SongEntity import com.maxrave.simpmusic.data.db.entities.SongInfoEntity import com.maxrave.simpmusic.viewModel.FilterState import java.time.LocalDateTime -import javax.inject.Inject -class LocalDataSource - @Inject - constructor(private val databaseDao: DatabaseDao) { +class LocalDataSource(private val databaseDao: DatabaseDao) { suspend fun getAllRecentData() = databaseDao.getAllRecentData() suspend fun getAllDownloadedPlaylist() = databaseDao.getAllDownloadedPlaylist() diff --git a/app/src/main/java/com/maxrave/simpmusic/data/repository/MainRepository.kt b/app/src/main/java/com/maxrave/simpmusic/data/repository/MainRepository.kt index b1cb1395..00a3142d 100644 --- a/app/src/main/java/com/maxrave/simpmusic/data/repository/MainRepository.kt +++ b/app/src/main/java/com/maxrave/simpmusic/data/repository/MainRepository.kt @@ -89,7 +89,6 @@ import com.maxrave.simpmusic.extension.toTrack import com.maxrave.simpmusic.service.test.source.MergingMediaSourceFactory import com.maxrave.simpmusic.utils.Resource import com.maxrave.simpmusic.viewModel.FilterState -import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow @@ -100,18 +99,12 @@ import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext import java.time.LocalDateTime -import javax.inject.Inject -import javax.inject.Singleton import kotlin.math.abs -// @ActivityRetainedScoped -@Singleton -class MainRepository -@Inject -constructor( +class MainRepository( private val localDataSource: LocalDataSource, private val dataStoreManager: DataStoreManager, - @ApplicationContext private val context: Context, + private val context: Context, ) { // Database suspend fun getSearchHistory(): Flow> = @@ -119,8 +112,7 @@ constructor( emit(localDataSource.getSearchHistory()) }.flowOn(Dispatchers.IO) - suspend fun insertSearchHistory(searchHistory: SearchHistory) = - withContext(Dispatchers.IO) { localDataSource.insertSearchHistory(searchHistory) } + suspend fun insertSearchHistory(searchHistory: SearchHistory) = withContext(Dispatchers.IO) { localDataSource.insertSearchHistory(searchHistory) } suspend fun deleteSearchHistory() = withContext(Dispatchers.IO) { @@ -183,8 +175,7 @@ constructor( fun getSongAsFlow(id: String) = localDataSource.getSongAsFlow(id) - suspend fun insertSong(songEntity: SongEntity): Flow = - flow { emit(localDataSource.insertSong(songEntity)) }.flowOn(Dispatchers.IO) + suspend fun insertSong(songEntity: SongEntity): Flow = flow { emit(localDataSource.insertSong(songEntity)) }.flowOn(Dispatchers.IO) suspend fun updateListenCount(videoId: String) = withContext(Dispatchers.IO) { @@ -348,8 +339,7 @@ constructor( ) } - suspend fun getAllLocalPlaylists(): Flow> = - flow { emit(localDataSource.getAllLocalPlaylists()) }.flowOn(Dispatchers.IO) + suspend fun getAllLocalPlaylists(): Flow> = flow { emit(localDataSource.getAllLocalPlaylists()) }.flowOn(Dispatchers.IO) suspend fun getLocalPlaylist(id: Long): Flow = flow { emit(localDataSource.getLocalPlaylist(id)) }.flowOn(Dispatchers.IO) @@ -410,8 +400,7 @@ constructor( offset: Int, ) = localDataSource.getRecentSongs(limit, offset) - suspend fun getSavedLyrics(videoId: String): Flow = - flow { emit(localDataSource.getSavedLyrics(videoId)) }.flowOn(Dispatchers.IO) + suspend fun getSavedLyrics(videoId: String): Flow = flow { emit(localDataSource.getSavedLyrics(videoId)) }.flowOn(Dispatchers.IO) suspend fun insertLyrics(lyricsEntity: LyricsEntity) = withContext(Dispatchers.IO) { @@ -423,8 +412,7 @@ constructor( localDataSource.insertNewFormat(newFormat) } - suspend fun getNewFormat(videoId: String): Flow = - flow { emit(localDataSource.getNewFormat(videoId)) }.flowOn(Dispatchers.Main) + suspend fun getNewFormat(videoId: String): Flow = flow { emit(localDataSource.getNewFormat(videoId)) }.flowOn(Dispatchers.Main) suspend fun getFormatFlow(videoId: String) = localDataSource.getNewFormatAsFlow(videoId) @@ -457,8 +445,7 @@ constructor( suspend fun insertSetVideoId(setVideoId: SetVideoIdEntity) = withContext(Dispatchers.IO) { localDataSource.insertSetVideoId(setVideoId) } - suspend fun getSetVideoId(videoId: String): Flow = - flow { emit(localDataSource.getSetVideoId(videoId)) }.flowOn(Dispatchers.IO) + suspend fun getSetVideoId(videoId: String): Flow = flow { emit(localDataSource.getSetVideoId(videoId)) }.flowOn(Dispatchers.IO) suspend fun insertPairSongLocalPlaylist(pairSongLocalPlaylist: PairSongLocalPlaylist) = withContext(Dispatchers.IO) { @@ -474,7 +461,7 @@ constructor( playlistId: Long, offset: Int, filterState: FilterState, - totalCount: Int + totalCount: Int, ): Flow?> = flow { emit(localDataSource.getPlaylistPairSongByOffset(playlistId, offset, filterState, totalCount)) @@ -570,166 +557,237 @@ constructor( suspend fun getAccountInfo() = flow { - YouTube.accountInfo().onSuccess { accountInfo -> - emit(accountInfo) - }.onFailure { - it.printStackTrace() - emit(null) - } + YouTube + .accountInfo() + .onSuccess { accountInfo -> + emit(accountInfo) + }.onFailure { + it.printStackTrace() + emit(null) + } }.flowOn(Dispatchers.IO) suspend fun getHomeData(params: String? = null): Flow>> = flow { runCatching { val limit = dataStoreManager.homeLimit.first() - YouTube.customQuery(browseId = "FEmusic_home", params = params).onSuccess { result -> - val list: ArrayList = arrayListOf() - if (result.contents?.singleColumnBrowseResultsRenderer?.tabs?.get( - 0, - )?.tabRenderer?.content?.sectionListRenderer?.contents?.get( - 0, - )?.musicCarouselShelfRenderer?.header?.musicCarouselShelfBasicHeaderRenderer?.strapline?.runs?.get( - 0, - )?.text != null - ) { - val accountName = - result.contents?.singleColumnBrowseResultsRenderer?.tabs?.get( - 0, - )?.tabRenderer?.content?.sectionListRenderer?.contents?.get( - 0, - )?.musicCarouselShelfRenderer?.header?.musicCarouselShelfBasicHeaderRenderer?.strapline?.runs?.get( - 0, - )?.text ?: "" - val accountThumbUrl = - result.contents?.singleColumnBrowseResultsRenderer?.tabs?.get( - 0, - )?.tabRenderer?.content?.sectionListRenderer?.contents?.get( - 0, - )?.musicCarouselShelfRenderer?.header?.musicCarouselShelfBasicHeaderRenderer - ?.thumbnail?.musicThumbnailRenderer?.thumbnail?.thumbnails?.get( + YouTube + .customQuery(browseId = "FEmusic_home", params = params) + .onSuccess { result -> + val list: ArrayList = arrayListOf() + if (result.contents + ?.singleColumnBrowseResultsRenderer + ?.tabs + ?.get( 0, - )?.url?.replace("s88", "s352") ?: "" - if (accountName != "" && accountThumbUrl != "") { - dataStoreManager.putString("AccountName", accountName) - dataStoreManager.putString("AccountThumbUrl", accountThumbUrl) - } - } - var continueParam = - result.contents?.singleColumnBrowseResultsRenderer?.tabs?.get( - 0, - )?.tabRenderer?.content?.sectionListRenderer?.continuations?.get( - 0, - )?.nextContinuationData?.continuation - val data = - result.contents?.singleColumnBrowseResultsRenderer?.tabs?.get( - 0, - )?.tabRenderer?.content?.sectionListRenderer?.contents - list.addAll(parseMixedContent(data, context)) - var count = 0 - while (count < limit && continueParam != null) { - YouTube.customQuery(browseId = "", continuation = continueParam) - .onSuccess { response -> - continueParam = - response.continuationContents?.sectionListContinuation?.continuations?.get( + )?.tabRenderer + ?.content + ?.sectionListRenderer + ?.contents + ?.get( + 0, + )?.musicCarouselShelfRenderer + ?.header + ?.musicCarouselShelfBasicHeaderRenderer + ?.strapline + ?.runs + ?.get( + 0, + )?.text != null + ) { + val accountName = + result.contents + ?.singleColumnBrowseResultsRenderer + ?.tabs + ?.get( 0, - )?.nextContinuationData?.continuation - Log.d("Repository", "continueParam: $continueParam") - val dataContinue = - response.continuationContents?.sectionListContinuation?.contents - list.addAll(parseMixedContent(dataContinue, context)) - count++ - Log.d("Repository", "count: $count") - }.onFailure { - Log.e("Repository", "Error: ${it.message}") - count++ + )?.tabRenderer + ?.content + ?.sectionListRenderer + ?.contents + ?.get( + 0, + )?.musicCarouselShelfRenderer + ?.header + ?.musicCarouselShelfBasicHeaderRenderer + ?.strapline + ?.runs + ?.get( + 0, + )?.text ?: "" + val accountThumbUrl = + result.contents + ?.singleColumnBrowseResultsRenderer + ?.tabs + ?.get( + 0, + )?.tabRenderer + ?.content + ?.sectionListRenderer + ?.contents + ?.get( + 0, + )?.musicCarouselShelfRenderer + ?.header + ?.musicCarouselShelfBasicHeaderRenderer + ?.thumbnail + ?.musicThumbnailRenderer + ?.thumbnail + ?.thumbnails + ?.get( + 0, + )?.url + ?.replace("s88", "s352") ?: "" + if (accountName != "" && accountThumbUrl != "") { + dataStoreManager.putString("AccountName", accountName) + dataStoreManager.putString("AccountThumbUrl", accountThumbUrl) } + } + var continueParam = + result.contents + ?.singleColumnBrowseResultsRenderer + ?.tabs + ?.get( + 0, + )?.tabRenderer + ?.content + ?.sectionListRenderer + ?.continuations + ?.get( + 0, + )?.nextContinuationData + ?.continuation + val data = + result.contents + ?.singleColumnBrowseResultsRenderer + ?.tabs + ?.get( + 0, + )?.tabRenderer + ?.content + ?.sectionListRenderer + ?.contents + list.addAll(parseMixedContent(data, context)) + var count = 0 + while (count < limit && continueParam != null) { + YouTube + .customQuery(browseId = "", continuation = continueParam) + .onSuccess { response -> + continueParam = + response.continuationContents + ?.sectionListContinuation + ?.continuations + ?.get( + 0, + )?.nextContinuationData + ?.continuation + Log.d("Repository", "continueParam: $continueParam") + val dataContinue = + response.continuationContents?.sectionListContinuation?.contents + list.addAll(parseMixedContent(dataContinue, context)) + count++ + Log.d("Repository", "count: $count") + }.onFailure { + Log.e("Repository", "Error: ${it.message}") + count++ + } + } + Log.d("Repository", "List size: ${list.size}") + emit(Resource.Success>(list)) + }.onFailure { error -> + emit(Resource.Error>(error.message.toString())) } - Log.d("Repository", "List size: ${list.size}") - emit(Resource.Success>(list)) - }.onFailure { error -> - emit(Resource.Error>(error.message.toString())) - } } }.flowOn(Dispatchers.IO) suspend fun getNewRelease(): Flow>> = flow { - YouTube.newRelease().onSuccess { result -> - emit(Resource.Success>(parseNewRelease(result, context))) - }.onFailure { error -> - emit(Resource.Error>(error.message.toString())) - } + YouTube + .newRelease() + .onSuccess { result -> + emit(Resource.Success>(parseNewRelease(result, context))) + }.onFailure { error -> + emit(Resource.Error>(error.message.toString())) + } }.flowOn(Dispatchers.IO) suspend fun getChartData(countryCode: String = "KR"): Flow> = flow { runCatching { - YouTube.customQuery("FEmusic_charts", country = countryCode).onSuccess { result -> - val data = - result.contents?.singleColumnBrowseResultsRenderer?.tabs?.get( - 0, - )?.tabRenderer?.content?.sectionListRenderer - val chart = parseChart(data) - if (chart != null) { - emit(Resource.Success(chart)) - } else { - emit(Resource.Error("Error")) + YouTube + .customQuery("FEmusic_charts", country = countryCode) + .onSuccess { result -> + val data = + result.contents + ?.singleColumnBrowseResultsRenderer + ?.tabs + ?.get( + 0, + )?.tabRenderer + ?.content + ?.sectionListRenderer + val chart = parseChart(data) + if (chart != null) { + emit(Resource.Success(chart)) + } else { + emit(Resource.Error("Error")) + } + }.onFailure { error -> + emit(Resource.Error(error.message.toString())) } - }.onFailure { error -> - emit(Resource.Error(error.message.toString())) - } } }.flowOn(Dispatchers.IO) suspend fun getMoodAndMomentsData(): Flow> = flow { runCatching { - YouTube.moodAndGenres().onSuccess { result -> - val listMoodMoments: ArrayList = arrayListOf() - val listGenre: ArrayList = arrayListOf() - result[0].let { moodsmoment -> - for (item in moodsmoment.items) { - listMoodMoments.add( - MoodsMoment( - params = item.endpoint.params ?: "", - title = item.title, - ), - ) + YouTube + .moodAndGenres() + .onSuccess { result -> + val listMoodMoments: ArrayList = arrayListOf() + val listGenre: ArrayList = arrayListOf() + result[0].let { moodsmoment -> + for (item in moodsmoment.items) { + listMoodMoments.add( + MoodsMoment( + params = item.endpoint.params ?: "", + title = item.title, + ), + ) + } } - } - result[1].let { genres -> - for (item in genres.items) { - listGenre.add( - Genre( - params = item.endpoint.params ?: "", - title = item.title, - ), - ) + result[1].let { genres -> + for (item in genres.items) { + listGenre.add( + Genre( + params = item.endpoint.params ?: "", + title = item.title, + ), + ) + } } + emit(Resource.Success(Mood(listGenre, listMoodMoments))) + }.onFailure { e -> + emit(Resource.Error(e.message.toString())) } - emit(Resource.Success(Mood(listGenre, listMoodMoments))) - }.onFailure { e -> - emit(Resource.Error(e.message.toString())) - } } }.flowOn(Dispatchers.IO) suspend fun getMoodData(params: String): Flow> = flow { runCatching { - YouTube.customQuery( - browseId = "FEmusic_moods_and_genres_category", - params = params, - ) - .onSuccess { result -> + YouTube + .customQuery( + browseId = "FEmusic_moods_and_genres_category", + params = params, + ).onSuccess { result -> val data = parseMoodsMomentObject(result) if (data != null) { emit(Resource.Success(data)) } else { emit(Resource.Error("Error")) } - } - .onFailure { e -> + }.onFailure { e -> emit(Resource.Error(e.message.toString())) } } @@ -738,19 +796,18 @@ constructor( suspend fun getGenreData(params: String): Flow> = flow { kotlin.runCatching { - YouTube.customQuery( - browseId = "FEmusic_moods_and_genres_category", - params = params, - ) - .onSuccess { result -> + YouTube + .customQuery( + browseId = "FEmusic_moods_and_genres_category", + params = params, + ).onSuccess { result -> val data = parseGenreObject(result) if (data != null) { emit(Resource.Success(data)) } else { emit(Resource.Error("Error")) } - } - .onFailure { e -> + }.onFailure { e -> emit(Resource.Error(e.message.toString())) } } @@ -764,15 +821,15 @@ constructor( runCatching { var newContinuation: String? = null newContinuation = null - YouTube.next( - if (playlistId.startsWith("RRDAMVM")) { - WatchEndpoint(videoId = playlistId.removePrefix("RRDAMVM")) - } else { - WatchEndpoint(playlistId = playlistId) - }, - continuation = continuation, - ) - .onSuccess { next -> + YouTube + .next( + if (playlistId.startsWith("RRDAMVM")) { + WatchEndpoint(videoId = playlistId.removePrefix("RRDAMVM")) + } else { + WatchEndpoint(playlistId = playlistId) + }, + continuation = continuation, + ).onSuccess { next -> val data: ArrayList = arrayListOf() data.addAll(next.items) newContinuation = next.continuation @@ -791,66 +848,68 @@ constructor( ): Flow>> = flow { runCatching { - YouTube.next(endpoint = WatchEndpoint(playlistId = radioId)).onSuccess { next -> - Log.w("Radio", "Title: ${next.title}") - val data: ArrayList = arrayListOf() - data.addAll(next.items) - var continuation = next.continuation - Log.w("Radio", "data: ${data.size}") - var count = 0 - while (continuation != null && count < 3) { - YouTube.next( - endpoint = WatchEndpoint(playlistId = radioId), - continuation = continuation, - ).onSuccess { nextContinue -> - data.addAll(nextContinue.items) - continuation = nextContinue.continuation - if (data.size >= 50) { - count = 3 - } - Log.w("Radio", "data: ${data.size}") - count++ - }.onFailure { - count = 3 + YouTube + .next(endpoint = WatchEndpoint(playlistId = radioId)) + .onSuccess { next -> + Log.w("Radio", "Title: ${next.title}") + val data: ArrayList = arrayListOf() + data.addAll(next.items) + var continuation = next.continuation + Log.w("Radio", "data: ${data.size}") + var count = 0 + while (continuation != null && count < 3) { + YouTube + .next( + endpoint = WatchEndpoint(playlistId = radioId), + continuation = continuation, + ).onSuccess { nextContinue -> + data.addAll(nextContinue.items) + continuation = nextContinue.continuation + if (data.size >= 50) { + count = 3 + } + Log.w("Radio", "data: ${data.size}") + count++ + }.onFailure { + count = 3 + } } - } - val listTrackResult = data.toListTrack() - if (originalTrack != null) { - listTrackResult.add(0, originalTrack.toTrack()) - } - Log.w("Repository", "data: ${data.size}") - val playlistBrowse = - PlaylistBrowse( - author = Author(id = "", name = "YouTube Music"), - description = - context.getString( - R.string.auto_created_by_youtube_music, - ), - duration = "", - durationSeconds = 0, - id = radioId, - privacy = "PRIVATE", - thumbnails = - listOf( - Thumbnail( - 544, - originalTrack?.thumbnails ?: artist?.thumbnails ?: "", - 544, - ), - ), - title = "${originalTrack?.title ?: artist?.name} ${ - context.getString( - R.string.radio, - ) - }", - trackCount = listTrackResult.size, - tracks = listTrackResult, - year = LocalDateTime.now().year.toString(), - ) - Log.w("Repository", "playlistBrowse: $playlistBrowse") - emit(Resource.Success>(Pair(playlistBrowse, continuation))) - } - .onFailure { exception -> + val listTrackResult = data.toListTrack() + if (originalTrack != null) { + listTrackResult.add(0, originalTrack.toTrack()) + } + Log.w("Repository", "data: ${data.size}") + val playlistBrowse = + PlaylistBrowse( + author = Author(id = "", name = "YouTube Music"), + description = + context.getString( + R.string.auto_created_by_youtube_music, + ), + duration = "", + durationSeconds = 0, + id = radioId, + privacy = "PRIVATE", + thumbnails = + listOf( + Thumbnail( + 544, + originalTrack?.thumbnails ?: artist?.thumbnails ?: "", + 544, + ), + ), + title = "${originalTrack?.title ?: artist?.name} ${ + context.getString( + R.string.radio, + ) + }", + trackCount = listTrackResult.size, + tracks = listTrackResult, + year = LocalDateTime.now().year.toString(), + ) + Log.w("Repository", "playlistBrowse: $playlistBrowse") + emit(Resource.Success>(Pair(playlistBrowse, continuation))) + }.onFailure { exception -> exception.printStackTrace() emit(Resource.Error>(exception.message.toString())) } @@ -860,7 +919,8 @@ constructor( suspend fun reloadSuggestionPlaylist(reloadParams: String): Flow?>?> = flow { runCatching { - YouTube.customQuery(browseId = "", continuation = reloadParams, setLogin = true) + YouTube + .customQuery(browseId = "", continuation = reloadParams, setLogin = true) .onSuccess { values -> val data = values.continuationContents?.musicShelfContinuation?.contents val dataResult: @@ -870,9 +930,13 @@ constructor( dataResult.addAll(data) } val reloadParamsNew = - values.continuationContents?.musicShelfContinuation?.continuations?.get( - 0, - )?.reloadContinuationData?.continuation + values.continuationContents + ?.musicShelfContinuation + ?.continuations + ?.get( + 0, + )?.reloadContinuationData + ?.continuation if (dataResult.isNotEmpty()) { val listTrack: ArrayList = arrayListOf() dataResult.forEach { @@ -881,7 +945,7 @@ constructor( SearchPage.toYTItem( it.musicResponsiveListItemRenderer, ) as SongItem - ).toTrack(), + ).toTrack(), ) } emit(Pair(reloadParamsNew, listTrack)) @@ -904,77 +968,116 @@ constructor( } else { id += ytPlaylistId } - YouTube.customQuery(browseId = id, setLogin = true).onSuccess { result -> - println(result) - var continueParam = - result.contents?.singleColumnBrowseResultsRenderer?.tabs?.get( - 0, - )?.tabRenderer?.content?.sectionListRenderer?.contents?.get( - 0, - )?.musicPlaylistShelfRenderer?.continuations?.get( - 0, - )?.nextContinuationData?.continuation - ?: result.contents?.singleColumnBrowseResultsRenderer?.tabs?.get( - 0, - )?.tabRenderer?.content?.sectionListRenderer?.continuations?.get( - 0, - )?.nextContinuationData?.continuation - ?: result.contents?.twoColumnBrowseResultsRenderer?.secondaryContents - ?.sectionListRenderer?.continuations?.firstOrNull() - ?.nextContinuationData?.continuation - val dataResult: ArrayList = arrayListOf() - var reloadParams: String? = null - println("continueParam: $continueParam") - while (continueParam != null) { - YouTube.customQuery( - browseId = "", - continuation = continueParam, - setLogin = true, - ).onSuccess { values -> - val data = - values.continuationContents?.sectionListContinuation?.contents?.get( - 0, - )?.musicShelfRenderer?.contents - println("data: $data") - if (!data.isNullOrEmpty()) { - dataResult.addAll(data) - } - reloadParams = - values.continuationContents?.sectionListContinuation?.contents?.get( + YouTube + .customQuery(browseId = id, setLogin = true) + .onSuccess { result -> + println(result) + var continueParam = + result.contents + ?.singleColumnBrowseResultsRenderer + ?.tabs + ?.get( 0, - )?.musicShelfRenderer?.continuations?.get( + )?.tabRenderer + ?.content + ?.sectionListRenderer + ?.contents + ?.get( 0, - )?.reloadContinuationData?.continuation - continueParam = - values.continuationContents?.musicPlaylistShelfContinuation?.continuations?.get( + )?.musicPlaylistShelfRenderer + ?.continuations + ?.get( 0, - )?.nextContinuationData?.continuation - println("reloadParams: $reloadParams") - println("continueParam: $continueParam") - }.onFailure { - Log.e("Repository", "Error: ${it.message}") - continueParam = null + )?.nextContinuationData + ?.continuation + ?: result.contents + ?.singleColumnBrowseResultsRenderer + ?.tabs + ?.get( + 0, + )?.tabRenderer + ?.content + ?.sectionListRenderer + ?.continuations + ?.get( + 0, + )?.nextContinuationData + ?.continuation + ?: result.contents + ?.twoColumnBrowseResultsRenderer + ?.secondaryContents + ?.sectionListRenderer + ?.continuations + ?.firstOrNull() + ?.nextContinuationData + ?.continuation + val dataResult: ArrayList = arrayListOf() + var reloadParams: String? = null + println("continueParam: $continueParam") + while (continueParam != null) { + YouTube + .customQuery( + browseId = "", + continuation = continueParam, + setLogin = true, + ).onSuccess { values -> + val data = + values.continuationContents + ?.sectionListContinuation + ?.contents + ?.get( + 0, + )?.musicShelfRenderer + ?.contents + println("data: $data") + if (!data.isNullOrEmpty()) { + dataResult.addAll(data) + } + reloadParams = + values.continuationContents + ?.sectionListContinuation + ?.contents + ?.get( + 0, + )?.musicShelfRenderer + ?.continuations + ?.get( + 0, + )?.reloadContinuationData + ?.continuation + continueParam = + values.continuationContents + ?.musicPlaylistShelfContinuation + ?.continuations + ?.get( + 0, + )?.nextContinuationData + ?.continuation + println("reloadParams: $reloadParams") + println("continueParam: $continueParam") + }.onFailure { + Log.e("Repository", "Error: ${it.message}") + continueParam = null + } } - } - println("dataResult: ${dataResult.size}") - if (dataResult.isNotEmpty()) { - val listTrack: ArrayList = arrayListOf() - dataResult.forEach { - listTrack.add( - ( - PlaylistPage.fromMusicResponsiveListItemRenderer( - it.musicResponsiveListItemRenderer, - ) as SongItem + println("dataResult: ${dataResult.size}") + if (dataResult.isNotEmpty()) { + val listTrack: ArrayList = arrayListOf() + dataResult.forEach { + listTrack.add( + ( + PlaylistPage.fromMusicResponsiveListItemRenderer( + it.musicResponsiveListItemRenderer, + ) as SongItem ).toTrack(), - ) + ) + } + println("listTrack: $listTrack") + emit(Pair(reloadParams, listTrack)) + } else { + emit(null) } - println("listTrack: $listTrack") - emit(Pair(reloadParams, listTrack)) - } else { - emit(null) - } - } - .onFailure { exception -> + }.onFailure { exception -> exception.printStackTrace() emit(null) } @@ -984,179 +1087,315 @@ constructor( suspend fun getPodcastData(podcastId: String): Flow> = flow { runCatching { - YouTube.customQuery(browseId = podcastId).onSuccess { result -> - val listEpisode = arrayListOf() - val thumbnail = - result.background?.musicThumbnailRenderer?.thumbnail?.thumbnails?.toListThumbnail() - val title = - result.contents?.twoColumnBrowseResultsRenderer?.tabs?.firstOrNull() - ?.tabRenderer?.content?.sectionListRenderer?.contents?.firstOrNull() - ?.musicResponsiveHeaderRenderer?.title?.runs?.firstOrNull()?.text - val author = - result.contents?.twoColumnBrowseResultsRenderer?.tabs?.firstOrNull() - ?.tabRenderer?.content?.sectionListRenderer?.contents?.firstOrNull() - ?.musicResponsiveHeaderRenderer?.let { - Artist( - id = it.straplineTextOne?.runs?.firstOrNull()?.navigationEndpoint?.browseEndpoint?.browseId, - name = it.straplineTextOne?.runs?.firstOrNull()?.text ?: "", - ) - } - val authorThumbnail = - result.contents?.twoColumnBrowseResultsRenderer?.tabs?.firstOrNull() - ?.tabRenderer?.content?.sectionListRenderer?.contents?.firstOrNull() - ?.musicResponsiveHeaderRenderer?.let { - it.straplineThumbnail?.musicThumbnailRenderer?.thumbnail?.thumbnails?.lastOrNull()?.url - } - val description = - result.contents?.twoColumnBrowseResultsRenderer?.tabs?.firstOrNull()?.tabRenderer?.content?.sectionListRenderer - ?.contents?.firstOrNull()?.musicResponsiveHeaderRenderer - ?.description?.musicDescriptionShelfRenderer?.description?.runs?.map { - it.text - }?.joinToString("") - val data = - result.contents?.twoColumnBrowseResultsRenderer?.secondaryContents?.sectionListRenderer?.contents?.firstOrNull() - ?.musicShelfRenderer?.contents - parsePodcastData(data, author).let { - listEpisode.addAll(it) - } - var continueParam = - result.contents?.twoColumnBrowseResultsRenderer?.secondaryContents - ?.sectionListRenderer?.contents?.firstOrNull() - ?.musicShelfRenderer?.continuations?.firstOrNull()?.nextContinuationData?.continuation - while (continueParam != null) { - YouTube.customQuery(continuation = continueParam, browseId = "") - .onSuccess { continueData -> - parsePodcastContinueData( - continueData.continuationContents?.musicShelfContinuation?.contents, - author, - ).let { - listEpisode.addAll(it) + YouTube + .customQuery(browseId = podcastId) + .onSuccess { result -> + val listEpisode = arrayListOf() + val thumbnail = + result.background + ?.musicThumbnailRenderer + ?.thumbnail + ?.thumbnails + ?.toListThumbnail() + val title = + result.contents + ?.twoColumnBrowseResultsRenderer + ?.tabs + ?.firstOrNull() + ?.tabRenderer + ?.content + ?.sectionListRenderer + ?.contents + ?.firstOrNull() + ?.musicResponsiveHeaderRenderer + ?.title + ?.runs + ?.firstOrNull() + ?.text + val author = + result.contents + ?.twoColumnBrowseResultsRenderer + ?.tabs + ?.firstOrNull() + ?.tabRenderer + ?.content + ?.sectionListRenderer + ?.contents + ?.firstOrNull() + ?.musicResponsiveHeaderRenderer + ?.let { + Artist( + id = + it.straplineTextOne + ?.runs + ?.firstOrNull() + ?.navigationEndpoint + ?.browseEndpoint + ?.browseId, + name = + it.straplineTextOne + ?.runs + ?.firstOrNull() + ?.text ?: "", + ) } - continueParam = - continueData.continuationContents?.musicShelfContinuation?.continuations?.firstOrNull() - ?.nextContinuationData?.continuation - } - .onFailure { - it.printStackTrace() - continueParam = null - } - } - if (author != null) { - emit( - Resource.Success( - PodcastBrowse( - title = title ?: "", - author = author, - authorThumbnail = authorThumbnail, - thumbnail = thumbnail ?: emptyList(), - description = description, - listEpisode = listEpisode, + val authorThumbnail = + result.contents + ?.twoColumnBrowseResultsRenderer + ?.tabs + ?.firstOrNull() + ?.tabRenderer + ?.content + ?.sectionListRenderer + ?.contents + ?.firstOrNull() + ?.musicResponsiveHeaderRenderer + ?.let { + it.straplineThumbnail + ?.musicThumbnailRenderer + ?.thumbnail + ?.thumbnails + ?.lastOrNull() + ?.url + } + val description = + result.contents + ?.twoColumnBrowseResultsRenderer + ?.tabs + ?.firstOrNull() + ?.tabRenderer + ?.content + ?.sectionListRenderer + ?.contents + ?.firstOrNull() + ?.musicResponsiveHeaderRenderer + ?.description + ?.musicDescriptionShelfRenderer + ?.description + ?.runs + ?.map { + it.text + }?.joinToString("") + val data = + result.contents + ?.twoColumnBrowseResultsRenderer + ?.secondaryContents + ?.sectionListRenderer + ?.contents + ?.firstOrNull() + ?.musicShelfRenderer + ?.contents + parsePodcastData(data, author).let { + listEpisode.addAll(it) + } + var continueParam = + result.contents + ?.twoColumnBrowseResultsRenderer + ?.secondaryContents + ?.sectionListRenderer + ?.contents + ?.firstOrNull() + ?.musicShelfRenderer + ?.continuations + ?.firstOrNull() + ?.nextContinuationData + ?.continuation + while (continueParam != null) { + YouTube + .customQuery(continuation = continueParam, browseId = "") + .onSuccess { continueData -> + parsePodcastContinueData( + continueData.continuationContents?.musicShelfContinuation?.contents, + author, + ).let { + listEpisode.addAll(it) + } + continueParam = + continueData.continuationContents + ?.musicShelfContinuation + ?.continuations + ?.firstOrNull() + ?.nextContinuationData + ?.continuation + }.onFailure { + it.printStackTrace() + continueParam = null + } + } + if (author != null) { + emit( + Resource.Success( + PodcastBrowse( + title = title ?: "", + author = author, + authorThumbnail = authorThumbnail, + thumbnail = thumbnail ?: emptyList(), + description = description, + listEpisode = listEpisode, + ), ), - ), - ) - } else { - emit(Resource.Error("Error")) + ) + } else { + emit(Resource.Error("Error")) + } + }.onFailure { error -> + Log.w("Podcast", "Error: ${error.message}") + emit(Resource.Error(error.message.toString())) } - }.onFailure { error -> - Log.w("Podcast", "Error: ${error.message}") - emit(Resource.Error(error.message.toString())) - } } } - suspend fun getRDATRadioData(radioId: String): Flow>> = flow>> { - runCatching { - val id = if (radioId.startsWith("VL")) { - radioId - } else { - "VL$radioId" - } - YouTube.customQuery(browseId = id, setLogin = true).onSuccess { result -> - val listContent: ArrayList = arrayListOf() - val data: List? = - result.contents?.singleColumnBrowseResultsRenderer?.tabs?.get( - 0, - )?.tabRenderer?.content?.sectionListRenderer?.contents?.get( - 0, - )?.musicPlaylistShelfRenderer?.contents - ?: result.contents?.twoColumnBrowseResultsRenderer - ?.secondaryContents?.sectionListRenderer?.contents?.get(0) - ?.musicPlaylistShelfRenderer?.contents - if (data != null) { - Log.d("Data", "data: $data") - Log.d("Data", "data size: ${data.size}") - listContent.addAll(data) - } - val header = - result.header?.musicDetailHeaderRenderer - ?: result.header?.musicEditablePlaylistDetailHeaderRenderer - ?: result.contents?.twoColumnBrowseResultsRenderer?.tabs?.get(0) - ?.tabRenderer?.content?.sectionListRenderer?.contents?.get(0) - ?.musicResponsiveHeaderRenderer - ?: result.contents?.twoColumnBrowseResultsRenderer?.tabs?.get(0) - ?.tabRenderer?.content?.sectionListRenderer?.contents?.get(0) - ?.musicEditablePlaylistDetailHeaderRenderer?.header?.musicResponsiveHeaderRenderer - Log.d("Header", "header: $header") - var continueParam = - result.contents?.singleColumnBrowseResultsRenderer?.tabs?.get( - 0, - )?.tabRenderer?.content?.sectionListRenderer?.contents?.get( - 0, - )?.musicPlaylistShelfRenderer?.continuations?.get( - 0, - )?.nextContinuationData?.continuation - ?: result.contents?.twoColumnBrowseResultsRenderer?.secondaryContents?.sectionListRenderer?.contents?.firstOrNull() - ?.musicPlaylistShelfRenderer?.continuations?.firstOrNull()?.nextContinuationData?.continuation - var count = 0 - Log.d("Repository", "playlist data: ${listContent.size}") - Log.d("Repository", "continueParam: $continueParam") + suspend fun getRDATRadioData(radioId: String): Flow>> = + flow>> { + runCatching { + val id = + if (radioId.startsWith("VL")) { + radioId + } else { + "VL$radioId" + } + YouTube + .customQuery(browseId = id, setLogin = true) + .onSuccess { result -> + val listContent: ArrayList = arrayListOf() + val data: List? = + result.contents + ?.singleColumnBrowseResultsRenderer + ?.tabs + ?.get( + 0, + )?.tabRenderer + ?.content + ?.sectionListRenderer + ?.contents + ?.get( + 0, + )?.musicPlaylistShelfRenderer + ?.contents + ?: result.contents + ?.twoColumnBrowseResultsRenderer + ?.secondaryContents + ?.sectionListRenderer + ?.contents + ?.get(0) + ?.musicPlaylistShelfRenderer + ?.contents + if (data != null) { + Log.d("Data", "data: $data") + Log.d("Data", "data size: ${data.size}") + listContent.addAll(data) + } + val header = + result.header?.musicDetailHeaderRenderer + ?: result.header?.musicEditablePlaylistDetailHeaderRenderer + ?: result.contents + ?.twoColumnBrowseResultsRenderer + ?.tabs + ?.get(0) + ?.tabRenderer + ?.content + ?.sectionListRenderer + ?.contents + ?.get(0) + ?.musicResponsiveHeaderRenderer + ?: result.contents + ?.twoColumnBrowseResultsRenderer + ?.tabs + ?.get(0) + ?.tabRenderer + ?.content + ?.sectionListRenderer + ?.contents + ?.get(0) + ?.musicEditablePlaylistDetailHeaderRenderer + ?.header + ?.musicResponsiveHeaderRenderer + Log.d("Header", "header: $header") + var continueParam = + result.contents + ?.singleColumnBrowseResultsRenderer + ?.tabs + ?.get( + 0, + )?.tabRenderer + ?.content + ?.sectionListRenderer + ?.contents + ?.get( + 0, + )?.musicPlaylistShelfRenderer + ?.continuations + ?.get( + 0, + )?.nextContinuationData + ?.continuation + ?: result.contents + ?.twoColumnBrowseResultsRenderer + ?.secondaryContents + ?.sectionListRenderer + ?.contents + ?.firstOrNull() + ?.musicPlaylistShelfRenderer + ?.continuations + ?.firstOrNull() + ?.nextContinuationData + ?.continuation + var count = 0 + Log.d("Repository", "playlist data: ${listContent.size}") + Log.d("Repository", "continueParam: $continueParam") // else { // var listTrack = playlistBrowse.tracks.toMutableList() - while (count < 1 && continueParam != null) { - YouTube.customQuery( - browseId = "", - continuation = continueParam, - setLogin = true, - ).onSuccess { values -> - Log.d("Continue", "continue: $continueParam") - val dataMore: List? = - values.continuationContents?.musicPlaylistShelfContinuation?.contents - if (dataMore != null) { - listContent.addAll(dataMore) + while (count < 1 && continueParam != null) { + YouTube + .customQuery( + browseId = "", + continuation = continueParam, + setLogin = true, + ).onSuccess { values -> + Log.d("Continue", "continue: $continueParam") + val dataMore: List? = + values.continuationContents?.musicPlaylistShelfContinuation?.contents + if (dataMore != null) { + listContent.addAll(dataMore) + } + continueParam = + values.continuationContents + ?.musicPlaylistShelfContinuation + ?.continuations + ?.get( + 0, + )?.nextContinuationData + ?.continuation + count++ + }.onFailure { + Log.e("Continue", "Error: ${it.message}") + count = 3 + } } - continueParam = - values.continuationContents?.musicPlaylistShelfContinuation?.continuations?.get( - 0, - )?.nextContinuationData?.continuation - count++ - }.onFailure { - Log.e("Continue", "Error: ${it.message}") - count = 3 - } - } - Log.d("Repository", "playlist final data: ${listContent.size}") - val finalContinueParam = continueParam - if (finalContinueParam != null) { - parsePlaylistData(header, listContent, radioId, context)?.let { playlist -> - emit( - Resource.Success( - Pair( - playlist.copy( - author = Author("", "YouTube Music"), - ), finalContinueParam + Log.d("Repository", "playlist final data: ${listContent.size}") + val finalContinueParam = continueParam + if (finalContinueParam != null) { + parsePlaylistData(header, listContent, radioId, context)?.let { playlist -> + emit( + Resource.Success( + Pair( + playlist.copy( + author = Author("", "YouTube Music"), + ), + finalContinueParam, + ), + ), ) - ) - ) - } ?: emit(Resource.Error("Can't parse data")) - } else { - emit(Resource.Error("Continue param is null")) - } - }.onFailure { e -> - Log.e("Playlist Data", e.message ?: "Error") - emit(Resource.Error(e.message.toString())) + } ?: emit(Resource.Error("Can't parse data")) + } else { + emit(Resource.Error("Continue param is null")) + } + }.onFailure { e -> + Log.e("Playlist Data", e.message ?: "Error") + emit(Resource.Error(e.message.toString())) + } } - } - }.flowOn(Dispatchers.IO) + }.flowOn(Dispatchers.IO) suspend fun getPlaylistData(playlistId: String): Flow> = flow { @@ -1169,90 +1408,147 @@ constructor( playlistId } Log.d("Repository", "playlist id: $id") - YouTube.customQuery(browseId = id, setLogin = true).onSuccess { result -> - val listContent: ArrayList = arrayListOf() - val data: List? = - result.contents?.singleColumnBrowseResultsRenderer?.tabs?.get( - 0, - )?.tabRenderer?.content?.sectionListRenderer?.contents?.get( - 0, - )?.musicPlaylistShelfRenderer?.contents - ?: result.contents?.twoColumnBrowseResultsRenderer - ?.secondaryContents?.sectionListRenderer?.contents?.get(0) - ?.musicPlaylistShelfRenderer?.contents - if (data != null) { - Log.d("Data", "data: $data") - Log.d("Data", "data size: ${data.size}") - listContent.addAll(data) - } - val header = - result.header?.musicDetailHeaderRenderer - ?: result.header?.musicEditablePlaylistDetailHeaderRenderer - ?: result.contents?.twoColumnBrowseResultsRenderer?.tabs?.get(0) - ?.tabRenderer?.content?.sectionListRenderer?.contents?.get(0) - ?.musicResponsiveHeaderRenderer - ?: result.contents?.twoColumnBrowseResultsRenderer?.tabs?.get(0) - ?.tabRenderer?.content?.sectionListRenderer?.contents?.get(0) - ?.musicEditablePlaylistDetailHeaderRenderer?.header?.musicResponsiveHeaderRenderer - Log.d("Header", "header: $header") - var continueParam = - result.contents?.singleColumnBrowseResultsRenderer?.tabs?.get( - 0, - )?.tabRenderer?.content?.sectionListRenderer?.contents?.get( - 0, - )?.musicPlaylistShelfRenderer?.continuations?.get( - 0, - )?.nextContinuationData?.continuation - ?: result.contents?.twoColumnBrowseResultsRenderer?.secondaryContents?.sectionListRenderer?.contents?.firstOrNull() - ?.musicPlaylistShelfRenderer?.continuations?.firstOrNull()?.nextContinuationData?.continuation - var count = 0 - Log.d("Repository", "playlist data: ${listContent.size}") - Log.d("Repository", "continueParam: $continueParam") + YouTube + .customQuery(browseId = id, setLogin = true) + .onSuccess { result -> + val listContent: ArrayList = arrayListOf() + val data: List? = + result.contents + ?.singleColumnBrowseResultsRenderer + ?.tabs + ?.get( + 0, + )?.tabRenderer + ?.content + ?.sectionListRenderer + ?.contents + ?.get( + 0, + )?.musicPlaylistShelfRenderer + ?.contents + ?: result.contents + ?.twoColumnBrowseResultsRenderer + ?.secondaryContents + ?.sectionListRenderer + ?.contents + ?.get(0) + ?.musicPlaylistShelfRenderer + ?.contents + if (data != null) { + Log.d("Data", "data: $data") + Log.d("Data", "data size: ${data.size}") + listContent.addAll(data) + } + val header = + result.header?.musicDetailHeaderRenderer + ?: result.header?.musicEditablePlaylistDetailHeaderRenderer + ?: result.contents + ?.twoColumnBrowseResultsRenderer + ?.tabs + ?.get(0) + ?.tabRenderer + ?.content + ?.sectionListRenderer + ?.contents + ?.get(0) + ?.musicResponsiveHeaderRenderer + ?: result.contents + ?.twoColumnBrowseResultsRenderer + ?.tabs + ?.get(0) + ?.tabRenderer + ?.content + ?.sectionListRenderer + ?.contents + ?.get(0) + ?.musicEditablePlaylistDetailHeaderRenderer + ?.header + ?.musicResponsiveHeaderRenderer + Log.d("Header", "header: $header") + var continueParam = + result.contents + ?.singleColumnBrowseResultsRenderer + ?.tabs + ?.get( + 0, + )?.tabRenderer + ?.content + ?.sectionListRenderer + ?.contents + ?.get( + 0, + )?.musicPlaylistShelfRenderer + ?.continuations + ?.get( + 0, + )?.nextContinuationData + ?.continuation + ?: result.contents + ?.twoColumnBrowseResultsRenderer + ?.secondaryContents + ?.sectionListRenderer + ?.contents + ?.firstOrNull() + ?.musicPlaylistShelfRenderer + ?.continuations + ?.firstOrNull() + ?.nextContinuationData + ?.continuation + var count = 0 + Log.d("Repository", "playlist data: ${listContent.size}") + Log.d("Repository", "continueParam: $continueParam") // else { // var listTrack = playlistBrowse.tracks.toMutableList() - while (continueParam != null) { - YouTube.customQuery( - browseId = "", - continuation = continueParam, - setLogin = true, - ).onSuccess { values -> - Log.d("Continue", "continue: $continueParam") - val dataMore: List? = - values.continuationContents?.musicPlaylistShelfContinuation?.contents - if (dataMore != null) { - listContent.addAll(dataMore) - } - continueParam = - values.continuationContents?.musicPlaylistShelfContinuation?.continuations?.get( - 0, - )?.nextContinuationData?.continuation - count++ - }.onFailure { - Log.e("Continue", "Error: ${it.message}") - continueParam = null - count++ + while (continueParam != null) { + YouTube + .customQuery( + browseId = "", + continuation = continueParam, + setLogin = true, + ).onSuccess { values -> + Log.d("Continue", "continue: $continueParam") + val dataMore: List? = + values.continuationContents?.musicPlaylistShelfContinuation?.contents + if (dataMore != null) { + listContent.addAll(dataMore) + } + continueParam = + values.continuationContents + ?.musicPlaylistShelfContinuation + ?.continuations + ?.get( + 0, + )?.nextContinuationData + ?.continuation + count++ + }.onFailure { + Log.e("Continue", "Error: ${it.message}") + continueParam = null + count++ + } } + Log.d("Repository", "playlist final data: ${listContent.size}") + parsePlaylistData(header, listContent, playlistId, context)?.let { playlist -> + emit(Resource.Success(playlist)) + } ?: emit(Resource.Error("Error")) + }.onFailure { e -> + Log.e("Playlist Data", e.message ?: "Error") + emit(Resource.Error(e.message.toString())) } - Log.d("Repository", "playlist final data: ${listContent.size}") - parsePlaylistData(header, listContent, playlistId, context)?.let { playlist -> - emit(Resource.Success(playlist)) - } ?: emit(Resource.Error("Error")) - }.onFailure { e -> - Log.e("Playlist Data", e.message ?: "Error") - emit(Resource.Error(e.message.toString())) - } } }.flowOn(Dispatchers.IO) suspend fun getAlbumData(browseId: String): Flow> = flow { runCatching { - YouTube.album(browseId, withSongs = true).onSuccess { result -> - emit(Resource.Success(parseAlbumData(result))) - }.onFailure { e -> - Log.d("Album", "Error: ${e.message}") - emit(Resource.Error(e.message.toString())) - } + YouTube + .album(browseId, withSongs = true) + .onSuccess { result -> + emit(Resource.Success(parseAlbumData(result))) + }.onFailure { e -> + Log.d("Album", "Error: ${e.message}") + emit(Resource.Error(e.message.toString())) + } } }.flowOn(Dispatchers.IO) @@ -1262,129 +1558,146 @@ constructor( ): Flow = flow { runCatching { - YouTube.browse(browseId = browseId, params = params).onSuccess { result -> - Log.w("Album More", "result: $result") - emit(result) - }.onFailure { - it.printStackTrace() - emit(null) - } + YouTube + .browse(browseId = browseId, params = params) + .onSuccess { result -> + Log.w("Album More", "result: $result") + emit(result) + }.onFailure { + it.printStackTrace() + emit(null) + } } }.flowOn(Dispatchers.IO) suspend fun getArtistData(channelId: String): Flow> = flow { runCatching { - YouTube.artist(channelId).onSuccess { result -> - emit(Resource.Success(parseArtistData(result, context))) - }.onFailure { e -> - Log.d("Artist", "Error: ${e.message}") - emit(Resource.Error(e.message.toString())) - } + YouTube + .artist(channelId) + .onSuccess { result -> + emit(Resource.Success(parseArtistData(result, context))) + }.onFailure { e -> + Log.d("Artist", "Error: ${e.message}") + emit(Resource.Error(e.message.toString())) + } } }.flowOn(Dispatchers.IO) suspend fun getSearchDataSong(query: String): Flow>> = flow { runCatching { - YouTube.search(query, YouTube.SearchFilter.FILTER_SONG).onSuccess { result -> - val listSongs: ArrayList = arrayListOf() - var countinueParam = result.continuation - parseSearchSong(result).let { list -> - listSongs.addAll(list) - } - var count = 0 - while (count < 2 && countinueParam != null) { - YouTube.searchContinuation(countinueParam).onSuccess { values -> - parseSearchSong(values).let { list -> - listSongs.addAll(list) - } - count++ - countinueParam = values.continuation - }.onFailure { - Log.e("Continue", "Error: ${it.message}") - countinueParam = null - count++ + YouTube + .search(query, YouTube.SearchFilter.FILTER_SONG) + .onSuccess { result -> + val listSongs: ArrayList = arrayListOf() + var countinueParam = result.continuation + parseSearchSong(result).let { list -> + listSongs.addAll(list) + } + var count = 0 + while (count < 2 && countinueParam != null) { + YouTube + .searchContinuation(countinueParam) + .onSuccess { values -> + parseSearchSong(values).let { list -> + listSongs.addAll(list) + } + count++ + countinueParam = values.continuation + }.onFailure { + Log.e("Continue", "Error: ${it.message}") + countinueParam = null + count++ + } } - } - emit(Resource.Success>(listSongs)) - }.onFailure { e -> - Log.d("Search", "Error: ${e.message}") - emit(Resource.Error>(e.message.toString())) - } + emit(Resource.Success>(listSongs)) + }.onFailure { e -> + Log.d("Search", "Error: ${e.message}") + emit(Resource.Error>(e.message.toString())) + } } }.flowOn(Dispatchers.IO) suspend fun getSearchDataVideo(query: String): Flow>> = flow { runCatching { - YouTube.search(query, YouTube.SearchFilter.FILTER_VIDEO).onSuccess { result -> - val listSongs: ArrayList = arrayListOf() - var countinueParam = result.continuation - parseSearchVideo(result).let { list -> - listSongs.addAll(list) - } - var count = 0 - while (count < 2 && countinueParam != null) { - YouTube.searchContinuation(countinueParam).onSuccess { values -> - parseSearchVideo(values).let { list -> - listSongs.addAll(list) - } - count++ - countinueParam = values.continuation - }.onFailure { - Log.e("Continue", "Error: ${it.message}") - countinueParam = null - count++ + YouTube + .search(query, YouTube.SearchFilter.FILTER_VIDEO) + .onSuccess { result -> + val listSongs: ArrayList = arrayListOf() + var countinueParam = result.continuation + parseSearchVideo(result).let { list -> + listSongs.addAll(list) + } + var count = 0 + while (count < 2 && countinueParam != null) { + YouTube + .searchContinuation(countinueParam) + .onSuccess { values -> + parseSearchVideo(values).let { list -> + listSongs.addAll(list) + } + count++ + countinueParam = values.continuation + }.onFailure { + Log.e("Continue", "Error: ${it.message}") + countinueParam = null + count++ + } } - } - emit(Resource.Success>(listSongs)) - }.onFailure { e -> - Log.d("Search", "Error: ${e.message}") - emit(Resource.Error>(e.message.toString())) - } + emit(Resource.Success>(listSongs)) + }.onFailure { e -> + Log.d("Search", "Error: ${e.message}") + emit(Resource.Error>(e.message.toString())) + } } }.flowOn(Dispatchers.IO) suspend fun getSearchDataPodcast(query: String): Flow>> = flow { runCatching { - YouTube.search(query, YouTube.SearchFilter.FILTER_PODCAST).onSuccess { result -> - println(query) - val listPlaylist: ArrayList = arrayListOf() - var countinueParam = result.continuation - Log.w("Podcast", "result: $result") - parsePodcast(result.listPodcast).let { list -> - listPlaylist.addAll(list) - } - var count = 0 - while (count < 2 && countinueParam != null) { - YouTube.searchContinuation(countinueParam).onSuccess { values -> - parsePodcast(values.listPodcast).let { list -> - listPlaylist.addAll(list) - } - count++ - countinueParam = values.continuation - }.onFailure { - Log.e("Continue", "Error: ${it.message}") - countinueParam = null - count++ + YouTube + .search(query, YouTube.SearchFilter.FILTER_PODCAST) + .onSuccess { result -> + println(query) + val listPlaylist: ArrayList = arrayListOf() + var countinueParam = result.continuation + Log.w("Podcast", "result: $result") + parsePodcast(result.listPodcast).let { list -> + listPlaylist.addAll(list) } + var count = 0 + while (count < 2 && countinueParam != null) { + YouTube + .searchContinuation(countinueParam) + .onSuccess { values -> + parsePodcast(values.listPodcast).let { list -> + listPlaylist.addAll(list) + } + count++ + countinueParam = values.continuation + }.onFailure { + Log.e("Continue", "Error: ${it.message}") + countinueParam = null + count++ + } + } + emit(Resource.Success>(listPlaylist)) + }.onFailure { e -> + Log.d("Search", "Error: ${e.message}") + emit(Resource.Error>(e.message.toString())) } - emit(Resource.Success>(listPlaylist)) - }.onFailure { e -> - Log.d("Search", "Error: ${e.message}") - emit(Resource.Error>(e.message.toString())) - } } }.flowOn(Dispatchers.IO) suspend fun getSearchDataFeaturedPlaylist(query: String): Flow>> = flow { runCatching { - YouTube.search(query, YouTube.SearchFilter.FILTER_FEATURED_PLAYLIST) + YouTube + .search(query, YouTube.SearchFilter.FILTER_FEATURED_PLAYLIST) .onSuccess { result -> val listPlaylist: ArrayList = arrayListOf() var countinueParam = result.continuation @@ -1393,17 +1706,19 @@ constructor( } var count = 0 while (count < 2 && countinueParam != null) { - YouTube.searchContinuation(countinueParam).onSuccess { values -> - parseSearchPlaylist(values).let { list -> - listPlaylist.addAll(list) + YouTube + .searchContinuation(countinueParam) + .onSuccess { values -> + parseSearchPlaylist(values).let { list -> + listPlaylist.addAll(list) + } + count++ + countinueParam = values.continuation + }.onFailure { + Log.e("Continue", "Error: ${it.message}") + countinueParam = null + count++ } - count++ - countinueParam = values.continuation - }.onFailure { - Log.e("Continue", "Error: ${it.message}") - countinueParam = null - count++ - } } emit(Resource.Success>(listPlaylist)) }.onFailure { e -> @@ -1416,69 +1731,78 @@ constructor( suspend fun getSearchDataArtist(query: String): Flow>> = flow { runCatching { - YouTube.search(query, YouTube.SearchFilter.FILTER_ARTIST).onSuccess { result -> - val listArtist: ArrayList = arrayListOf() - var countinueParam = result.continuation - parseSearchArtist(result).let { list -> - listArtist.addAll(list) - } - var count = 0 - while (count < 2 && countinueParam != null) { - YouTube.searchContinuation(countinueParam).onSuccess { values -> - parseSearchArtist(values).let { list -> - listArtist.addAll(list) - } - count++ - countinueParam = values.continuation - }.onFailure { - Log.e("Continue", "Error: ${it.message}") - countinueParam = null - count++ + YouTube + .search(query, YouTube.SearchFilter.FILTER_ARTIST) + .onSuccess { result -> + val listArtist: ArrayList = arrayListOf() + var countinueParam = result.continuation + parseSearchArtist(result).let { list -> + listArtist.addAll(list) + } + var count = 0 + while (count < 2 && countinueParam != null) { + YouTube + .searchContinuation(countinueParam) + .onSuccess { values -> + parseSearchArtist(values).let { list -> + listArtist.addAll(list) + } + count++ + countinueParam = values.continuation + }.onFailure { + Log.e("Continue", "Error: ${it.message}") + countinueParam = null + count++ + } } + emit(Resource.Success>(listArtist)) + }.onFailure { e -> + Log.d("Search", "Error: ${e.message}") + emit(Resource.Error>(e.message.toString())) } - emit(Resource.Success>(listArtist)) - }.onFailure { e -> - Log.d("Search", "Error: ${e.message}") - emit(Resource.Error>(e.message.toString())) - } } }.flowOn(Dispatchers.IO) suspend fun getSearchDataAlbum(query: String): Flow>> = flow { runCatching { - YouTube.search(query, YouTube.SearchFilter.FILTER_ALBUM).onSuccess { result -> - val listAlbum: ArrayList = arrayListOf() - var countinueParam = result.continuation - parseSearchAlbum(result).let { list -> - listAlbum.addAll(list) - } - var count = 0 - while (count < 2 && countinueParam != null) { - YouTube.searchContinuation(countinueParam).onSuccess { values -> - parseSearchAlbum(values).let { list -> - listAlbum.addAll(list) - } - count++ - countinueParam = values.continuation - }.onFailure { - Log.e("Continue", "Error: ${it.message}") - countinueParam = null - count++ + YouTube + .search(query, YouTube.SearchFilter.FILTER_ALBUM) + .onSuccess { result -> + val listAlbum: ArrayList = arrayListOf() + var countinueParam = result.continuation + parseSearchAlbum(result).let { list -> + listAlbum.addAll(list) } + var count = 0 + while (count < 2 && countinueParam != null) { + YouTube + .searchContinuation(countinueParam) + .onSuccess { values -> + parseSearchAlbum(values).let { list -> + listAlbum.addAll(list) + } + count++ + countinueParam = values.continuation + }.onFailure { + Log.e("Continue", "Error: ${it.message}") + countinueParam = null + count++ + } + } + emit(Resource.Success>(listAlbum)) + }.onFailure { e -> + Log.d("Search", "Error: ${e.message}") + emit(Resource.Error>(e.message.toString())) } - emit(Resource.Success>(listAlbum)) - }.onFailure { e -> - Log.d("Search", "Error: ${e.message}") - emit(Resource.Error>(e.message.toString())) - } } }.flowOn(Dispatchers.IO) suspend fun getSearchDataPlaylist(query: String): Flow>> = flow { runCatching { - YouTube.search(query, YouTube.SearchFilter.FILTER_COMMUNITY_PLAYLIST) + YouTube + .search(query, YouTube.SearchFilter.FILTER_COMMUNITY_PLAYLIST) .onSuccess { result -> val listPlaylist: ArrayList = arrayListOf() var countinueParam = result.continuation @@ -1487,17 +1811,19 @@ constructor( } var count = 0 while (count < 2 && countinueParam != null) { - YouTube.searchContinuation(countinueParam).onSuccess { values -> - parseSearchPlaylist(values).let { list -> - listPlaylist.addAll(list) + YouTube + .searchContinuation(countinueParam) + .onSuccess { values -> + parseSearchPlaylist(values).let { list -> + listPlaylist.addAll(list) + } + count++ + countinueParam = values.continuation + }.onFailure { + Log.e("Continue", "Error: ${it.message}") + countinueParam = null + count++ } - count++ - countinueParam = values.continuation - }.onFailure { - Log.e("Continue", "Error: ${it.message}") - countinueParam = null - count++ - } } emit(Resource.Success>(listPlaylist)) }.onFailure { e -> @@ -1516,19 +1842,22 @@ constructor( // Log.d("Suggest", "Error: ${e.message}") // emit(Resource.Error>(e.message.toString())) // } - YouTube.getYTMusicSearchSuggestions(query).onSuccess { - emit(Resource.Success(it)) - }.onFailure { e -> - Log.d("Suggest", "Error: ${e.message}") - emit(Resource.Error(e.message.toString())) - } + YouTube + .getYTMusicSearchSuggestions(query) + .onSuccess { + emit(Resource.Success(it)) + }.onFailure { e -> + Log.d("Suggest", "Error: ${e.message}") + emit(Resource.Error(e.message.toString())) + } } }.flowOn(Dispatchers.IO) suspend fun getRelatedData(videoId: String): Flow, String?>>> = flow { runCatching { - YouTube.next(WatchEndpoint(videoId = videoId)) + YouTube + .next(WatchEndpoint(videoId = videoId)) .onSuccess { next -> val data: ArrayList = arrayListOf() data.addAll(next.items.filter { it.id != videoId }.toSet()) @@ -1551,13 +1880,15 @@ constructor( suspend fun getYouTubeCaption(videoId: String): Flow> = flow { runCatching { - YouTube.getYouTubeCaption(videoId).onSuccess { lyrics -> - Log.w("Lyrics", "lyrics: ${lyrics.toLyrics()}") - emit(Resource.Success(lyrics.toLyrics())) - }.onFailure { e -> - Log.d("Lyrics", "Error: ${e.message}") - emit(Resource.Error(e.message.toString())) - } + YouTube + .getYouTubeCaption(videoId) + .onSuccess { lyrics -> + Log.w("Lyrics", "lyrics: ${lyrics.toLyrics()}") + emit(Resource.Success(lyrics.toLyrics())) + }.onFailure { e -> + Log.d("Lyrics", "Error: ${e.message}") + emit(Resource.Error(e.message.toString())) + } } } @@ -1569,16 +1900,20 @@ constructor( runCatching { getSongById(videoId).first().let { song -> val q = - "${song?.title} ${song?.artistName?.firstOrNull() ?: ""}".replace( - Regex("\\((feat\\.|ft.|cùng với|con|mukana|com|avec|合作音乐人: ) "), - " ", - ).replace( - Regex("( và | & | и | e | und |, |和| dan)"), - " ", - ).replace(" ", " ").replace(Regex("([()])"), "").replace(".", " ") + "${song?.title} ${song?.artistName?.firstOrNull() ?: ""}" + .replace( + Regex("\\((feat\\.|ft.|cùng với|con|mukana|com|avec|合作音乐人: ) "), + " ", + ).replace( + Regex("( và | & | и | e | und |, |和| dan)"), + " ", + ).replace(" ", " ") + .replace(Regex("([()])"), "") + .replace(".", " ") .replace(" ", " ") var spotifyPersonalToken = "" - if (dataStoreManager.spotifyPersonalToken.first() + if (dataStoreManager.spotifyPersonalToken + .first() .isNotEmpty() && dataStoreManager.spotifyPersonalTokenExpires.first() > System.currentTimeMillis() && dataStoreManager.spotifyPersonalTokenExpires.first() != 0L @@ -1586,7 +1921,132 @@ constructor( spotifyPersonalToken = dataStoreManager.spotifyPersonalToken.first() Log.d("Lyrics", "spotifyPersonalToken: $spotifyPersonalToken") } else if (dataStoreManager.spdc.first().isNotEmpty()) { - YouTube.getPersonalToken(dataStoreManager.spdc.first()).onSuccess { + YouTube + .getPersonalToken(dataStoreManager.spdc.first()) + .onSuccess { + spotifyPersonalToken = it.accessToken + dataStoreManager.setSpotifyPersonalToken(spotifyPersonalToken) + dataStoreManager.setSpotifyPersonalTokenExpires( + it.accessTokenExpirationTimestampMs, + ) + Log.d("Lyrics", "spotifyPersonalToken: $spotifyPersonalToken") + }.onFailure { + it.printStackTrace() + emit(null) + } + } + if (spotifyPersonalToken.isNotEmpty()) { + var clientToken = dataStoreManager.spotifyClientToken.first() + Log.d("Lyrics", "clientToken: $clientToken") + YouTube + .searchSpotifyTrack(q, clientToken) + .onSuccess { searchResponse -> + val track = + if (duration != 0) { + searchResponse.tracks.items.find { + abs( + ((it.duration_ms / 1000) - duration), + ) < 1 + } + ?: searchResponse.tracks.items.firstOrNull() + } else { + searchResponse.tracks.items.firstOrNull() + } + if (track != null) { + YouTube + .getSpotifyCanvas( + track.id, + spotifyPersonalToken, + ).onSuccess { + Log.w("Spotify Canvas", "canvas: $it") + emit(it) + }.onFailure { + it.printStackTrace() + emit(null) + } + } else { + emit(null) + } + }.onFailure { throwable -> + throwable.printStackTrace() + YouTube + .getClientToken() + .onSuccess { tokenResponse -> + clientToken = tokenResponse.accessToken + Log.w("Lyrics", "clientToken: $clientToken") + dataStoreManager.setSpotifyClientToken(clientToken) + YouTube + .searchSpotifyTrack(q, clientToken) + .onSuccess { searchResponse -> + val track = + if (duration != 0) { + searchResponse.tracks.items.find { + abs( + ((it.duration_ms / 1000) - duration), + ) < 1 + } + ?: searchResponse.tracks.items.firstOrNull() + } else { + searchResponse.tracks.items.firstOrNull() + } + if (track != null) { + YouTube + .getSpotifyCanvas( + track.id, + spotifyPersonalToken, + ).onSuccess { + Log.w("Spotify Canvas", "canvas: $it") + emit(it) + }.onFailure { + it.printStackTrace() + emit(null) + } + } else { + emit(null) + } + } + }.onFailure { + it.printStackTrace() + emit(null) + } + } + } + } + } + }.flowOn(Dispatchers.IO) + + suspend fun getSpotifyLyrics( + query: String, + duration: Int?, + ): Flow> = + flow { + runCatching { + val q = + query + .replace( + Regex("\\((feat\\.|ft.|cùng với|con|mukana|com|avec|合作音乐人: ) "), + " ", + ).replace( + Regex("( và | & | и | e | und |, |和| dan)"), + " ", + ).replace(" ", " ") + .replace(Regex("([()])"), "") + .replace(".", " ") + .replace(" ", " ") + Log.d("Lyrics", "query: $q") + var spotifyPersonalToken = "" + if (dataStoreManager.spotifyPersonalToken + .first() + .isNotEmpty() && + dataStoreManager.spotifyPersonalTokenExpires.first() > System.currentTimeMillis() && + dataStoreManager.spotifyPersonalTokenExpires.first() != 0L + ) { + spotifyPersonalToken = dataStoreManager.spotifyPersonalToken.first() + Log.d("Lyrics", "spotifyPersonalToken: $spotifyPersonalToken") + } else if (dataStoreManager.spdc.first().isNotEmpty()) { + YouTube + .getPersonalToken(dataStoreManager.spdc.first()) + .onSuccess { spotifyPersonalToken = it.accessToken dataStoreManager.setSpotifyPersonalToken(spotifyPersonalToken) dataStoreManager.setSpotifyPersonalTokenExpires( @@ -1595,15 +2055,17 @@ constructor( Log.d("Lyrics", "spotifyPersonalToken: $spotifyPersonalToken") }.onFailure { it.printStackTrace() - emit(null) + emit(Resource.Error("Not found")) } - } - if (spotifyPersonalToken.isNotEmpty()) { - var clientToken = dataStoreManager.spotifyClientToken.first() - Log.d("Lyrics", "clientToken: $clientToken") - YouTube.searchSpotifyTrack(q, clientToken).onSuccess { searchResponse -> + } + if (spotifyPersonalToken.isNotEmpty()) { + var clientToken = dataStoreManager.spotifyClientToken.first() + Log.d("Lyrics", "clientToken: $clientToken") + YouTube + .searchSpotifyTrack(q, clientToken) + .onSuccess { searchResponse -> val track = - if (duration != 0) { + if (duration != null && duration != 0) { searchResponse.tracks.items.find { abs( ((it.duration_ms / 1000) - duration), @@ -1613,30 +2075,30 @@ constructor( } else { searchResponse.tracks.items.firstOrNull() } + Log.d("Lyrics", "track: $track") if (track != null) { - YouTube.getSpotifyCanvas( - track.id, - spotifyPersonalToken, - ).onSuccess { - Log.w("Spotify Canvas", "canvas: $it") - emit(it) - }.onFailure { - it.printStackTrace() - emit(null) - } + YouTube + .getSpotifyLyrics(track.id, spotifyPersonalToken) + .onSuccess { + emit(Resource.Success(it.toLyrics())) + }.onFailure { + it.printStackTrace() + emit(Resource.Error("Not found")) + } } else { - emit(null) + emit(Resource.Error("Not found")) } }.onFailure { throwable -> throwable.printStackTrace() - YouTube.getClientToken().onSuccess { tokenResponse -> - clientToken = tokenResponse.accessToken - Log.w("Lyrics", "clientToken: $clientToken") - dataStoreManager.setSpotifyClientToken(clientToken) - YouTube.searchSpotifyTrack(q, clientToken) - .onSuccess { searchResponse -> + YouTube + .getClientToken() + .onSuccess { + clientToken = it.accessToken + Log.w("Lyrics", "clientToken: $clientToken") + dataStoreManager.setSpotifyClientToken(clientToken) + YouTube.searchSpotifyTrack(q, clientToken).onSuccess { searchResponse -> val track = - if (duration != 0) { + if (duration != null && duration != 0) { searchResponse.tracks.items.find { abs( ((it.duration_ms / 1000) - duration), @@ -1646,129 +2108,25 @@ constructor( } else { searchResponse.tracks.items.firstOrNull() } + Log.d("Lyrics", "track: $track") if (track != null) { - YouTube.getSpotifyCanvas( - track.id, - spotifyPersonalToken, - ) + YouTube + .getSpotifyLyrics(track.id, spotifyPersonalToken) .onSuccess { - Log.w("Spotify Canvas", "canvas: $it") - emit(it) + emit(Resource.Success(it.toLyrics())) }.onFailure { it.printStackTrace() - emit(null) + emit(Resource.Error("Not found")) } } else { - emit(null) - } - } - }.onFailure { - it.printStackTrace() - emit(null) - } - } - } - } - } - }.flowOn(Dispatchers.IO) - - suspend fun getSpotifyLyrics( - query: String, - duration: Int?, - ): Flow> = - flow { - runCatching { - val q = - query.replace( - Regex("\\((feat\\.|ft.|cùng với|con|mukana|com|avec|合作音乐人: ) "), - " ", - ).replace( - Regex("( và | & | и | e | und |, |和| dan)"), - " ", - ).replace(" ", " ").replace(Regex("([()])"), "").replace(".", " ") - .replace(" ", " ") - Log.d("Lyrics", "query: $q") - var spotifyPersonalToken = "" - if (dataStoreManager.spotifyPersonalToken.first() - .isNotEmpty() && dataStoreManager.spotifyPersonalTokenExpires.first() > System.currentTimeMillis() && dataStoreManager.spotifyPersonalTokenExpires.first() != 0L - ) { - spotifyPersonalToken = dataStoreManager.spotifyPersonalToken.first() - Log.d("Lyrics", "spotifyPersonalToken: $spotifyPersonalToken") - } else if (dataStoreManager.spdc.first().isNotEmpty()) { - YouTube.getPersonalToken(dataStoreManager.spdc.first()).onSuccess { - spotifyPersonalToken = it.accessToken - dataStoreManager.setSpotifyPersonalToken(spotifyPersonalToken) - dataStoreManager.setSpotifyPersonalTokenExpires( - it.accessTokenExpirationTimestampMs, - ) - Log.d("Lyrics", "spotifyPersonalToken: $spotifyPersonalToken") - }.onFailure { - it.printStackTrace() - emit(Resource.Error("Not found")) - } - } - if (spotifyPersonalToken.isNotEmpty()) { - var clientToken = dataStoreManager.spotifyClientToken.first() - Log.d("Lyrics", "clientToken: $clientToken") - YouTube.searchSpotifyTrack(q, clientToken).onSuccess { searchResponse -> - val track = - if (duration != null && duration != 0) { - searchResponse.tracks.items.find { - abs( - ((it.duration_ms / 1000) - duration), - ) < 1 - } - ?: searchResponse.tracks.items.firstOrNull() - } else { - searchResponse.tracks.items.firstOrNull() - } - Log.d("Lyrics", "track: $track") - if (track != null) { - YouTube.getSpotifyLyrics(track.id, spotifyPersonalToken).onSuccess { - emit(Resource.Success(it.toLyrics())) - }.onFailure { - it.printStackTrace() - emit(Resource.Error("Not found")) - } - } else { - emit(Resource.Error("Not found")) - } - }.onFailure { throwable -> - throwable.printStackTrace() - YouTube.getClientToken().onSuccess { - clientToken = it.accessToken - Log.w("Lyrics", "clientToken: $clientToken") - dataStoreManager.setSpotifyClientToken(clientToken) - YouTube.searchSpotifyTrack(q, clientToken).onSuccess { searchResponse -> - val track = - if (duration != null && duration != 0) { - searchResponse.tracks.items.find { - abs( - ((it.duration_ms / 1000) - duration), - ) < 1 - } - ?: searchResponse.tracks.items.firstOrNull() - } else { - searchResponse.tracks.items.firstOrNull() - } - Log.d("Lyrics", "track: $track") - if (track != null) { - YouTube.getSpotifyLyrics(track.id, spotifyPersonalToken) - .onSuccess { - emit(Resource.Success(it.toLyrics())) - }.onFailure { - it.printStackTrace() emit(Resource.Error("Not found")) } - } else { + } + }.onFailure { + it.printStackTrace() emit(Resource.Error("Not found")) } - } - }.onFailure { - it.printStackTrace() - emit(Resource.Error("Not found")) } - } } } } @@ -1780,61 +2138,74 @@ constructor( ): Flow>> = flow { runCatching { - val TAG = "Lyrics" + val tag = "Lyrics" // val q = query.replace(Regex("\\([^)]*?(feat.|ft.|cùng với|con)[^)]*?\\)"), "") // .replace(" ", " ") val qartist = - sartist.replace( - Regex("\\((feat\\.|ft.|cùng với|con|mukana|com|avec|合作音乐人: ) "), - " ", - ).replace( - Regex("( và | & | и | e | und |, |和| dan)"), - " ", - ).replace(" ", " ").replace(Regex("([()])"), "").replace(".", " ") + sartist + .replace( + Regex("\\((feat\\.|ft.|cùng với|con|mukana|com|avec|合作音乐人: ) "), + " ", + ).replace( + Regex("( và | & | и | e | und |, |和| dan)"), + " ", + ).replace(" ", " ") + .replace(Regex("([()])"), "") + .replace(".", " ") val qtrack = - strack.replace( - Regex("\\((feat\\.|ft.|cùng với|con|mukana|com|avec|合作音乐人: ) "), - " ", - ).replace( - Regex("( và | & | и | e | und |, |和| dan)"), - " ", - ).replace(" ", " ").replace(Regex("([()])"), "").replace(".", " ") + strack + .replace( + Regex("\\((feat\\.|ft.|cùng với|con|mukana|com|avec|合作音乐人: ) "), + " ", + ).replace( + Regex("( và | & | и | e | und |, |和| dan)"), + " ", + ).replace(" ", " ") + .replace(Regex("([()])"), "") + .replace(".", " ") val q = "$qtrack $qartist" - Log.d(TAG, "query: $q") + Log.d(tag, "query: $q") var musixMatchUserToken = YouTube.musixmatchUserToken if (musixMatchUserToken == null) { - YouTube.getMusixmatchUserToken().onSuccess { usertoken -> - YouTube.musixmatchUserToken = usertoken.message.body.user_token - Log.d(TAG, "musixMatchUserToken: ${usertoken.message.body.user_token}") - musixMatchUserToken = usertoken.message.body.user_token - } - .onFailure { throwable -> - Log.e(TAG, throwable.message.toString()) + YouTube + .getMusixmatchUserToken() + .onSuccess { usertoken -> + YouTube.musixmatchUserToken = usertoken.message.body.user_token + Log.d(tag, "musixMatchUserToken: ${usertoken.message.body.user_token}") + musixMatchUserToken = usertoken.message.body.user_token + }.onFailure { throwable -> + Log.e(tag, throwable.message.toString()) emit(Pair("", Resource.Error("Not found"))) } } - YouTube.searchMusixmatchTrackId(q, musixMatchUserToken!!) + YouTube + .searchMusixmatchTrackId(q, musixMatchUserToken!!) .onSuccess { searchResult -> Log.d( - TAG, "searchResult: ${ + tag, + "searchResult: ${ searchResult.message.body.track_list?.map { it.track.track_name + " " + it.track.artist_name } - }" + }", ) val trackList = - if (!searchResult.message.body.track_list.isNullOrEmpty()) { + if (!searchResult.message.body.track_list + .isNullOrEmpty() + ) { searchResult.message.body.track_list } else { - searchResult.message.body.macro_result_list?.track_list + searchResult.message.body.macro_result_list + ?.track_list } if (!trackList.isNullOrEmpty()) { Log.d( - TAG, "trackList: ${ + tag, + "trackList: ${ trackList.map { it.track.track_name + " " + it.track.artist_name } - }" + }", ) val list = arrayListOf() for (i in trackList) { @@ -1842,7 +2213,7 @@ constructor( } var id = "" var track: SearchMusixmatchResponse.Message.Body.Track.TrackX? = null - Log.d(TAG, "duration: $durationInt") + Log.d(tag, "duration: $durationInt") val bestMatchingIndex = bestMatchingIndex(q, list) if (bestMatchingIndex != null) { if (durationInt != null && durationInt != 0) { @@ -1856,27 +2227,33 @@ constructor( it - durationInt, ) } - if (closestIndex != null && kotlin.math.abs( + if (closestIndex != null && + kotlin.math.abs( closestIndex - durationInt, ) < 2 ) { id += - trackList.find { - it.track.track_length == closestIndex - }?.track?.track_id.toString() + trackList + .find { + it.track.track_length == closestIndex + }?.track + ?.track_id + .toString() track = trackList.find { it.track.track_length == closestIndex }?.track } if (id == "" && list.get(bestMatchingIndex).contains( - trackList.get( - bestMatchingIndex, - ).track.track_name, + trackList + .get( + bestMatchingIndex, + ).track.track_name, ) && q.contains( - trackList.get( - bestMatchingIndex, - ).track.track_name, + trackList + .get( + bestMatchingIndex, + ).track.track_name, ) ) { Log.w( @@ -1888,24 +2265,30 @@ constructor( }", ) id += - trackList.get( - bestMatchingIndex, - ).track.track_id.toString() + trackList + .get( + bestMatchingIndex, + ).track.track_id + .toString() track = - trackList.get( - bestMatchingIndex, - ).track + trackList + .get( + bestMatchingIndex, + ).track } - } else if (list.get(bestMatchingIndex) + } else if (list + .get(bestMatchingIndex) .contains( - trackList.get( - bestMatchingIndex, - ).track.track_name, + trackList + .get( + bestMatchingIndex, + ).track.track_name, ) && q.contains( - trackList.get( - bestMatchingIndex, - ).track.track_name, + trackList + .get( + bestMatchingIndex, + ).track.track_name, ) ) { Log.w( @@ -1917,22 +2300,26 @@ constructor( }", ) id += - trackList.get( - bestMatchingIndex, - ).track.track_id.toString() + trackList + .get( + bestMatchingIndex, + ).track.track_id + .toString() track = - trackList.get( - bestMatchingIndex, - ).track + trackList + .get( + bestMatchingIndex, + ).track } } - Log.d(TAG, "id: $id") + Log.d(tag, "id: $id") Log.w( - TAG, - "item lyrics ${track.toString()}", + tag, + "item lyrics $track", ) if (id != "" && track != null) { - YouTube.getMusixmatchLyricsByQ(track, musixMatchUserToken!!) + YouTube + .getMusixmatchLyricsByQ(track, musixMatchUserToken!!) .onSuccess { if (it != null) { emit( @@ -1947,67 +2334,73 @@ constructor( } }.onFailure { throwable -> throwable.printStackTrace() - YouTube.getLrclibLyrics(qtrack, qartist, durationInt).onSuccess { - it?.let { emit(Pair(id, Resource.Success(it.toLyrics()))) } - }.onFailure { - it.printStackTrace() - emit(Pair(id, Resource.Error("Not found"))) - } + YouTube + .getLrclibLyrics(qtrack, qartist, durationInt) + .onSuccess { + it?.let { emit(Pair(id, Resource.Success(it.toLyrics()))) } + }.onFailure { + it.printStackTrace() + emit(Pair(id, Resource.Error("Not found"))) + } } } else { - YouTube.fixSearchMusixmatch( - q_artist = qartist, - q_track = qtrack, - q_duration = (durationInt ?: 0).toString(), - userToken = musixMatchUserToken!!, - ) - .onSuccess { + YouTube + .fixSearchMusixmatch( + q_artist = qartist, + q_track = qtrack, + q_duration = (durationInt ?: 0).toString(), + userToken = musixMatchUserToken!!, + ).onSuccess { val trackX = it.message.body.track - Log.w(TAG, "Fix Search Musixmatch: $trackX") + Log.w(tag, "Fix Search Musixmatch: $trackX") if (trackX != null && (abs(trackX.track_length - (durationInt ?: 0)) <= 10)) { - YouTube.getMusixmatchLyricsByQ(trackX, musixMatchUserToken!!).onSuccess { - Log.w(TAG, "Item lyrics ${it?.lyrics?.syncType.toString()}") - if (it != null) { - emit( - Pair( - trackX.track_id.toString(), - Resource.Success(it.toLyrics()), - ), - ) - } else { - Log.w("Lyrics", "Error: Lỗi getLyrics $it") - YouTube.getLrclibLyrics(qtrack, qartist, durationInt) + YouTube + .getMusixmatchLyricsByQ(trackX, musixMatchUserToken!!) + .onSuccess { + Log.w(tag, "Item lyrics ${it?.lyrics?.syncType}") + if (it != null) { + emit( + Pair( + trackX.track_id.toString(), + Resource.Success(it.toLyrics()), + ), + ) + } else { + Log.w("Lyrics", "Error: Lỗi getLyrics $it") + YouTube.getLrclibLyrics(qtrack, qartist, durationInt) + emit(Pair(id, Resource.Error("Not found"))) + } + }.onFailure { + it.printStackTrace() emit(Pair(id, Resource.Error("Not found"))) } - }.onFailure { - it.printStackTrace() - emit(Pair(id, Resource.Error("Not found"))) - } } else { - YouTube.getLrclibLyrics(qtrack, qartist, durationInt).onSuccess { - it?.let { emit(Pair(trackX?.track_id.toString(), Resource.Success(it.toLyrics()))) } + YouTube + .getLrclibLyrics(qtrack, qartist, durationInt) + .onSuccess { + it?.let { emit(Pair(trackX?.track_id.toString(), Resource.Success(it.toLyrics()))) } + }.onFailure { + it.printStackTrace() + emit(Pair(id, Resource.Error("Not found"))) + } + } + }.onFailure { + Log.e(tag, "Fix musixmatch search" + it.message.toString()) + YouTube + .getLrclibLyrics(qtrack, qartist, durationInt) + .onSuccess { + Log.w(tag, "Liblrc Item lyrics ${it?.lyrics?.syncType}") + it?.let { emit(Pair(id, Resource.Success(it.toLyrics()))) } }.onFailure { - it.printStackTrace() + Log.e(tag, "Liblrc Error: ${it.message}") emit(Pair(id, Resource.Error("Not found"))) } - } - } - .onFailure { - Log.e(TAG, "Fix musixmatch search" + it.message.toString()) - YouTube.getLrclibLyrics(qtrack, qartist, durationInt).onSuccess { - Log.w(TAG, "Liblrc Item lyrics ${it?.lyrics?.syncType.toString()}") - it?.let { emit(Pair(id, Resource.Success(it.toLyrics()))) } - }.onFailure { - Log.e(TAG, "Liblrc Error: ${it.message}") - emit(Pair(id, Resource.Error("Not found"))) - } } } } else { emit(Pair("", Resource.Error("Not found"))) } - } - .onFailure { throwable -> + }.onFailure { throwable -> throwable.printStackTrace() emit(Pair("", Resource.Error("Not found"))) } @@ -2235,16 +2628,15 @@ constructor( flow { runCatching { YouTube.musixmatchUserToken?.let { - YouTube.getMusixmatchTranslateLyrics( - id, - it, - dataStoreManager.translationLanguage.first(), - ) - .onSuccess { lyrics -> + YouTube + .getMusixmatchTranslateLyrics( + id, + it, + dataStoreManager.translationLanguage.first(), + ).onSuccess { lyrics -> Log.w("Translate Lyrics", "lyrics: $lyrics") emit(lyrics) - } - .onFailure { + }.onFailure { it.printStackTrace() emit(null) } @@ -2256,81 +2648,89 @@ constructor( suspend fun getSongInfo(videoId: String): Flow = flow { runCatching { - val id = if (videoId.contains(MergingMediaSourceFactory.isVideo)) - videoId.removePrefix(MergingMediaSourceFactory.isVideo) - else videoId - YouTube.getSongInfo(id).onSuccess { songInfo -> - val song = - SongInfoEntity( - videoId = songInfo.videoId, - author = songInfo.author, - authorId = songInfo.authorId, - authorThumbnail = songInfo.authorThumbnail, - description = songInfo.description, - uploadDate = songInfo.uploadDate, - subscribers = songInfo.subscribers, - viewCount = songInfo.viewCount, - like = songInfo.like, - dislike = songInfo.dislike, + val id = + if (videoId.contains(MergingMediaSourceFactory.isVideo)) { + videoId.removePrefix(MergingMediaSourceFactory.isVideo) + } else { + videoId + } + YouTube + .getSongInfo(id) + .onSuccess { songInfo -> + val song = + SongInfoEntity( + videoId = songInfo.videoId, + author = songInfo.author, + authorId = songInfo.authorId, + authorThumbnail = songInfo.authorThumbnail, + description = songInfo.description, + uploadDate = songInfo.uploadDate, + subscribers = songInfo.subscribers, + viewCount = songInfo.viewCount, + like = songInfo.like, + dislike = songInfo.dislike, + ) + emit(song) + insertSongInfo( + song, ) - emit(song) - insertSongInfo( - song, - ) - }.onFailure { - it.printStackTrace() - emit(getSongInfoEntiy(videoId).firstOrNull()) - } + }.onFailure { + it.printStackTrace() + emit(getSongInfoEntiy(videoId).firstOrNull()) + } } }.flowOn(Dispatchers.IO) - suspend fun getLikeStatus(videoId: String): Flow = flow { - runCatching { - YouTube.getLikedInfo(videoId).onSuccess { - if (it == LikeStatus.LIKE) emit(true) else emit(false) - }.onFailure { - it.printStackTrace() - emit(false) + suspend fun getLikeStatus(videoId: String): Flow = + flow { + runCatching { + YouTube + .getLikedInfo(videoId) + .onSuccess { + if (it == LikeStatus.LIKE) emit(true) else emit(false) + }.onFailure { + it.printStackTrace() + emit(false) + } } } - } @UnstableApi - suspend fun updateFormat( - videoId: String - ) { + suspend fun updateFormat(videoId: String) { localDataSource.getNewFormat(videoId)?.let { oldFormat -> Log.w("Stream", "oldFormatExpired: ${oldFormat.expiredTime}") Log.w("Stream", "now: ${LocalDateTime.now()}") Log.w("Stream", "isExpired: ${oldFormat.expiredTime.isBefore(LocalDateTime.now())}") if (oldFormat.expiredTime.isBefore(LocalDateTime.now())) { - YouTube.player(videoId).onSuccess { triple -> - val response = triple.second - localDataSource.updateNewFormat( - oldFormat.copy( - expiredTime = LocalDateTime.now().plusSeconds(response.streamingData?.expiresInSeconds?.toLong() ?: 0L), - playbackTrackingVideostatsPlaybackUrl = - response.playbackTracking?.videostatsPlaybackUrl?.baseUrl?.replace( - "https://s.youtube.com", - "https://music.youtube.com", + YouTube + .player(videoId) + .onSuccess { triple -> + val response = triple.second + localDataSource.updateNewFormat( + oldFormat.copy( + expiredTime = LocalDateTime.now().plusSeconds(response.streamingData?.expiresInSeconds?.toLong() ?: 0L), + playbackTrackingVideostatsPlaybackUrl = + response.playbackTracking?.videostatsPlaybackUrl?.baseUrl?.replace( + "https://s.youtube.com", + "https://music.youtube.com", + ), + playbackTrackingAtrUrl = + response.playbackTracking?.atrUrl?.baseUrl?.replace( + "https://s.youtube.com", + "https://music.youtube.com", + ), + playbackTrackingVideostatsWatchtimeUrl = + response.playbackTracking?.videostatsWatchtimeUrl?.baseUrl?.replace( + "https://s.youtube.com", + "https://music.youtube.com", + ), + cpn = triple.first, ), - playbackTrackingAtrUrl = - response.playbackTracking?.atrUrl?.baseUrl?.replace( - "https://s.youtube.com", - "https://music.youtube.com", - ), - playbackTrackingVideostatsWatchtimeUrl = - response.playbackTracking?.videostatsWatchtimeUrl?.baseUrl?.replace( - "https://s.youtube.com", - "https://music.youtube.com", - ), - cpn = triple.first, ) - ) - Log.w("UpdateFormat", "Updated format for $videoId") - }.onFailure { throwable -> - Log.e("UpdateFormat", "Error: ${throwable.message}") - } + Log.w("UpdateFormat", "Updated format for $videoId") + }.onFailure { throwable -> + Log.e("UpdateFormat", "Error: ${throwable.message}") + } } } } @@ -2341,92 +2741,110 @@ constructor( isVideo: Boolean, ): Flow = flow { - //134, 136, 137 - YouTube.player(videoId).onSuccess { data -> - val itag = QUALITY.itags.getOrNull(QUALITY.items.indexOf(dataStoreManager.quality.first())) - val videoItag = - VIDEO_QUALITY.itags.getOrNull( - VIDEO_QUALITY.items.indexOf(dataStoreManager.videoQuality.first()), - ) - ?: 134 - val response = data.second - if (data.third == MediaType.Song) { + // 134, 136, 137 + YouTube + .player(videoId) + .onSuccess { data -> + val itag = QUALITY.itags.getOrNull(QUALITY.items.indexOf(dataStoreManager.quality.first())) + val videoItag = + VIDEO_QUALITY.itags.getOrNull( + VIDEO_QUALITY.items.indexOf(dataStoreManager.videoQuality.first()), + ) + ?: 134 + val response = data.second + if (data.third == MediaType.Song) { + Log.w( + "Stream", + "response: is SONG", + ) + } else { + Log.w("Stream", "response: is VIDEO") + } Log.w( "Stream", - "response: is SONG", + response.streamingData + ?.formats + ?.map { it.itag } + .toString() + " " + + response.streamingData + ?.adaptiveFormats + ?.map { it.itag } + .toString(), ) - } else { - Log.w("Stream", "response: is VIDEO") - } - Log.w("Stream", response.streamingData?.formats?.map { it.itag }.toString() + " " + - response.streamingData?.adaptiveFormats?.map { it.itag }.toString() - ) - - Log.w("Stream", "Get stream for video $isVideo") - var format = - if (isVideo) { - response.streamingData?.formats?.find { it.itag == videoItag } - ?: response.streamingData?.adaptiveFormats?.find { it.itag == videoItag } - ?: response.streamingData?.formats?.find { it.itag == 136 } - ?: response.streamingData?.adaptiveFormats?.find { it.itag == 136 } - ?: response.streamingData?.formats?.find { it.itag == 134 } - ?: response.streamingData?.adaptiveFormats?.find { it.itag == 134 } - } else { - if (response.streamingData?.adaptiveFormats?.find { it.itag == 141 } != null) { - response.streamingData?.adaptiveFormats?.find { it.itag == 141 } + + Log.w("Stream", "Get stream for video $isVideo") + var format = + if (isVideo) { + response.streamingData?.formats?.find { it.itag == videoItag } + ?: response.streamingData?.adaptiveFormats?.find { it.itag == videoItag } + ?: response.streamingData?.formats?.find { it.itag == 136 } + ?: response.streamingData?.adaptiveFormats?.find { it.itag == 136 } + ?: response.streamingData?.formats?.find { it.itag == 134 } + ?: response.streamingData?.adaptiveFormats?.find { it.itag == 134 } } else { - response.streamingData?.adaptiveFormats?.find { it.itag == itag } + if (response.streamingData?.adaptiveFormats?.find { it.itag == 141 } != null) { + response.streamingData?.adaptiveFormats?.find { it.itag == 141 } + } else { + response.streamingData?.adaptiveFormats?.find { it.itag == itag } + } } + if (format == null) { + format = response.streamingData?.adaptiveFormats?.lastOrNull() } - if (format == null) { - format = response.streamingData?.adaptiveFormats?.lastOrNull() - } - Log.w("Stream", "format: $format") - Log.d("Stream", "expireInSeconds ${response.streamingData?.expiresInSeconds}") - Log.w("Stream", "expired at ${LocalDateTime.now().plusSeconds(response.streamingData?.expiresInSeconds?.toLong() ?: 0L)}") - runBlocking { - insertNewFormat( - NewFormatEntity( - videoId = if (VIDEO_QUALITY.itags.contains(format?.itag)) "${MergingMediaSourceFactory.isVideo}$videoId" else videoId, - itag = format?.itag ?: itag ?: 141, - mimeType = - Regex("""([^;]+);\s*codecs=["']([^"']+)["']""").find( - format?.mimeType ?: "", - )?.groupValues?.getOrNull(1) ?: format?.mimeType ?: "", - codecs = - Regex("""([^;]+);\s*codecs=["']([^"']+)["']""").find( - format?.mimeType ?: "", - )?.groupValues?.getOrNull(2) ?: format?.mimeType ?: "", - bitrate = format?.bitrate, - sampleRate = format?.audioSampleRate, - contentLength = format?.contentLength, - loudnessDb = response.playerConfig?.audioConfig?.loudnessDb?.toFloat(), - lengthSeconds = response.videoDetails?.lengthSeconds?.toInt(), - playbackTrackingVideostatsPlaybackUrl = - response.playbackTracking?.videostatsPlaybackUrl?.baseUrl?.replace( - "https://s.youtube.com", - "https://music.youtube.com", - ), - playbackTrackingAtrUrl = - response.playbackTracking?.atrUrl?.baseUrl?.replace( - "https://s.youtube.com", - "https://music.youtube.com", - ), - playbackTrackingVideostatsWatchtimeUrl = - response.playbackTracking?.videostatsWatchtimeUrl?.baseUrl?.replace( - "https://s.youtube.com", - "https://music.youtube.com", + Log.w("Stream", "format: $format") + Log.d("Stream", "expireInSeconds ${response.streamingData?.expiresInSeconds}") + Log.w("Stream", "expired at ${LocalDateTime.now().plusSeconds(response.streamingData?.expiresInSeconds?.toLong() ?: 0L)}") + runBlocking { + insertNewFormat( + NewFormatEntity( + videoId = if (VIDEO_QUALITY.itags.contains(format?.itag)) "${MergingMediaSourceFactory.isVideo}$videoId" else videoId, + itag = format?.itag ?: itag ?: 141, + mimeType = + Regex("""([^;]+);\s*codecs=["']([^"']+)["']""") + .find( + format?.mimeType ?: "", + )?.groupValues + ?.getOrNull(1) ?: format?.mimeType ?: "", + codecs = + Regex("""([^;]+);\s*codecs=["']([^"']+)["']""") + .find( + format?.mimeType ?: "", + )?.groupValues + ?.getOrNull(2) ?: format?.mimeType ?: "", + bitrate = format?.bitrate, + sampleRate = format?.audioSampleRate, + contentLength = format?.contentLength, + loudnessDb = + response.playerConfig + ?.audioConfig + ?.loudnessDb + ?.toFloat(), + lengthSeconds = response.videoDetails?.lengthSeconds?.toInt(), + playbackTrackingVideostatsPlaybackUrl = + response.playbackTracking?.videostatsPlaybackUrl?.baseUrl?.replace( + "https://s.youtube.com", + "https://music.youtube.com", + ), + playbackTrackingAtrUrl = + response.playbackTracking?.atrUrl?.baseUrl?.replace( + "https://s.youtube.com", + "https://music.youtube.com", + ), + playbackTrackingVideostatsWatchtimeUrl = + response.playbackTracking?.videostatsWatchtimeUrl?.baseUrl?.replace( + "https://s.youtube.com", + "https://music.youtube.com", + ), + cpn = data.first, + expiredTime = LocalDateTime.now().plusSeconds(response.streamingData?.expiresInSeconds?.toLong() ?: 0L), ), - cpn = data.first, - expiredTime = LocalDateTime.now().plusSeconds(response.streamingData?.expiresInSeconds?.toLong() ?: 0L) - ), - ) - } - if (data.first != null) { - emit(format?.url?.plus("&cpn=${data.first}")) - } else { - emit(format?.url) - } + ) + } + if (data.first != null) { + emit(format?.url?.plus("&cpn=${data.first}")) + } else { + emit(format?.url) + } // insertFormat( // FormatEntity( // videoId = videoId, @@ -2447,32 +2865,41 @@ constructor( // lengthSeconds = response.videoDetails?.lengthSeconds?.toInt(), // ) // ) - }.onFailure { - it.printStackTrace() - Log.e("Stream", "Error: ${it.message}") - emit(null) - } + }.onFailure { + it.printStackTrace() + Log.e("Stream", "Error: ${it.message}") + emit(null) + } }.flowOn(Dispatchers.IO) suspend fun getLibraryPlaylist(): Flow?> = flow { - YouTube.getLibraryPlaylists().onSuccess { data -> - val input = - data.contents?.singleColumnBrowseResultsRenderer?.tabs?.get( - 0, - )?.tabRenderer?.content?.sectionListRenderer?.contents?.get( - 0, - )?.gridRenderer?.items - ?: null - if (input != null) { - Log.w("Library", "input: ${input.size}") - val list = parseLibraryPlaylist(input) - emit(list) - } else { - emit(null) - } - } - .onFailure { e -> + YouTube + .getLibraryPlaylists() + .onSuccess { data -> + val input = + data.contents + ?.singleColumnBrowseResultsRenderer + ?.tabs + ?.get( + 0, + )?.tabRenderer + ?.content + ?.sectionListRenderer + ?.contents + ?.get( + 0, + )?.gridRenderer + ?.items + ?: null + if (input != null) { + Log.w("Library", "input: ${input.size}") + val list = parseLibraryPlaylist(input) + emit(list) + } else { + emit(null) + } + }.onFailure { e -> Log.e("Library", "Error: ${e.message}") e.printStackTrace() emit(null) @@ -2487,40 +2914,47 @@ constructor( playlistId: String?, ): Flow> = flow { - YouTube.initPlayback(playback, atr, watchTime, cpn, playlistId).onSuccess { response -> - emit(response) - }.onFailure { - Log.e("InitPlayback", "Error: ${it.message}") - emit(Pair(0, 0f)) - } + YouTube + .initPlayback(playback, atr, watchTime, cpn, playlistId) + .onSuccess { response -> + emit(response) + }.onFailure { + Log.e("InitPlayback", "Error: ${it.message}") + emit(Pair(0, 0f)) + } }.flowOn(Dispatchers.IO) suspend fun getSkipSegments(videoId: String): Flow?> = flow { - YouTube.getSkipSegments(videoId).onSuccess { - emit(it) - }.onFailure { - emit(null) - } + YouTube + .getSkipSegments(videoId) + .onSuccess { + emit(it) + }.onFailure { + emit(null) + } }.flowOn(Dispatchers.IO) fun getFullMetadata(videoId: String): Flow = flow { Log.w("getFullMetadata", "videoId: $videoId") - YouTube.getFullMetadata(videoId).onSuccess { - emit(it) - }.onFailure { - Log.e("getFullMetadata", "Error: ${it.message}") - emit(null) - } + YouTube + .getFullMetadata(videoId) + .onSuccess { + emit(it) + }.onFailure { + Log.e("getFullMetadata", "Error: ${it.message}") + emit(null) + } }.flowOn(Dispatchers.IO) fun checkForUpdate(): Flow = flow { - YouTube.checkForUpdate().onSuccess { - emit(it) - } - .onFailure { + YouTube + .checkForUpdate() + .onSuccess { + emit(it) + }.onFailure { emit(null) } } @@ -2535,77 +2969,104 @@ constructor( id += youtubePlaylistId } Log.d("Repository", "playlist id: $id") - YouTube.customQuery(browseId = id, setLogin = true).onSuccess { result -> - val listContent: ArrayList = arrayListOf() - val data: List? = - result.contents?.singleColumnBrowseResultsRenderer?.tabs?.get( - 0, - )?.tabRenderer?.content?.sectionListRenderer?.contents?.get( - 0, - )?.musicPlaylistShelfRenderer?.contents - if (data != null) { - Log.d("Data", "data: $data") - Log.d("Data", "data size: ${data.size}") - listContent.addAll(data) - } - var continueParam = - result.contents?.singleColumnBrowseResultsRenderer?.tabs?.get( - 0, - )?.tabRenderer?.content?.sectionListRenderer?.contents?.get( - 0, - )?.musicPlaylistShelfRenderer?.continuations?.get( - 0, - )?.nextContinuationData?.continuation - var count = 0 - Log.d("Repository", "playlist data: ${listContent.size}") - Log.d("Repository", "continueParam: $continueParam") - while (continueParam != null) { - YouTube.customQuery( - browseId = "", - continuation = continueParam, - setLogin = true, - ).onSuccess { values -> - Log.d("Continue", "continue: $continueParam") - val dataMore: List? = - values.continuationContents?.musicPlaylistShelfContinuation?.contents - if (dataMore != null) { - listContent.addAll(dataMore) - } - continueParam = - values.continuationContents?.musicPlaylistShelfContinuation?.continuations?.get( + YouTube + .customQuery(browseId = id, setLogin = true) + .onSuccess { result -> + val listContent: ArrayList = arrayListOf() + val data: List? = + result.contents + ?.singleColumnBrowseResultsRenderer + ?.tabs + ?.get( 0, - )?.nextContinuationData?.continuation - count++ - }.onFailure { - Log.e("Continue", "Error: ${it.message}") - continueParam = null - count++ + )?.tabRenderer + ?.content + ?.sectionListRenderer + ?.contents + ?.get( + 0, + )?.musicPlaylistShelfRenderer + ?.contents + if (data != null) { + Log.d("Data", "data: $data") + Log.d("Data", "data size: ${data.size}") + listContent.addAll(data) } - } - Log.d("Repository", "playlist final data: ${listContent.size}") - parseSetVideoId(listContent).let { playlist -> - Log.d("Repository", "playlist final data setVideoId: $playlist") - playlist.forEach { item -> - insertSetVideoId(item) + var continueParam = + result.contents + ?.singleColumnBrowseResultsRenderer + ?.tabs + ?.get( + 0, + )?.tabRenderer + ?.content + ?.sectionListRenderer + ?.contents + ?.get( + 0, + )?.musicPlaylistShelfRenderer + ?.continuations + ?.get( + 0, + )?.nextContinuationData + ?.continuation + var count = 0 + Log.d("Repository", "playlist data: ${listContent.size}") + Log.d("Repository", "continueParam: $continueParam") + while (continueParam != null) { + YouTube + .customQuery( + browseId = "", + continuation = continueParam, + setLogin = true, + ).onSuccess { values -> + Log.d("Continue", "continue: $continueParam") + val dataMore: List? = + values.continuationContents?.musicPlaylistShelfContinuation?.contents + if (dataMore != null) { + listContent.addAll(dataMore) + } + continueParam = + values.continuationContents + ?.musicPlaylistShelfContinuation + ?.continuations + ?.get( + 0, + )?.nextContinuationData + ?.continuation + count++ + }.onFailure { + Log.e("Continue", "Error: ${it.message}") + continueParam = null + count++ + } + } + Log.d("Repository", "playlist final data: ${listContent.size}") + parseSetVideoId(listContent).let { playlist -> + Log.d("Repository", "playlist final data setVideoId: $playlist") + playlist.forEach { item -> + insertSetVideoId(item) + } + emit(playlist) } - emit(playlist) + }.onFailure { e -> + e.printStackTrace() + emit(null) } - }.onFailure { e -> - e.printStackTrace() - emit(null) - } } }.flowOn(Dispatchers.IO) suspend fun createYouTubePlaylist(playlist: LocalPlaylistEntity): Flow = flow { runCatching { - YouTube.createPlaylist(playlist.title, playlist.tracks).onSuccess { - emit(it.playlistId) - }.onFailure { - it.printStackTrace() - emit(null) - } + YouTube + .createPlaylist(playlist.title, playlist.tracks) + .onSuccess { + emit(it.playlistId) + }.onFailure { + it.printStackTrace() + emit(null) + } } } @@ -2615,12 +3076,14 @@ constructor( ): Flow = flow { runCatching { - YouTube.editPlaylist(youtubePlaylistId, title).onSuccess { response -> - emit(response) - }.onFailure { - it.printStackTrace() - emit(0) - } + YouTube + .editPlaylist(youtubePlaylistId, title) + .onSuccess { response -> + emit(response) + }.onFailure { + it.printStackTrace() + emit(0) + } } } @@ -2631,15 +3094,16 @@ constructor( runCatching { getSetVideoId(videoId).collect { setVideoId -> if (setVideoId?.setVideoId != null) { - YouTube.removeItemYouTubePlaylist( - youtubePlaylistId, - videoId, - setVideoId.setVideoId, - ).onSuccess { - emit(it) - }.onFailure { - emit(0) - } + YouTube + .removeItemYouTubePlaylist( + youtubePlaylistId, + videoId, + setVideoId.setVideoId, + ).onSuccess { + emit(it) + }.onFailure { + emit(0) + } } else { emit(0) } @@ -2652,23 +3116,25 @@ constructor( videoId: String, ) = flow { runCatching { - YouTube.addPlaylistItem(youtubePlaylistId, videoId).onSuccess { - if (it.playlistEditResults.isNotEmpty()) { - for (playlistEditResult in it.playlistEditResults) { - insertSetVideoId( - SetVideoIdEntity( - playlistEditResult.playlistEditVideoAddedResultData.videoId, - playlistEditResult.playlistEditVideoAddedResultData.setVideoId, - ), - ) + YouTube + .addPlaylistItem(youtubePlaylistId, videoId) + .onSuccess { + if (it.playlistEditResults.isNotEmpty()) { + for (playlistEditResult in it.playlistEditResults) { + insertSetVideoId( + SetVideoIdEntity( + playlistEditResult.playlistEditVideoAddedResultData.videoId, + playlistEditResult.playlistEditVideoAddedResultData.setVideoId, + ), + ) + } + emit(it.status) + } else { + emit("FAILED") } - emit(it.status) - } else { + }.onFailure { emit("FAILED") } - }.onFailure { - emit("FAILED") - } } } @@ -2679,21 +3145,8 @@ constructor( flow { runCatching { if (YouTube.musixmatchUserToken != null && YouTube.musixmatchUserToken != "") { - YouTube.postMusixmatchCredentials( - email, - password, - YouTube.musixmatchUserToken!!, - ).onSuccess { response -> - emit(response) - }.onFailure { - it.printStackTrace() - emit(null) - } - } else { - YouTube.getMusixmatchUserToken().onSuccess { usertoken -> - YouTube.musixmatchUserToken = usertoken.message.body.user_token - delay(2000) - YouTube.postMusixmatchCredentials( + YouTube + .postMusixmatchCredentials( email, password, YouTube.musixmatchUserToken!!, @@ -2703,8 +3156,24 @@ constructor( it.printStackTrace() emit(null) } - } - .onFailure { throwable -> + } else { + YouTube + .getMusixmatchUserToken() + .onSuccess { usertoken -> + YouTube.musixmatchUserToken = usertoken.message.body.user_token + delay(2000) + YouTube + .postMusixmatchCredentials( + email, + password, + YouTube.musixmatchUserToken!!, + ).onSuccess { response -> + emit(response) + }.onFailure { + it.printStackTrace() + emit(null) + } + }.onFailure { throwable -> throwable.printStackTrace() emit(null) } @@ -2720,17 +3189,18 @@ constructor( ): Flow = flow { runCatching { - YouTube.updateWatchTime( - playbackTrackingVideostatsWatchtimeUrl, - watchTimeList, - cpn, - playlistId, - ).onSuccess { response -> - emit(response) - }.onFailure { - it.printStackTrace() - emit(0) - } + YouTube + .updateWatchTime( + playbackTrackingVideostatsWatchtimeUrl, + watchTimeList, + cpn, + playlistId, + ).onSuccess { response -> + emit(response) + }.onFailure { + it.printStackTrace() + emit(0) + } } }.flowOn(Dispatchers.IO) @@ -2741,12 +3211,14 @@ constructor( ): Flow = flow { runCatching { - YouTube.updateWatchTimeFull(watchTime, cpn, playlistId).onSuccess { response -> - emit(response) - }.onFailure { - it.printStackTrace() - emit(0) - } + YouTube + .updateWatchTimeFull(watchTime, cpn, playlistId) + .onSuccess { response -> + emit(response) + }.onFailure { + it.printStackTrace() + emit(0) + } } }.flowOn(Dispatchers.IO) @@ -2754,13 +3226,15 @@ constructor( flow { if (mediaId != null) { runCatching { - YouTube.addToLiked(mediaId).onSuccess { - Log.d("Liked", "Success: $it") - emit(it) - }.onFailure { - it.printStackTrace() - emit(0) - } + YouTube + .addToLiked(mediaId) + .onSuccess { + Log.d("Liked", "Success: $it") + emit(it) + }.onFailure { + it.printStackTrace() + emit(0) + } } } }.flowOn(Dispatchers.IO) @@ -2769,13 +3243,15 @@ constructor( flow { if (mediaId != null) { runCatching { - YouTube.removeFromLiked(mediaId).onSuccess { - Log.d("Liked", "Success: $it") - emit(it) - }.onFailure { - it.printStackTrace() - emit(0) - } + YouTube + .removeFromLiked(mediaId) + .onSuccess { + Log.d("Liked", "Success: $it") + emit(it) + }.onFailure { + it.printStackTrace() + emit(0) + } } } }.flowOn(Dispatchers.IO) diff --git a/app/src/main/java/com/maxrave/simpmusic/di/DatabaseModule.kt b/app/src/main/java/com/maxrave/simpmusic/di/DatabaseModule.kt new file mode 100644 index 00000000..dbcb7e55 --- /dev/null +++ b/app/src/main/java/com/maxrave/simpmusic/di/DatabaseModule.kt @@ -0,0 +1,132 @@ +package com.maxrave.simpmusic.di + +import android.util.Log +import androidx.core.content.contentValuesOf +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.Preferences +import androidx.room.OnConflictStrategy +import androidx.room.Room +import androidx.room.RoomDatabase +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase +import com.google.common.reflect.TypeToken +import com.google.gson.Gson +import com.maxrave.simpmusic.common.DB_NAME +import com.maxrave.simpmusic.data.dataStore.DataStoreManager +import com.maxrave.simpmusic.data.db.Converters +import com.maxrave.simpmusic.data.db.DatabaseDao +import com.maxrave.simpmusic.data.db.LocalDataSource +import com.maxrave.simpmusic.data.db.MusicDatabase +import com.maxrave.simpmusic.data.db.entities.PairSongLocalPlaylist +import com.maxrave.simpmusic.data.repository.MainRepository +import com.maxrave.simpmusic.extension.dataStore +import com.maxrave.simpmusic.extension.toSQLiteQuery +import com.maxrave.simpmusic.service.test.notification.NotifyWork +import org.koin.android.ext.koin.androidContext +import org.koin.androidx.workmanager.dsl.workerOf +import org.koin.dsl.module +import java.lang.reflect.Type +import java.time.ZoneOffset + +val databaseModule = + module { + // Database + single(createdAtStart = true) { + Room + .databaseBuilder(androidContext(), MusicDatabase::class.java, DB_NAME) + .addTypeConverter(Converters()) + .addMigrations( + object : Migration(5, 6) { + override fun migrate(db: SupportSQLiteDatabase) { + val playlistSongMaps = mutableListOf() + db.query("SELECT * FROM local_playlist".toSQLiteQuery()).use { cursor -> + while (cursor.moveToNext()) { + val input = cursor.getString(8) + if (input != null) { + val listType: Type = + object : TypeToken?>() {}.type + val tracks = Gson().fromJson?>(input, listType) + Log.w("MIGRATION_5_6", "tracks: $tracks") + tracks?.mapIndexed { index, track -> + if (track != null) { + playlistSongMaps.add( + PairSongLocalPlaylist( + playlistId = cursor.getLong(0), + songId = track, + position = index, + ), + ) + } + } + } + } + } + db.execSQL("ALTER TABLE `format` ADD COLUMN `lengthSeconds` INTEGER DEFAULT NULL") + db.execSQL("ALTER TABLE `format` ADD COLUMN `youtubeCaptionsUrl` TEXT DEFAULT NULL") + db.execSQL("ALTER TABLE `format` ADD COLUMN `cpn` TEXT DEFAULT NULL") + db.execSQL( + "CREATE TABLE IF NOT EXISTS `pair_song_local_playlist` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `playlistId` INTEGER NOT NULL, `songId` TEXT NOT NULL, `position` INTEGER NOT NULL, `inPlaylist` INTEGER NOT NULL, FOREIGN KEY(`playlistId`) REFERENCES `local_playlist`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`songId`) REFERENCES `song`(`videoId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + ) + db.execSQL( + "CREATE INDEX IF NOT EXISTS `index_pair_song_local_playlist_playlistId` ON `pair_song_local_playlist` (`playlistId`)", + ) + db.execSQL("CREATE INDEX IF NOT EXISTS `index_pair_song_local_playlist_songId` ON `pair_song_local_playlist` (`songId`)") + playlistSongMaps.forEach { pair -> + db.insert( + table = "pair_song_local_playlist", + conflictAlgorithm = OnConflictStrategy.IGNORE, + values = + contentValuesOf( + "playlistId" to pair.playlistId, + "songId" to pair.songId, + "position" to pair.position, + "inPlaylist" to + pair.inPlaylist + .atZone(ZoneOffset.UTC) + .toInstant() + .toEpochMilli(), + ), + ) + } + } + }, + ).addCallback( + object : RoomDatabase.Callback() { + override fun onOpen(db: SupportSQLiteDatabase) { + super.onOpen(db) + db.execSQL( + "CREATE TRIGGER IF NOT EXISTS on_delete_pair_song_local_playlist AFTER DELETE ON pair_song_local_playlist\n" + + "FOR EACH ROW\n" + + "BEGIN\n" + + " UPDATE pair_song_local_playlist\n" + + " SET position = position - 1\n" + + " WHERE playlistId = OLD.playlistId AND position > OLD.position;\n" + + "END;", + ) + } + }, + ).build() + } + // DatabaseDao + single(createdAtStart = true) { + get().getDatabaseDao() + } + // LocalDataSource + single(createdAtStart = true) { + LocalDataSource(get()) + } + // Datastore + single(createdAtStart = true) { + androidContext().dataStore + } + // DatastoreManager + single(createdAtStart = true) { + DataStoreManager(get>()) + } + // MainRepository + single(createdAtStart = true) { + MainRepository(get(), get(), androidContext()) + } + // Notification Worker + workerOf(::NotifyWork) + } \ No newline at end of file diff --git a/app/src/main/java/com/maxrave/simpmusic/di/LocalServiceModule.kt b/app/src/main/java/com/maxrave/simpmusic/di/LocalServiceModule.kt deleted file mode 100644 index 634f9b61..00000000 --- a/app/src/main/java/com/maxrave/simpmusic/di/LocalServiceModule.kt +++ /dev/null @@ -1,130 +0,0 @@ -package com.maxrave.simpmusic.di - -import android.content.Context -import android.util.Log -import androidx.core.content.contentValuesOf -import androidx.datastore.core.DataStore -import androidx.datastore.preferences.core.Preferences -import androidx.room.OnConflictStrategy -import androidx.room.Room -import androidx.room.RoomDatabase -import androidx.room.migration.Migration -import androidx.sqlite.db.SupportSQLiteDatabase -import com.google.common.reflect.TypeToken -import com.google.gson.Gson -import com.maxrave.simpmusic.common.DB_NAME -import com.maxrave.simpmusic.data.dataStore.DataStoreManager -import com.maxrave.simpmusic.data.db.Converters -import com.maxrave.simpmusic.data.db.DatabaseDao -import com.maxrave.simpmusic.data.db.MusicDatabase -import com.maxrave.simpmusic.data.db.entities.PairSongLocalPlaylist -import com.maxrave.simpmusic.extension.dataStore -import com.maxrave.simpmusic.extension.toSQLiteQuery -import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn -import dagger.hilt.android.qualifiers.ApplicationContext -import dagger.hilt.components.SingletonComponent -import java.lang.reflect.Type -import java.time.ZoneOffset -import javax.inject.Singleton - -@Module -@InstallIn(SingletonComponent::class) -object LocalServiceModule { - @Provides - @Singleton - fun provideMusicDatabase( - @ApplicationContext context: Context, - ): MusicDatabase = - Room.databaseBuilder(context, MusicDatabase::class.java, DB_NAME) - .addTypeConverter(Converters()) - .addMigrations( - object : Migration(5, 6) { - override fun migrate(db: SupportSQLiteDatabase) { - val playlistSongMaps = mutableListOf() - db.query("SELECT * FROM local_playlist".toSQLiteQuery()).use { cursor -> - while (cursor.moveToNext()) { - val input = cursor.getString(8) - if (input != null) { - val listType: Type = - object : TypeToken?>() {}.type - val tracks = Gson().fromJson?>(input, listType) - Log.w("MIGRATION_5_6", "tracks: $tracks") - tracks?.mapIndexed { index, track -> - if (track != null) { - playlistSongMaps.add( - PairSongLocalPlaylist( - playlistId = cursor.getLong(0), - songId = track, - position = index, - ), - ) - } - } - } - } - } - db.execSQL("ALTER TABLE `format` ADD COLUMN `lengthSeconds` INTEGER DEFAULT NULL") - db.execSQL("ALTER TABLE `format` ADD COLUMN `youtubeCaptionsUrl` TEXT DEFAULT NULL") - db.execSQL("ALTER TABLE `format` ADD COLUMN `cpn` TEXT DEFAULT NULL") - db.execSQL( - "CREATE TABLE IF NOT EXISTS `pair_song_local_playlist` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `playlistId` INTEGER NOT NULL, `songId` TEXT NOT NULL, `position` INTEGER NOT NULL, `inPlaylist` INTEGER NOT NULL, FOREIGN KEY(`playlistId`) REFERENCES `local_playlist`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`songId`) REFERENCES `song`(`videoId`) ON UPDATE NO ACTION ON DELETE CASCADE )", - ) - db.execSQL( - "CREATE INDEX IF NOT EXISTS `index_pair_song_local_playlist_playlistId` ON `pair_song_local_playlist` (`playlistId`)", - ) - db.execSQL("CREATE INDEX IF NOT EXISTS `index_pair_song_local_playlist_songId` ON `pair_song_local_playlist` (`songId`)") - playlistSongMaps.forEach { pair -> - db.insert( - table = "pair_song_local_playlist", - conflictAlgorithm = OnConflictStrategy.IGNORE, - values = - contentValuesOf( - "playlistId" to pair.playlistId, - "songId" to pair.songId, - "position" to pair.position, - "inPlaylist" to - pair.inPlaylist.atZone(ZoneOffset.UTC).toInstant() - .toEpochMilli(), - ), - ) - } - } - }, - ) - .addCallback( - object : RoomDatabase.Callback() { - override fun onOpen(db: SupportSQLiteDatabase) { - super.onOpen(db) - db.execSQL( - "CREATE TRIGGER IF NOT EXISTS on_delete_pair_song_local_playlist AFTER DELETE ON pair_song_local_playlist\n" + - "FOR EACH ROW\n" + - "BEGIN\n" + - " UPDATE pair_song_local_playlist\n" + - " SET position = position - 1\n" + - " WHERE playlistId = OLD.playlistId AND position > OLD.position;\n" + - "END;", - ) - } - }, - ) - .build() - - @Provides - @Singleton - fun provideDatabaseDao(musicDatabase: MusicDatabase): DatabaseDao = musicDatabase.getDatabaseDao() - - @Provides - @Singleton - fun provideDatastore( - @ApplicationContext context: Context, - ): DataStore = context.dataStore - - @Provides - @Singleton - fun provideDatastoreManager(settingsDataStore: DataStore): DataStoreManager = - DataStoreManager( - settingsDataStore, - ) -} \ No newline at end of file diff --git a/app/src/main/java/com/maxrave/simpmusic/di/MediaServiceModule.kt b/app/src/main/java/com/maxrave/simpmusic/di/MediaServiceModule.kt new file mode 100644 index 00000000..4361414e --- /dev/null +++ b/app/src/main/java/com/maxrave/simpmusic/di/MediaServiceModule.kt @@ -0,0 +1,70 @@ +package com.maxrave.simpmusic.di + +import androidx.media3.common.util.UnstableApi +import androidx.media3.database.DatabaseProvider +import androidx.media3.database.StandaloneDatabaseProvider +import androidx.media3.datasource.cache.LeastRecentlyUsedCacheEvictor +import androidx.media3.datasource.cache.NoOpCacheEvictor +import androidx.media3.datasource.cache.SimpleCache +import com.maxrave.simpmusic.common.Config.CANVAS_CACHE +import com.maxrave.simpmusic.common.Config.DOWNLOAD_CACHE +import com.maxrave.simpmusic.common.Config.PLAYER_CACHE +import com.maxrave.simpmusic.data.dataStore.DataStoreManager +import com.maxrave.simpmusic.data.repository.MainRepository +import com.maxrave.simpmusic.service.SimpleMediaSessionCallback +import com.maxrave.simpmusic.service.test.download.DownloadUtils +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.runBlocking +import org.koin.android.ext.koin.androidContext +import org.koin.core.qualifier.named +import org.koin.dsl.module + +@UnstableApi +val mediaServiceModule = + module { + // Cache + single(createdAtStart = true) { + StandaloneDatabaseProvider(androidContext()) + } + // Player Cache + single(createdAtStart = true, qualifier = named(PLAYER_CACHE)) { + SimpleCache( + androidContext().filesDir.resolve("exoplayer"), + when (val cacheSize = runBlocking { get().maxSongCacheSize.first() }) { + -1 -> NoOpCacheEvictor() + else -> LeastRecentlyUsedCacheEvictor(cacheSize * 1024 * 1024L) + }, + get(), + ) + } + // Download Cache + single(createdAtStart = true, qualifier = named(DOWNLOAD_CACHE)) { + SimpleCache( + androidContext().filesDir.resolve("download"), + NoOpCacheEvictor(), + get(), + ) + } + // Spotify Canvas Cache + single(createdAtStart = true, qualifier = named(CANVAS_CACHE)) { + SimpleCache( + androidContext().filesDir.resolve("spotifyCanvas"), + NoOpCacheEvictor(), + get(), + ) + } + // MediaSession Callback for main player + single(createdAtStart = true) { + SimpleMediaSessionCallback(androidContext(), get()) + } + // DownloadUtils + single(createdAtStart = true) { + DownloadUtils( + context = androidContext(), + playerCache = get(named(PLAYER_CACHE)), + downloadCache = get(named(DOWNLOAD_CACHE)), + mainRepository = get(), + databaseProvider = get(), + ) + } + } \ No newline at end of file diff --git a/app/src/main/java/com/maxrave/simpmusic/di/MusicServiceModule.kt b/app/src/main/java/com/maxrave/simpmusic/di/MusicServiceModule.kt deleted file mode 100644 index 03aebce7..00000000 --- a/app/src/main/java/com/maxrave/simpmusic/di/MusicServiceModule.kt +++ /dev/null @@ -1,73 +0,0 @@ -package com.maxrave.simpmusic.di - -import android.content.Context -import androidx.media3.common.util.UnstableApi -import androidx.media3.database.DatabaseProvider -import androidx.media3.database.StandaloneDatabaseProvider -import androidx.media3.datasource.cache.LeastRecentlyUsedCacheEvictor -import androidx.media3.datasource.cache.NoOpCacheEvictor -import androidx.media3.datasource.cache.SimpleCache -import com.maxrave.simpmusic.data.dataStore.DataStoreManager -import com.maxrave.simpmusic.data.repository.MainRepository -import com.maxrave.simpmusic.service.SimpleMediaSessionCallback -import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn -import dagger.hilt.android.qualifiers.ApplicationContext -import dagger.hilt.components.SingletonComponent -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.runBlocking -import javax.inject.Qualifier -import javax.inject.Singleton - -@Qualifier -@Retention(AnnotationRetention.BINARY) -annotation class PlayerCache - -@Qualifier -@Retention(AnnotationRetention.BINARY) -annotation class DownloadCache - -@Module -@InstallIn(SingletonComponent::class) -@UnstableApi -object MusicServiceModule { - @Singleton - @Provides - fun provideDatabaseProvider(@ApplicationContext context: Context): DatabaseProvider = - StandaloneDatabaseProvider(context) - - @Singleton - @Provides - @PlayerCache - fun providePlayerCache( - @ApplicationContext context: Context, - databaseProvider: DatabaseProvider, - dataStoreManager: DataStoreManager - ): SimpleCache = - SimpleCache( - context.filesDir.resolve("exoplayer"), - when (val cacheSize = runBlocking { dataStoreManager.maxSongCacheSize.first() }) { - -1 -> NoOpCacheEvictor() - else -> LeastRecentlyUsedCacheEvictor(cacheSize * 1024 * 1024L) - }, - databaseProvider - ) - - @Singleton - @Provides - @DownloadCache - fun provideDownloadCache( - @ApplicationContext context: Context, - databaseProvider: DatabaseProvider - ): SimpleCache = - SimpleCache(context.filesDir.resolve("download"), NoOpCacheEvictor(), databaseProvider) - - - @Provides - @Singleton - fun provideMediaSessionCallback( - @ApplicationContext context: Context, - mainRepository: MainRepository - ): SimpleMediaSessionCallback = SimpleMediaSessionCallback(context, mainRepository) -} \ No newline at end of file diff --git a/app/src/main/java/com/maxrave/simpmusic/service/SimpleMediaService.kt b/app/src/main/java/com/maxrave/simpmusic/service/SimpleMediaService.kt index 2de30923..10c34e6b 100644 --- a/app/src/main/java/com/maxrave/simpmusic/service/SimpleMediaService.kt +++ b/app/src/main/java/com/maxrave/simpmusic/service/SimpleMediaService.kt @@ -1,6 +1,5 @@ package com.maxrave.simpmusic.service - import android.app.PendingIntent import android.content.ComponentName import android.content.Context @@ -37,16 +36,14 @@ import androidx.media3.session.MediaSession import androidx.media3.session.SessionToken import com.google.common.util.concurrent.MoreExecutors import com.maxrave.simpmusic.R +import com.maxrave.simpmusic.common.Config import com.maxrave.simpmusic.common.MEDIA_NOTIFICATION import com.maxrave.simpmusic.data.dataStore.DataStoreManager import com.maxrave.simpmusic.data.repository.MainRepository -import com.maxrave.simpmusic.di.DownloadCache -import com.maxrave.simpmusic.di.PlayerCache import com.maxrave.simpmusic.service.test.CoilBitmapLoader import com.maxrave.simpmusic.service.test.source.MergingMediaSourceFactory import com.maxrave.simpmusic.ui.MainActivity import com.maxrave.simpmusic.ui.widget.BasicWidget -import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob @@ -54,33 +51,24 @@ import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.cancellable import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking -import javax.inject.Inject - +import org.koin.android.ext.android.inject +import org.koin.core.qualifier.named -@AndroidEntryPoint @UnstableApi class SimpleMediaService : MediaLibraryService() { - lateinit var player: ExoPlayer lateinit var mediaSession: MediaLibrarySession - @Inject - lateinit var dataStoreManager: DataStoreManager + private val dataStoreManager: DataStoreManager by inject() - @Inject - lateinit var mainRepository: MainRepository + private val mainRepository: MainRepository by inject() - @Inject - @PlayerCache - lateinit var playerCache: SimpleCache + private val playerCache: SimpleCache by inject(named(Config.PLAYER_CACHE)) - @Inject - @DownloadCache - lateinit var downloadCache: SimpleCache + private val downloadCache: SimpleCache by inject(named(Config.DOWNLOAD_CACHE)) - @Inject - lateinit var simpleMediaSessionCallback: SimpleMediaSessionCallback + private val simpleMediaSessionCallback: SimpleMediaSessionCallback by inject() lateinit var simpleMediaServiceHandler: SimpleMediaServiceHandler @@ -97,51 +85,59 @@ class SimpleMediaService : MediaLibraryService() { this, { MEDIA_NOTIFICATION.NOTIFICATION_ID }, MEDIA_NOTIFICATION.NOTIFICATION_CHANNEL_ID, - R.string.notification_channel_name - ) - .apply { - setSmallIcon(R.drawable.mono) - } + R.string.notification_channel_name, + ).apply { + setSmallIcon(R.drawable.mono) + }, ) - player = ExoPlayer.Builder(this) - .setAudioAttributes(provideAudioAttributes(), true) - .setWakeMode(C.WAKE_MODE_NETWORK) - .setHandleAudioBecomingNoisy(true) - .setSeekForwardIncrementMs(5000) - .setSeekBackIncrementMs(5000) - .setMediaSourceFactory(provideMergingMediaSource( - downloadCache, - playerCache, - mainRepository, + player = + ExoPlayer + .Builder(this) + .setAudioAttributes(provideAudioAttributes(), true) + .setWakeMode(C.WAKE_MODE_NETWORK) + .setHandleAudioBecomingNoisy(true) + .setSeekForwardIncrementMs(5000) + .setSeekBackIncrementMs(5000) + .setMediaSourceFactory( + provideMergingMediaSource( + downloadCache, + playerCache, + mainRepository, + serviceCoroutineScope, + dataStoreManager, + ), + ).setRenderersFactory(provideRendererFactory(this)) + .build() + + mediaSession = + provideMediaLibrarySession( + this, + this, + player, + simpleMediaSessionCallback, serviceCoroutineScope, - dataStoreManager - )) - .setRenderersFactory(provideRendererFactory(this)) - .build() - - mediaSession = provideMediaLibrarySession( - this, - this, - player, - simpleMediaSessionCallback, - serviceCoroutineScope - ) + ) val sessionToken = SessionToken(this, ComponentName(this, SimpleMediaService::class.java)) val controllerFuture = MediaController.Builder(this, sessionToken).buildAsync() controllerFuture.addListener({ controllerFuture.get() }, MoreExecutors.directExecutor()) - simpleMediaServiceHandler = SimpleMediaServiceHandler( - player = player, - mediaSession = mediaSession, - mediaSessionCallback = simpleMediaSessionCallback, - dataStoreManager = dataStoreManager, - mainRepository = mainRepository, - coroutineScope = serviceCoroutineScope, - context = application.applicationContext - ) + simpleMediaServiceHandler = + SimpleMediaServiceHandler( + player = player, + mediaSession = mediaSession, + mediaSessionCallback = simpleMediaSessionCallback, + dataStoreManager = dataStoreManager, + mainRepository = mainRepository, + coroutineScope = serviceCoroutineScope, + context = application.applicationContext, + ) } @UnstableApi - override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + override fun onStartCommand( + intent: Intent?, + flags: Int, + startId: Int, + ): Int { if (intent != null && intent.action != null) { when (intent.action) { BasicWidget.ACTION_TOGGLE_PAUSE -> { @@ -160,13 +156,14 @@ class SimpleMediaService : MediaLibraryService() { return super.onStartCommand(intent, flags, startId) } - override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaLibrarySession = - mediaSession + override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaLibrarySession = mediaSession @UnstableApi - override fun onUpdateNotification(session: MediaSession, startInForegroundRequired: Boolean) { + override fun onUpdateNotification( + session: MediaSession, + startInForegroundRequired: Boolean, + ) { super.onUpdateNotification(session, startInForegroundRequired) - } @UnstableApi @@ -201,54 +198,56 @@ class SimpleMediaService : MediaLibraryService() { get() = this@SimpleMediaService } - override fun onBind(intent: Intent?): IBinder = - super.onBind(intent) ?: binder + override fun onBind(intent: Intent?): IBinder = super.onBind(intent) ?: binder private fun provideAudioAttributes(): AudioAttributes = - AudioAttributes.Builder() + AudioAttributes + .Builder() .setContentType(C.AUDIO_CONTENT_TYPE_MUSIC) .setUsage(C.USAGE_MEDIA) .build() @UnstableApi fun provideCacheDataSource( - @DownloadCache downloadCache: SimpleCache, - @PlayerCache playerCache: SimpleCache - ): CacheDataSource.Factory { - return CacheDataSource.Factory() + downloadCache: SimpleCache, + playerCache: SimpleCache, + ): CacheDataSource.Factory = + CacheDataSource + .Factory() .setCache(downloadCache) .setUpstreamDataSourceFactory( - CacheDataSource.Factory() + CacheDataSource + .Factory() .setCache(playerCache) .setUpstreamDataSourceFactory( - DefaultHttpDataSource.Factory() + DefaultHttpDataSource + .Factory() .setAllowCrossProtocolRedirects(true) .setUserAgent("Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36") - .setConnectTimeoutMs(5000) - ) - ) - .setCacheWriteDataSinkFactory(null) + .setConnectTimeoutMs(5000), + ), + ).setCacheWriteDataSinkFactory(null) .setFlags(CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR) - } @UnstableApi fun provideResolvingDataSourceFactory( cacheDataSourceFactory: CacheDataSource.Factory, - @DownloadCache downloadCache: SimpleCache, - @PlayerCache playerCache: SimpleCache, + downloadCache: SimpleCache, + playerCache: SimpleCache, mainRepository: MainRepository, - coroutineScope: CoroutineScope + coroutineScope: CoroutineScope, ): DataSource.Factory { return ResolvingDataSource.Factory(cacheDataSourceFactory) { dataSpec -> val mediaId = dataSpec.key ?: error("No media id") Log.w("Stream", mediaId) Log.w("Stream", mediaId.startsWith(MergingMediaSourceFactory.isVideo).toString()) - val CHUNK_LENGTH = 512 * 1024L + val chunkLength = 512 * 1024L if (downloadCache.isCached( mediaId, dataSpec.position, - if (dataSpec.length >= 0) dataSpec.length else 1 - ) || playerCache.isCached(mediaId, dataSpec.position, CHUNK_LENGTH) + if (dataSpec.length >= 0) dataSpec.length else 1, + ) || + playerCache.isCached(mediaId, dataSpec.position, chunkLength) ) { coroutineScope.launch(Dispatchers.IO) { mainRepository.updateFormat( @@ -256,7 +255,7 @@ class SimpleMediaService : MediaLibraryService() { mediaId.removePrefix(MergingMediaSourceFactory.isVideo) } else { mediaId - } + }, ) } return@Factory dataSpec @@ -265,22 +264,27 @@ class SimpleMediaService : MediaLibraryService() { runBlocking(Dispatchers.IO) { if (mediaId.contains(MergingMediaSourceFactory.isVideo)) { val id = mediaId.removePrefix(MergingMediaSourceFactory.isVideo) - mainRepository.getStream( - id, true - ).cancellable().collect { - if (it != null) { - dataSpecReturn = dataSpec.withUri(it.toUri()) + mainRepository + .getStream( + id, + true, + ).cancellable() + .collect { + if (it != null) { + dataSpecReturn = dataSpec.withUri(it.toUri()) + } } - } - } - else { - mainRepository.getStream( - mediaId, isVideo = false - ).cancellable().collect { - if (it != null) { - dataSpecReturn = dataSpec.withUri(it.toUri()) + } else { + mainRepository + .getStream( + mediaId, + isVideo = false, + ).cancellable() + .collect { + if (it != null) { + dataSpecReturn = dataSpec.withUri(it.toUri()) + } } - } } } return@Factory dataSpecReturn @@ -288,26 +292,27 @@ class SimpleMediaService : MediaLibraryService() { } @UnstableApi - fun provideExtractorFactory(): ExtractorsFactory = ExtractorsFactory { - arrayOf( - MatroskaExtractor( - DefaultSubtitleParserFactory() - ), - FragmentedMp4Extractor( - DefaultSubtitleParserFactory() - ), - androidx.media3.extractor.mp4.Mp4Extractor( - DefaultSubtitleParserFactory() - ), - ) - } + fun provideExtractorFactory(): ExtractorsFactory = + ExtractorsFactory { + arrayOf( + MatroskaExtractor( + DefaultSubtitleParserFactory(), + ), + FragmentedMp4Extractor( + DefaultSubtitleParserFactory(), + ), + androidx.media3.extractor.mp4.Mp4Extractor( + DefaultSubtitleParserFactory(), + ), + ) + } @UnstableApi fun provideMediaSourceFactory( downloadCache: SimpleCache, playerCache: SimpleCache, mainRepository: MainRepository, - coroutineScope: CoroutineScope + coroutineScope: CoroutineScope, ): DefaultMediaSourceFactory = DefaultMediaSourceFactory( provideResolvingDataSourceFactory( @@ -315,9 +320,9 @@ class SimpleMediaService : MediaLibraryService() { downloadCache, playerCache, mainRepository, - coroutineScope + coroutineScope, ), - provideExtractorFactory() + provideExtractorFactory(), ) private fun provideMergingMediaSource( @@ -325,16 +330,17 @@ class SimpleMediaService : MediaLibraryService() { playerCache: SimpleCache, mainRepository: MainRepository, coroutineScope: CoroutineScope, - dataStoreManager: DataStoreManager - ): MergingMediaSourceFactory = MergingMediaSourceFactory( - provideMediaSourceFactory( - downloadCache, - playerCache, - mainRepository, - coroutineScope - ), - dataStoreManager - ) + dataStoreManager: DataStoreManager, + ): MergingMediaSourceFactory = + MergingMediaSourceFactory( + provideMediaSourceFactory( + downloadCache, + playerCache, + mainRepository, + coroutineScope, + ), + dataStoreManager, + ) @UnstableApi fun provideRendererFactory(context: Context): DefaultRenderersFactory = @@ -342,30 +348,32 @@ class SimpleMediaService : MediaLibraryService() { override fun buildAudioSink( context: Context, enableFloatOutput: Boolean, - enableAudioTrackPlaybackParams: Boolean - ): AudioSink { - return DefaultAudioSink.Builder(context) + enableAudioTrackPlaybackParams: Boolean, + ): AudioSink = + DefaultAudioSink + .Builder(context) .setEnableFloatOutput(enableFloatOutput) .setEnableAudioTrackPlaybackParams(enableAudioTrackPlaybackParams) .setAudioProcessorChain( DefaultAudioSink.DefaultAudioProcessorChain( emptyArray(), SilenceSkippingAudioProcessor( - 2_000_000, (20_000/2_000_000).toFloat(), + 2_000_000, + (20_000 / 2_000_000).toFloat(), 2_000_000, 0, - 256 + 256, ), - SonicAudioProcessor() - ) - ) - .build() - } + SonicAudioProcessor(), + ), + ).build() } @UnstableApi - fun provideCoilBitmapLoader(context: Context, coroutineScope: CoroutineScope): CoilBitmapLoader = CoilBitmapLoader(context, coroutineScope) - + fun provideCoilBitmapLoader( + context: Context, + coroutineScope: CoroutineScope, + ): CoilBitmapLoader = CoilBitmapLoader(context, coroutineScope) @UnstableApi fun provideMediaLibrarySession( @@ -373,18 +381,20 @@ class SimpleMediaService : MediaLibraryService() { service: MediaLibraryService, player: ExoPlayer, callback: SimpleMediaSessionCallback, - coroutineScope: CoroutineScope - ): MediaLibrarySession = MediaLibrarySession.Builder( - service, player, callback - ) - .setSessionActivity( - PendingIntent.getActivity( - context, 0, Intent(context, MainActivity::class.java), - PendingIntent.FLAG_IMMUTABLE - ) - ) - .setBitmapLoader(provideCoilBitmapLoader(context, coroutineScope)) - .build() - -} - + coroutineScope: CoroutineScope, + ): MediaLibrarySession = + MediaLibrarySession + .Builder( + service, + player, + callback, + ).setSessionActivity( + PendingIntent.getActivity( + context, + 0, + Intent(context, MainActivity::class.java), + PendingIntent.FLAG_IMMUTABLE, + ), + ).setBitmapLoader(provideCoilBitmapLoader(context, coroutineScope)) + .build() +} \ No newline at end of file diff --git a/app/src/main/java/com/maxrave/simpmusic/service/SimpleMediaSessionCallback.kt b/app/src/main/java/com/maxrave/simpmusic/service/SimpleMediaSessionCallback.kt index cf90cfff..97d9d54f 100644 --- a/app/src/main/java/com/maxrave/simpmusic/service/SimpleMediaSessionCallback.kt +++ b/app/src/main/java/com/maxrave/simpmusic/service/SimpleMediaSessionCallback.kt @@ -35,20 +35,16 @@ import com.maxrave.simpmusic.extension.toPlaylistEntity import com.maxrave.simpmusic.extension.toSongEntity import com.maxrave.simpmusic.extension.toTrack import com.maxrave.simpmusic.utils.Resource -import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.flow.first import kotlinx.coroutines.guava.future -import javax.inject.Inject -class SimpleMediaSessionCallback - @Inject - constructor( - @ApplicationContext private val context: Context, - private val mainRepository: MainRepository, - ) : MediaLibrarySession.Callback { +class SimpleMediaSessionCallback( + private val context: Context, + private val mainRepository: MainRepository, +) : MediaLibrarySession.Callback { var toggleLike: () -> Unit = {} private val scope = CoroutineScope(Dispatchers.Main + Job()) val searchTempList = mutableListOf() diff --git a/app/src/main/java/com/maxrave/simpmusic/service/test/download/DownloadUtils.kt b/app/src/main/java/com/maxrave/simpmusic/service/test/download/DownloadUtils.kt index 124016d6..65aad23e 100644 --- a/app/src/main/java/com/maxrave/simpmusic/service/test/download/DownloadUtils.kt +++ b/app/src/main/java/com/maxrave/simpmusic/service/test/download/DownloadUtils.kt @@ -17,11 +17,8 @@ import androidx.media3.exoplayer.offline.DownloadRequest import androidx.media3.exoplayer.offline.DownloadService import com.maxrave.simpmusic.common.DownloadState import com.maxrave.simpmusic.data.repository.MainRepository -import com.maxrave.simpmusic.di.DownloadCache -import com.maxrave.simpmusic.di.PlayerCache import com.maxrave.simpmusic.service.test.download.MusicDownloadService.Companion.CHANNEL_ID import com.maxrave.simpmusic.service.test.source.MergingMediaSourceFactory -import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob @@ -33,15 +30,12 @@ import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import java.util.concurrent.Executors -import javax.inject.Inject -import javax.inject.Singleton @UnstableApi -@Singleton -class DownloadUtils @Inject constructor( - @ApplicationContext private val context: Context, - @PlayerCache private val playerCache: SimpleCache, - @DownloadCache private val downloadCache: SimpleCache, +class DownloadUtils ( + private val context: Context, + private val playerCache: SimpleCache, + private val downloadCache: SimpleCache, private val mainRepository: MainRepository, databaseProvider: DatabaseProvider ) { @@ -61,12 +55,12 @@ class DownloadUtils @Inject constructor( val mediaId = dataSpec.key ?: error("No media id") Log.w("Stream", mediaId) Log.w("Stream", mediaId.startsWith(MergingMediaSourceFactory.isVideo).toString()) - val CHUNK_LENGTH = 512 * 1024L + val chunkLength = 512 * 1024L if (downloadCache.isCached( mediaId, dataSpec.position, if (dataSpec.length >= 0) dataSpec.length else 1 - ) || playerCache.isCached(mediaId, dataSpec.position, CHUNK_LENGTH) + ) || playerCache.isCached(mediaId, dataSpec.position, chunkLength) ) { return@Factory dataSpec } diff --git a/app/src/main/java/com/maxrave/simpmusic/service/test/download/MusicDownloadService.kt b/app/src/main/java/com/maxrave/simpmusic/service/test/download/MusicDownloadService.kt index edb5668a..8ae7deac 100644 --- a/app/src/main/java/com/maxrave/simpmusic/service/test/download/MusicDownloadService.kt +++ b/app/src/main/java/com/maxrave/simpmusic/service/test/download/MusicDownloadService.kt @@ -12,11 +12,9 @@ import androidx.media3.exoplayer.offline.DownloadService import androidx.media3.exoplayer.scheduler.PlatformScheduler import androidx.media3.exoplayer.scheduler.Scheduler import com.maxrave.simpmusic.R -import dagger.hilt.android.AndroidEntryPoint -import javax.inject.Inject +import org.koin.android.ext.android.inject @UnstableApi -@AndroidEntryPoint class MusicDownloadService : DownloadService( NOTIFICATION_ID, 1000L, @@ -24,8 +22,7 @@ class MusicDownloadService : DownloadService( R.string.download, 0 ) { - @Inject - lateinit var downloadUtil: DownloadUtils + private val downloadUtil: DownloadUtils by inject() override fun getDownloadManager() = downloadUtil.downloadManager diff --git a/app/src/main/java/com/maxrave/simpmusic/service/test/notification/NotifyWork.kt b/app/src/main/java/com/maxrave/simpmusic/service/test/notification/NotifyWork.kt index a1c28147..2d91e850 100644 --- a/app/src/main/java/com/maxrave/simpmusic/service/test/notification/NotifyWork.kt +++ b/app/src/main/java/com/maxrave/simpmusic/service/test/notification/NotifyWork.kt @@ -2,7 +2,6 @@ package com.maxrave.simpmusic.service.test.notification import android.content.Context import android.util.Log -import androidx.hilt.work.HiltWorker import androidx.work.CoroutineWorker import androidx.work.WorkerParameters import com.maxrave.kotlinytmusicscraper.models.AlbumItem @@ -14,24 +13,19 @@ import com.maxrave.simpmusic.data.model.browse.artist.ResultSingle import com.maxrave.simpmusic.data.repository.MainRepository import com.maxrave.simpmusic.extension.symmetricDifference import com.maxrave.simpmusic.viewModel.MoreAlbumsViewModel -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.first import kotlinx.coroutines.withContext +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject import java.time.LocalDateTime -import javax.inject.Inject -@HiltWorker -class NotifyWork - @AssistedInject - constructor( - @Assisted context: Context, - @Assisted params: WorkerParameters, - ) : CoroutineWorker(context, params) { - @Inject - lateinit var mainRepository: MainRepository +class NotifyWork( + context: Context, + params: WorkerParameters, + ) : CoroutineWorker(context, params), KoinComponent { + val mainRepository: MainRepository by inject() private val mapOfNotification = arrayListOf() @@ -44,7 +38,10 @@ class NotifyWork Log.w("NotifyWork", "doWork: $artistList") Log.w("NotifyWork", "doWork: $listFollowedArtistSingleAndAlbum") artistList.forEach { art -> - combine(mainRepository.getAlbumMore("MPAD${art.channelId}", MoreAlbumsViewModel.ALBUM_PARAM), mainRepository.getAlbumMore("MPAD${art.channelId}", MoreAlbumsViewModel.SINGLE_PARAM)) { + combine( + mainRepository.getAlbumMore("MPAD${art.channelId}", MoreAlbumsViewModel.ALBUM_PARAM), + mainRepository.getAlbumMore("MPAD${art.channelId}", MoreAlbumsViewModel.SINGLE_PARAM) + ) { album, single -> Pair(album, single) }.first().let { pair -> val albumItem = pair.first?.items?.firstOrNull()?.items diff --git a/app/src/main/java/com/maxrave/simpmusic/ui/MainActivity.kt b/app/src/main/java/com/maxrave/simpmusic/ui/MainActivity.kt index d17f6a92..4011e7fa 100644 --- a/app/src/main/java/com/maxrave/simpmusic/ui/MainActivity.kt +++ b/app/src/main/java/com/maxrave/simpmusic/ui/MainActivity.kt @@ -47,7 +47,6 @@ import com.maxrave.simpmusic.common.STATUS_DONE import com.maxrave.simpmusic.common.SUPPORTED_LANGUAGE import com.maxrave.simpmusic.common.SUPPORTED_LOCATION import com.maxrave.simpmusic.data.dataStore.DataStoreManager -import com.maxrave.simpmusic.data.repository.MainRepository import com.maxrave.simpmusic.databinding.ActivityMainBinding import com.maxrave.simpmusic.extension.isMyServiceRunning import com.maxrave.simpmusic.extension.navigateSafe @@ -55,7 +54,6 @@ import com.maxrave.simpmusic.service.SimpleMediaService import com.maxrave.simpmusic.ui.screen.MiniPlayer import com.maxrave.simpmusic.ui.theme.AppTheme import com.maxrave.simpmusic.viewModel.SharedViewModel -import dagger.hilt.android.AndroidEntryPoint import dev.chrisbanes.insetter.applyInsetter import kotlinx.coroutines.delay import kotlinx.coroutines.flow.collectLatest @@ -70,10 +68,8 @@ import pub.devrel.easypermissions.EasyPermissions import java.io.File import java.text.SimpleDateFormat import java.util.Locale -import javax.inject.Inject @UnstableApi -@AndroidEntryPoint @Suppress("DEPRECATION") class MainActivity : AppCompatActivity() { private lateinit var binding: ActivityMainBinding @@ -81,12 +77,6 @@ class MainActivity : AppCompatActivity() { private var action: String? = null private var data: Uri? = null - @Inject - lateinit var dataStoreManager: DataStoreManager - - @Inject - lateinit var mainRepository: MainRepository - private val serviceConnection = object : ServiceConnection { override fun onServiceConnected( @@ -109,7 +99,6 @@ class MainActivity : AppCompatActivity() { } } - override fun onNewIntent(intent: Intent) { super.onNewIntent(intent) action = intent.action @@ -213,10 +202,11 @@ class MainActivity : AppCompatActivity() { // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { // WindowCompat.setDecorFitsSystemWindows(window, false) enableEdgeToEdge( - navigationBarStyle = SystemBarStyle.auto( - lightScrim = Color.Transparent.toArgb(), - darkScrim = Color.Transparent.toArgb() - ) + navigationBarStyle = + SystemBarStyle.auto( + lightScrim = Color.Transparent.toArgb(), + darkScrim = Color.Transparent.toArgb(), + ), ) viewModel.checkIsRestoring() viewModel.runWorker() @@ -240,12 +230,14 @@ class MainActivity : AppCompatActivity() { val originalResponse = chain.proceed(chain.request()) if (isNetworkAvailable(applicationContext)) { val maxAge = 60 // read from cache for 1 minute - return originalResponse.newBuilder() + return originalResponse + .newBuilder() .header("Cache-Control", "public, max-age=$maxAge") .build() } else { val maxStale = 60 * 60 * 24 * 28 // tolerate 4-weeks stale - return originalResponse.newBuilder() + return originalResponse + .newBuilder() .header("Cache-Control", "public, only-if-cached, max-stale=$maxStale") .build() } @@ -295,7 +287,8 @@ class MainActivity : AppCompatActivity() { -> val rect = Rect(left, top, right, bottom) val oldRect = Rect(oldLeft, oldTop, oldRight, oldBottom) - if ((rect.width() != oldRect.width() || rect.height() != oldRect.height()) && oldRect != + if ((rect.width() != oldRect.width() || rect.height() != oldRect.height()) && + oldRect != Rect( 0, 0, @@ -343,23 +336,29 @@ class MainActivity : AppCompatActivity() { Log.w("Destination", "onCreate: ${destination.id}") when (destination.id) { R.id.bottom_navigation_item_home, R.id.settingsFragment, R.id.recentlySongsFragment, R.id.moodFragment -> { - binding.bottomNavigationView.menu.findItem( - R.id.bottom_navigation_item_home, - )?.isChecked = + binding.bottomNavigationView.menu + .findItem( + R.id.bottom_navigation_item_home, + )?.isChecked = true } R.id.bottom_navigation_item_search -> { - binding.bottomNavigationView.menu.findItem( - R.id.bottom_navigation_item_search, - )?.isChecked = + binding.bottomNavigationView.menu + .findItem( + R.id.bottom_navigation_item_search, + )?.isChecked = true } - R.id.bottom_navigation_item_library, R.id.downloadedFragment, R.id.mostPlayedFragment, R.id.followedFragment, R.id.favoriteFragment, R.id.localPlaylistFragment -> { - binding.bottomNavigationView.menu.findItem( - R.id.bottom_navigation_item_library, - )?.isChecked = + R.id.bottom_navigation_item_library, R.id.downloadedFragment, + R.id.mostPlayedFragment, R.id.followedFragment, + R.id.favoriteFragment, R.id.localPlaylistFragment, + -> { + binding.bottomNavigationView.menu + .findItem( + R.id.bottom_navigation_item_library, + )?.isChecked = true } @@ -367,23 +366,26 @@ class MainActivity : AppCompatActivity() { val currentBackStack = nav.previousBackStackEntry?.destination?.id when (currentBackStack) { R.id.bottom_navigation_item_library, R.id.downloadedFragment, R.id.mostPlayedFragment, R.id.followedFragment, R.id.favoriteFragment, R.id.localPlaylistFragment -> { - binding.bottomNavigationView.menu.findItem( - R.id.bottom_navigation_item_library, - )?.isChecked = + binding.bottomNavigationView.menu + .findItem( + R.id.bottom_navigation_item_library, + )?.isChecked = true } R.id.bottom_navigation_item_search -> { - binding.bottomNavigationView.menu.findItem( - R.id.bottom_navigation_item_search, - )?.isChecked = + binding.bottomNavigationView.menu + .findItem( + R.id.bottom_navigation_item_search, + )?.isChecked = true } R.id.bottom_navigation_item_home, R.id.settingsFragment, R.id.recentlySongsFragment, R.id.moodFragment -> { - binding.bottomNavigationView.menu.findItem( - R.id.bottom_navigation_item_home, - )?.isChecked = + binding.bottomNavigationView.menu + .findItem( + R.id.bottom_navigation_item_home, + )?.isChecked = true } } @@ -392,16 +394,22 @@ class MainActivity : AppCompatActivity() { Log.w("MainActivity", "Destination: ${destination.label}") Log.w("MainActivity", "Show or Hide: ${viewModel.showOrHideMiniplayer}") if ( - (listOf( - "NowPlayingFragment", "FullscreenFragment", "InfoFragment", - "QueueFragment", "SpotifyLogInFragment", "fragment_log_in", "MusixmatchFragment") - ).contains(destination.label) + ( + listOf( + "NowPlayingFragment", + "FullscreenFragment", + "InfoFragment", + "QueueFragment", + "SpotifyLogInFragment", + "fragment_log_in", + "MusixmatchFragment", + ) + ).contains(destination.label) ) { - lifecycleScope.launch{ viewModel.showOrHideMiniplayer.emit(false) } + lifecycleScope.launch { viewModel.showOrHideMiniplayer.emit(false) } Log.w("MainActivity", "onCreate: HIDE MINIPLAYER") - } - else { - lifecycleScope.launch{ viewModel.showOrHideMiniplayer.emit(true) } + } else { + lifecycleScope.launch { viewModel.showOrHideMiniplayer.emit(true) } Log.w("MainActivity", "onCreate: SHOW MINIPLAYER") } } @@ -479,7 +487,8 @@ class MainActivity : AppCompatActivity() { Log.d("MainActivity", "onCreate: $data") when (val path = data!!.pathSegments.firstOrNull()) { "playlist" -> - data!!.getQueryParameter("list") + data!! + .getQueryParameter("list") ?.let { playlistId -> if (playlistId.startsWith("OLAK5uy_")) { viewModel.intent.value = null @@ -519,13 +528,14 @@ class MainActivity : AppCompatActivity() { }, ) } else { - Toast.makeText( - this@MainActivity, - getString( - R.string.this_link_is_not_supported, - ), - Toast.LENGTH_SHORT, - ).show() + Toast + .makeText( + this@MainActivity, + getString( + R.string.this_link_is_not_supported, + ), + Toast.LENGTH_SHORT, + ).show() } // else { // viewModel.convertNameToId(artistId) @@ -571,8 +581,7 @@ class MainActivity : AppCompatActivity() { .setMessage(getString(R.string.good_night)) .setPositiveButton(getString(R.string.yes)) { d, _ -> d.dismiss() - } - .show() + }.show() } } } @@ -580,67 +589,82 @@ class MainActivity : AppCompatActivity() { job2.join() } lifecycleScope.launch { - val miniplayerJob = launch { - repeatOnLifecycle(Lifecycle.State.CREATED) { - viewModel.nowPlayingScreenData.collect { - Log.w("MainActivity", "Current Destination: ${navController.currentDestination?.label}") - if (!(listOf( - "NowPlayingFragment", "FullscreenFragment", "InfoFragment", - "QueueFragment", "SpotifyLogInFragment", "fragment_log_in", "MusixmatchFragment") - ).contains(navController.currentDestination?.label) - && it.nowPlayingTitle.isNotEmpty() - && binding.miniplayer.visibility != View.VISIBLE + val miniplayerJob = + launch { + repeatOnLifecycle(Lifecycle.State.CREATED) { + viewModel.nowPlayingScreenData.collect { + Log.w("MainActivity", "Current Destination: ${navController.currentDestination?.label}") + if (!( + listOf( + "NowPlayingFragment", + "FullscreenFragment", + "InfoFragment", + "QueueFragment", + "SpotifyLogInFragment", + "fragment_log_in", + "MusixmatchFragment", + ) + ).contains(navController.currentDestination?.label) && + it.nowPlayingTitle.isNotEmpty() && + binding.miniplayer.visibility != View.VISIBLE ) { - Log.w("MainActivity", "Show Miniplayer") - binding.miniplayer.animation = AnimationUtils.loadAnimation(this@MainActivity, R.anim.slide_from_right - ) - binding.miniplayer.visibility = View.VISIBLE + Log.w("MainActivity", "Show Miniplayer") + binding.miniplayer.animation = + AnimationUtils.loadAnimation( + this@MainActivity, + R.anim.slide_from_right, + ) + binding.miniplayer.visibility = View.VISIBLE + } } } } - } - val showHideJob = launch { - repeatOnLifecycle(Lifecycle.State.RESUMED) { - viewModel.showOrHideMiniplayer.collectLatest { - if (it && binding.miniplayer.visibility != View.VISIBLE && - binding.bottomNavigationView.visibility != View.VISIBLE && - viewModel.nowPlayingState.value?.isNotEmpty() == true - ) { - Log.w("MainActivity", "Show Miniplayer") - lifecycleScope.launch { - delay(500) - binding.bottomNavigationView.animation = AnimationUtils.loadAnimation(this@MainActivity, R.anim.btt) - binding.miniplayer.animation = AnimationUtils.loadAnimation(this@MainActivity, R.anim.btt) - binding.miniplayer.visibility = View.VISIBLE - binding.bottomNavigationView.visibility = View.VISIBLE + val showHideJob = + launch { + repeatOnLifecycle(Lifecycle.State.RESUMED) { + viewModel.showOrHideMiniplayer.collectLatest { + if (it && + binding.miniplayer.visibility != View.VISIBLE && + binding.bottomNavigationView.visibility != View.VISIBLE && + viewModel.nowPlayingState.value?.isNotEmpty() == true + ) { + Log.w("MainActivity", "Show Miniplayer") + lifecycleScope.launch { + delay(500) + binding.bottomNavigationView.animation = AnimationUtils.loadAnimation(this@MainActivity, R.anim.btt) + binding.miniplayer.animation = AnimationUtils.loadAnimation(this@MainActivity, R.anim.btt) + binding.miniplayer.visibility = View.VISIBLE + binding.bottomNavigationView.visibility = View.VISIBLE + } + } else if (binding.bottomNavigationView.visibility != View.GONE && + binding.miniplayer.visibility != View.GONE && + !it + ) { + binding.bottomNavigationView.animation = AnimationUtils.loadAnimation(this@MainActivity, R.anim.ttb) + binding.miniplayer.animation = AnimationUtils.loadAnimation(this@MainActivity, R.anim.ttb) + binding.bottomNavigationView.visibility = View.GONE + binding.miniplayer.visibility = View.GONE } - } else if (binding.bottomNavigationView.visibility != View.GONE && - binding.miniplayer.visibility != View.GONE && !it ) { - binding.bottomNavigationView.animation = AnimationUtils.loadAnimation(this@MainActivity, R.anim.ttb) - binding.miniplayer.animation = AnimationUtils.loadAnimation(this@MainActivity, R.anim.ttb) - binding.bottomNavigationView.visibility = View.GONE - binding.miniplayer.visibility = View.GONE } } } - } - val bottomNavBarJob = launch { - repeatOnLifecycle(Lifecycle.State.CREATED) { - dataStoreManager.translucentBottomBar.distinctUntilChanged().collectLatest { - if (it == DataStoreManager.TRUE) { - binding.bottomNavigationView.background = - ResourcesCompat.getDrawable(resources, R.drawable.transparent_rect, null)?.apply { - this.setDither(true) - } - } - else if (it == DataStoreManager.FALSE) { - binding.bottomNavigationView.background = - ColorDrawable(ResourcesCompat.getColor(resources, R.color.md_theme_dark_background, null)) + val bottomNavBarJob = + launch { + repeatOnLifecycle(Lifecycle.State.CREATED) { + viewModel.getTranslucentBottomBar().distinctUntilChanged().collectLatest { + if (it == DataStoreManager.TRUE) { + binding.bottomNavigationView.background = + ResourcesCompat.getDrawable(resources, R.drawable.transparent_rect, null)?.apply { + this.setDither(true) + } + } else if (it == DataStoreManager.FALSE) { + binding.bottomNavigationView.background = + ColorDrawable(ResourcesCompat.getColor(resources, R.color.md_theme_dark_background, null)) + } } } } - } miniplayerJob.join() showHideJob.join() @@ -680,7 +704,6 @@ class MainActivity : AppCompatActivity() { } } - override fun onDestroy() { super.onDestroy() // stopService() @@ -769,7 +792,8 @@ class MainActivity : AppCompatActivity() { val outputFormat = SimpleDateFormat("dd MMM yyyy HH:mm:ss", Locale.getDefault()) val formatted = response.publishedAt?.let { input -> - inputFormat.parse(input) + inputFormat + .parse(input) ?.let { outputFormat.format(it) } } @@ -782,19 +806,16 @@ class MainActivity : AppCompatActivity() { formatted, response.body, ), - ) - .setPositiveButton(getString(R.string.download)) { _, _ -> + ).setPositiveButton(getString(R.string.download)) { _, _ -> val browserIntent = Intent( Intent.ACTION_VIEW, Uri.parse(response.assets?.firstOrNull()?.browserDownloadUrl), ) startActivity(browserIntent) - } - .setNegativeButton(getString(R.string.cancel)) { dialog, _ -> + }.setNegativeButton(getString(R.string.cancel)) { dialog, _ -> dialog.dismiss() - } - .show() + }.show() } } } @@ -807,9 +828,7 @@ class MainActivity : AppCompatActivity() { viewModel.putString(key, value) } - private fun getString(key: String): String? { - return viewModel.getString(key) - } + private fun getString(key: String): String? = viewModel.getString(key) override fun onConfigurationChanged(newConfig: Configuration) { super.onConfigurationChanged(newConfig) diff --git a/app/src/main/java/com/maxrave/simpmusic/ui/fragment/SearchFragment.kt b/app/src/main/java/com/maxrave/simpmusic/ui/fragment/SearchFragment.kt index 122e09ca..d9d66755 100644 --- a/app/src/main/java/com/maxrave/simpmusic/ui/fragment/SearchFragment.kt +++ b/app/src/main/java/com/maxrave/simpmusic/ui/fragment/SearchFragment.kt @@ -1,6 +1,5 @@ package com.maxrave.simpmusic.ui.fragment -import android.app.Application import android.content.Intent import android.os.Bundle import android.util.Log @@ -64,20 +63,13 @@ import com.maxrave.simpmusic.service.test.download.MusicDownloadService import com.maxrave.simpmusic.utils.Resource import com.maxrave.simpmusic.viewModel.SearchViewModel import com.maxrave.simpmusic.viewModel.SharedViewModel -import dagger.hilt.android.AndroidEntryPoint import dev.chrisbanes.insetter.applyInsetter import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChangedBy import kotlinx.coroutines.launch import java.time.LocalDateTime -import javax.inject.Inject -@AndroidEntryPoint class SearchFragment : Fragment() { - - @Inject - lateinit var application: Application - private var _binding: FragmentSearchBinding? = null private val binding get() = _binding!! diff --git a/app/src/main/java/com/maxrave/simpmusic/ui/fragment/home/GenreFragment.kt b/app/src/main/java/com/maxrave/simpmusic/ui/fragment/home/GenreFragment.kt index 4a47396e..94489e48 100644 --- a/app/src/main/java/com/maxrave/simpmusic/ui/fragment/home/GenreFragment.kt +++ b/app/src/main/java/com/maxrave/simpmusic/ui/fragment/home/GenreFragment.kt @@ -15,14 +15,12 @@ import com.maxrave.simpmusic.data.model.explore.mood.genre.ItemsPlaylist import com.maxrave.simpmusic.databinding.MoodMomentDialogBinding import com.maxrave.simpmusic.utils.Resource import com.maxrave.simpmusic.viewModel.GenreViewModel -import dagger.hilt.android.AndroidEntryPoint import dev.chrisbanes.insetter.applyInsetter -@AndroidEntryPoint class GenreFragment: Fragment(){ private val viewModel by viewModels() private var _binding: MoodMomentDialogBinding? = null - private val binding get() = _binding!! + val binding get() = _binding!! private lateinit var genreList: ArrayList private lateinit var genreItemAdapter: GenreItemAdapter diff --git a/app/src/main/java/com/maxrave/simpmusic/ui/fragment/home/HomeFragment.kt b/app/src/main/java/com/maxrave/simpmusic/ui/fragment/home/HomeFragment.kt index 9f46f088..2d8e24ad 100644 --- a/app/src/main/java/com/maxrave/simpmusic/ui/fragment/home/HomeFragment.kt +++ b/app/src/main/java/com/maxrave/simpmusic/ui/fragment/home/HomeFragment.kt @@ -1,5 +1,6 @@ package com.maxrave.simpmusic.ui.fragment.home +import android.annotation.SuppressLint import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -17,9 +18,7 @@ import com.maxrave.simpmusic.ui.screen.home.HomeScreen import com.maxrave.simpmusic.ui.theme.AppTheme import com.maxrave.simpmusic.viewModel.HomeViewModel import com.maxrave.simpmusic.viewModel.SharedViewModel -import dagger.hilt.android.AndroidEntryPoint -@AndroidEntryPoint @UnstableApi @ExperimentalFoundationApi @ExperimentalMaterial3Api @@ -364,6 +363,7 @@ class HomeFragment : Fragment() { } } + @SuppressLint("UnusedMaterial3ScaffoldPaddingParameter") override fun onViewCreated( view: View, savedInstanceState: Bundle?, diff --git a/app/src/main/java/com/maxrave/simpmusic/ui/fragment/home/MoodFragment.kt b/app/src/main/java/com/maxrave/simpmusic/ui/fragment/home/MoodFragment.kt index 5cf5d3fb..23cd8378 100644 --- a/app/src/main/java/com/maxrave/simpmusic/ui/fragment/home/MoodFragment.kt +++ b/app/src/main/java/com/maxrave/simpmusic/ui/fragment/home/MoodFragment.kt @@ -5,7 +5,6 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.compose.material3.Scaffold -import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.ComposeView import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels @@ -13,9 +12,7 @@ import androidx.navigation.fragment.findNavController import com.maxrave.simpmusic.ui.screen.home.MoodScreen import com.maxrave.simpmusic.ui.theme.AppTheme import com.maxrave.simpmusic.viewModel.MoodViewModel -import dagger.hilt.android.AndroidEntryPoint -@AndroidEntryPoint class MoodFragment: Fragment() { private val viewModel by viewModels() diff --git a/app/src/main/java/com/maxrave/simpmusic/ui/fragment/home/NotificationFragment.kt b/app/src/main/java/com/maxrave/simpmusic/ui/fragment/home/NotificationFragment.kt index 41b8a6c4..78678a62 100644 --- a/app/src/main/java/com/maxrave/simpmusic/ui/fragment/home/NotificationFragment.kt +++ b/app/src/main/java/com/maxrave/simpmusic/ui/fragment/home/NotificationFragment.kt @@ -1,5 +1,6 @@ package com.maxrave.simpmusic.ui.fragment.home +import android.annotation.SuppressLint import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -12,9 +13,7 @@ import androidx.navigation.fragment.findNavController import com.maxrave.simpmusic.ui.screen.home.NotificationScreen import com.maxrave.simpmusic.ui.theme.AppTheme import com.maxrave.simpmusic.viewModel.NotificationViewModel -import dagger.hilt.android.AndroidEntryPoint -@AndroidEntryPoint class NotificationFragment : Fragment() { private lateinit var composeView: ComposeView @@ -31,6 +30,7 @@ class NotificationFragment : Fragment() { } } + @SuppressLint("UnusedMaterial3ScaffoldPaddingParameter") override fun onViewCreated( view: View, savedInstanceState: Bundle?, diff --git a/app/src/main/java/com/maxrave/simpmusic/ui/fragment/home/RecentlySongsFragment.kt b/app/src/main/java/com/maxrave/simpmusic/ui/fragment/home/RecentlySongsFragment.kt index 450bd43b..5be320c1 100644 --- a/app/src/main/java/com/maxrave/simpmusic/ui/fragment/home/RecentlySongsFragment.kt +++ b/app/src/main/java/com/maxrave/simpmusic/ui/fragment/home/RecentlySongsFragment.kt @@ -27,17 +27,15 @@ import com.maxrave.simpmusic.service.PlaylistType import com.maxrave.simpmusic.service.QueueData import com.maxrave.simpmusic.viewModel.RecentlySongsViewModel import com.maxrave.simpmusic.viewModel.SharedViewModel -import dagger.hilt.android.AndroidEntryPoint import dev.chrisbanes.insetter.applyInsetter import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -@AndroidEntryPoint class RecentlySongsFragment: Fragment() { private var _binding: FragmentRecentlySongsBinding? = null - private val binding get() = _binding!! + val binding get() = _binding!! private val viewModel by viewModels() private val sharedViewModel by activityViewModels() diff --git a/app/src/main/java/com/maxrave/simpmusic/ui/fragment/home/SettingsFragment.kt b/app/src/main/java/com/maxrave/simpmusic/ui/fragment/home/SettingsFragment.kt index baa1f4f2..03dc6ec0 100644 --- a/app/src/main/java/com/maxrave/simpmusic/ui/fragment/home/SettingsFragment.kt +++ b/app/src/main/java/com/maxrave/simpmusic/ui/fragment/home/SettingsFragment.kt @@ -47,7 +47,6 @@ import com.maxrave.simpmusic.viewModel.SettingsViewModel import com.maxrave.simpmusic.viewModel.SharedViewModel import com.mikepenz.aboutlibraries.Libs import com.mikepenz.aboutlibraries.LibsBuilder -import dagger.hilt.android.AndroidEntryPoint import dev.chrisbanes.insetter.applyInsetter import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch @@ -61,34 +60,34 @@ import java.time.format.DateTimeFormatter import java.util.Locale import java.util.Scanner - @UnstableApi -@AndroidEntryPoint class SettingsFragment : Fragment() { - private var _binding: FragmentSettingsBinding? = null - private val binding get() = _binding!! + val binding get() = _binding!! private val viewModel by activityViewModels() private val sharedViewModel by activityViewModels() - private val backupLauncher = registerForActivityResult(ActivityResultContracts.CreateDocument("application/octet-stream")) { uri -> - if (uri != null) { - viewModel.backup(requireContext(), uri) + private val backupLauncher = + registerForActivityResult(ActivityResultContracts.CreateDocument("application/octet-stream")) { uri -> + if (uri != null) { + viewModel.backup(requireContext(), uri) + } } - } - private val restoreLauncher = registerForActivityResult(ActivityResultContracts.OpenDocument()) { uri -> - if (uri != null) { - viewModel.restore(requireContext(), uri) + private val restoreLauncher = + registerForActivityResult(ActivityResultContracts.OpenDocument()) { uri -> + if (uri != null) { + viewModel.restore(requireContext(), uri) + } } - } override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, + inflater: LayoutInflater, + container: ViewGroup?, savedInstanceState: Bundle?, ): View { _binding = FragmentSettingsBinding.inflate(inflater, container, false) binding.topAppBarLayout.applyInsetter { - type(statusBars = true){ + type(statusBars = true) { margin() } } @@ -104,7 +103,10 @@ class SettingsFragment : Fragment() { @OptIn(ExperimentalCoilApi::class) @UnstableApi - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + override fun onViewCreated( + view: View, + savedInstanceState: Bundle?, + ) { super.onViewCreated(view, savedInstanceState) viewModel.getLocation() @@ -137,284 +139,320 @@ class SettingsFragment : Fragment() { lifecycleScope.launch { viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.CREATED) { - val job1 = launch { - viewModel.musixmatchLoggedIn.collect { - if (it != null) { - if (it == DataStoreManager.TRUE) { - binding.tvMusixmatchLoginTitle.text = - getString(R.string.log_out_from_musixmatch) - binding.tvMusixmatchLogin.text = getString(R.string.logged_in) - setEnabledAll(binding.swUseMusixmatchTranslation, true) - setEnabledAll(binding.btTranslationLanguage, true) - } else if (it == DataStoreManager.FALSE) { - binding.tvMusixmatchLoginTitle.text = - getString(R.string.log_in_to_Musixmatch) - binding.tvMusixmatchLogin.text = - getString(R.string.only_support_email_and_password_type) - setEnabledAll(binding.swUseMusixmatchTranslation, false) - setEnabledAll(binding.btTranslationLanguage, false) + val job1 = + launch { + viewModel.musixmatchLoggedIn.collect { + if (it != null) { + if (it == DataStoreManager.TRUE) { + binding.tvMusixmatchLoginTitle.text = + getString(R.string.log_out_from_musixmatch) + binding.tvMusixmatchLogin.text = getString(R.string.logged_in) + setEnabledAll(binding.swUseMusixmatchTranslation, true) + setEnabledAll(binding.btTranslationLanguage, true) + } else if (it == DataStoreManager.FALSE) { + binding.tvMusixmatchLoginTitle.text = + getString(R.string.log_in_to_Musixmatch) + binding.tvMusixmatchLogin.text = + getString(R.string.only_support_email_and_password_type) + setEnabledAll(binding.swUseMusixmatchTranslation, false) + setEnabledAll(binding.btTranslationLanguage, false) + } } } } - } - val job2 = launch { - viewModel.playVideoInsteadOfAudio.collect { - if (it == DataStoreManager.TRUE) { - binding.swEnableVideo.isChecked = true - setEnabledAll(binding.btVideoQuality, true) - } else if (it == DataStoreManager.FALSE) { - binding.swEnableVideo.isChecked = false - setEnabledAll(binding.btVideoQuality, false) + val job2 = + launch { + viewModel.playVideoInsteadOfAudio.collect { + if (it == DataStoreManager.TRUE) { + binding.swEnableVideo.isChecked = true + setEnabledAll(binding.btVideoQuality, true) + } else if (it == DataStoreManager.FALSE) { + binding.swEnableVideo.isChecked = false + setEnabledAll(binding.btVideoQuality, false) + } } } - } - val job3 = launch { - viewModel.videoQuality.collect { - binding.tvVideoQuality.text = it + val job3 = + launch { + viewModel.videoQuality.collect { + binding.tvVideoQuality.text = it + } } - } - val job4 = launch { - viewModel.mainLyricsProvider.collect { - if (it == DataStoreManager.YOUTUBE) { - binding.tvMainLyricsProvider.text = LYRICS_PROVIDER.items.get(1) - } else if (it == DataStoreManager.MUSIXMATCH) { - binding.tvMainLyricsProvider.text = LYRICS_PROVIDER.items.get(0) + val job4 = + launch { + viewModel.mainLyricsProvider.collect { + if (it == DataStoreManager.YOUTUBE) { + binding.tvMainLyricsProvider.text = LYRICS_PROVIDER.items.get(1) + } else if (it == DataStoreManager.MUSIXMATCH) { + binding.tvMainLyricsProvider.text = LYRICS_PROVIDER.items.get(0) + } } } - } - val job5 = launch { - viewModel.translationLanguage.collect { - binding.tvTranslationLanguage.text = it + val job5 = + launch { + viewModel.translationLanguage.collect { + binding.tvTranslationLanguage.text = it + } } - } - val job6 = launch { - viewModel.useTranslation.collect { - binding.swUseMusixmatchTranslation.isChecked = it == DataStoreManager.TRUE + val job6 = + launch { + viewModel.useTranslation.collect { + binding.swUseMusixmatchTranslation.isChecked = it == DataStoreManager.TRUE + } } - } - val job7 = launch { - viewModel.sendBackToGoogle.collect { - binding.swSaveHistory.isChecked = it == DataStoreManager.TRUE + val job7 = + launch { + viewModel.sendBackToGoogle.collect { + binding.swSaveHistory.isChecked = it == DataStoreManager.TRUE + } } - } - val job8 = launch { - viewModel.location.collect { - binding.tvContentCountry.text = it + val job8 = + launch { + viewModel.location.collect { + binding.tvContentCountry.text = it + } } - } - val job9 = launch { - viewModel.language.collect { - Log.w("Language", it.toString()) - if (it != null) { - if (it.contains("id") || it.contains("in")) { - binding.tvLanguage.text = "Bahasa Indonesia" - } else { - val temp = - SUPPORTED_LANGUAGE.items.getOrNull( - SUPPORTED_LANGUAGE.codes.indexOf( - it + val job9 = + launch { + viewModel.language.collect { + Log.w("Language", it.toString()) + if (it != null) { + if (it.contains("id") || it.contains("in")) { + binding.tvLanguage.text = "Bahasa Indonesia" + } else { + val temp = + SUPPORTED_LANGUAGE.items.getOrNull( + SUPPORTED_LANGUAGE.codes.indexOf( + it, + ), ) - ) - binding.tvLanguage.text = temp + binding.tvLanguage.text = temp + } + } else { + binding.tvLanguage.text = "Automatic" } - } else { - binding.tvLanguage.text = "Automatic" } } - } - val job10 = launch { - viewModel.quality.collect { - binding.tvQuality.text = it + val job10 = + launch { + viewModel.quality.collect { + binding.tvQuality.text = it + } } - } - val job11 = launch { - viewModel.cacheSize.collect { - if (it != null) { - drawDataStat() - binding.tvPlayerCache.text = - getString(R.string.cache_size, bytesToMB(it).toString()) + val job11 = + launch { + viewModel.cacheSize.collect { + if (it != null) { + drawDataStat() + binding.tvPlayerCache.text = + getString(R.string.cache_size, bytesToMB(it).toString()) + } } } - } - val job12 = launch { - viewModel.downloadedCacheSize.collect { - if (it != null) { - drawDataStat() - binding.tvDownloadedCache.text = - getString(R.string.cache_size, bytesToMB(it).toString()) + val job12 = + launch { + viewModel.downloadedCacheSize.collect { + if (it != null) { + drawDataStat() + binding.tvDownloadedCache.text = + getString(R.string.cache_size, bytesToMB(it).toString()) + } } } - } - val job13 = launch { - viewModel.normalizeVolume.collect { - binding.swNormalizeVolume.isChecked = it == DataStoreManager.TRUE + val job13 = + launch { + viewModel.normalizeVolume.collect { + binding.swNormalizeVolume.isChecked = it == DataStoreManager.TRUE + } } - } - val job14 = launch { - viewModel.skipSilent.collect { - binding.swSkipSilent.isChecked = it == DataStoreManager.TRUE + val job14 = + launch { + viewModel.skipSilent.collect { + binding.swSkipSilent.isChecked = it == DataStoreManager.TRUE + } } - } - val job15 = launch { - viewModel.savedPlaybackState.collect { - binding.swSavePlaybackState.isChecked = it == DataStoreManager.TRUE + val job15 = + launch { + viewModel.savedPlaybackState.collect { + binding.swSavePlaybackState.isChecked = it == DataStoreManager.TRUE + } } - } - val job16 = launch { - viewModel.saveRecentSongAndQueue.collect { - binding.swSaveLastPlayed.isChecked = it == DataStoreManager.TRUE + val job16 = + launch { + viewModel.saveRecentSongAndQueue.collect { + binding.swSaveLastPlayed.isChecked = it == DataStoreManager.TRUE + } } - } - val job17 = launch { - viewModel.saveRecentSongAndQueue.collect { - binding.swSaveLastPlayed.isChecked = it == DataStoreManager.TRUE + val job17 = + launch { + viewModel.saveRecentSongAndQueue.collect { + binding.swSaveLastPlayed.isChecked = it == DataStoreManager.TRUE + } } - } - val job18 = launch { - viewModel.sponsorBlockEnabled.collect { - binding.swEnableSponsorBlock.isChecked = it == DataStoreManager.TRUE + val job18 = + launch { + viewModel.sponsorBlockEnabled.collect { + binding.swEnableSponsorBlock.isChecked = it == DataStoreManager.TRUE + } } - } - val job19 = launch { - viewModel.lastCheckForUpdate.collect { - if (it != null) { - binding.tvCheckForUpdate.text = getString( - R.string.last_checked_at, - DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss") - .withZone(ZoneId.systemDefault()) - .format(Instant.ofEpochMilli(it.toLong())) - ) + val job19 = + launch { + viewModel.lastCheckForUpdate.collect { + if (it != null) { + binding.tvCheckForUpdate.text = + getString( + R.string.last_checked_at, + DateTimeFormatter + .ofPattern("yyyy-MM-dd HH:mm:ss") + .withZone(ZoneId.systemDefault()) + .format(Instant.ofEpochMilli(it.toLong())), + ) + } } } - } - val job20 = launch { - viewModel.playerCacheLimit.collect { - binding.tvLimitPlayerCache.text = - if (it != -1) "$it MB" else getString(R.string.unlimited) + val job20 = + launch { + viewModel.playerCacheLimit.collect { + binding.tvLimitPlayerCache.text = + if (it != -1) "$it MB" else getString(R.string.unlimited) + } } - } - val job21 = launch { - viewModel.githubResponse.collect { response -> - if (response != null) { - if (response.tagName != getString(R.string.version_name)) { - binding.tvCheckForUpdate.text = getString( - R.string.last_checked_at, - DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss") - .withZone(ZoneId.systemDefault()) - .format(Instant.ofEpochMilli(System.currentTimeMillis())) - ) - val inputFormat = - SimpleDateFormat( - "yyyy-MM-dd'T'HH:mm:ss'Z'", - Locale.getDefault() - ) - val outputFormat = - SimpleDateFormat("dd MMM yyyy HH:mm:ss", Locale.getDefault()) - val formatted = response.publishedAt?.let { input -> - inputFormat.parse(input) - ?.let { outputFormat.format(it) } - } - MaterialAlertDialogBuilder(requireContext()) - .setTitle(getString(R.string.update_available)) - .setMessage( + val job21 = + launch { + viewModel.githubResponse.collect { response -> + if (response != null) { + if (response.tagName != getString(R.string.version_name)) { + binding.tvCheckForUpdate.text = getString( - R.string.update_message, - response.tagName, - formatted, - response.body - ) - ) - .setPositiveButton(getString(R.string.download)) { _, _ -> - val browserIntent = Intent( - Intent.ACTION_VIEW, - Uri.parse(response.assets?.firstOrNull()?.browserDownloadUrl) - ) - startActivity(browserIntent) - } - .setNegativeButton(getString(R.string.cancel)) { dialog, _ -> - dialog.dismiss() - } - .show() - } else { - Toast.makeText( - requireContext(), - getString(R.string.no_update), - Toast.LENGTH_SHORT - ).show() - viewModel.getLastCheckForUpdate() - viewModel.lastCheckForUpdate.collect { - if (it != null) { - binding.tvCheckForUpdate.text = getString( R.string.last_checked_at, - DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss") + DateTimeFormatter + .ofPattern("yyyy-MM-dd HH:mm:ss") .withZone(ZoneId.systemDefault()) - .format(Instant.ofEpochMilli(it.toLong())) + .format(Instant.ofEpochMilli(System.currentTimeMillis())), ) + val inputFormat = + SimpleDateFormat( + "yyyy-MM-dd'T'HH:mm:ss'Z'", + Locale.getDefault(), + ) + val outputFormat = + SimpleDateFormat("dd MMM yyyy HH:mm:ss", Locale.getDefault()) + val formatted = + response.publishedAt?.let { input -> + inputFormat + .parse(input) + ?.let { outputFormat.format(it) } + } + MaterialAlertDialogBuilder(requireContext()) + .setTitle(getString(R.string.update_available)) + .setMessage( + getString( + R.string.update_message, + response.tagName, + formatted, + response.body, + ), + ).setPositiveButton(getString(R.string.download)) { _, _ -> + val browserIntent = + Intent( + Intent.ACTION_VIEW, + Uri.parse(response.assets?.firstOrNull()?.browserDownloadUrl), + ) + startActivity(browserIntent) + }.setNegativeButton(getString(R.string.cancel)) { dialog, _ -> + dialog.dismiss() + }.show() + } else { + Toast + .makeText( + requireContext(), + getString(R.string.no_update), + Toast.LENGTH_SHORT, + ).show() + viewModel.getLastCheckForUpdate() + viewModel.lastCheckForUpdate.collect { + if (it != null) { + binding.tvCheckForUpdate.text = + getString( + R.string.last_checked_at, + DateTimeFormatter + .ofPattern("yyyy-MM-dd HH:mm:ss") + .withZone(ZoneId.systemDefault()) + .format(Instant.ofEpochMilli(it.toLong())), + ) + } } } } } } - } - val job22 = launch { - viewModel.thumbCacheSize.collect { - binding.tvThumbnailCache.text = getString( - R.string.cache_size, if (it != null) { - bytesToMB(it) - } else { - 0 - }.toString() - ) + val job22 = + launch { + viewModel.thumbCacheSize.collect { + binding.tvThumbnailCache.text = + getString( + R.string.cache_size, + if (it != null) { + bytesToMB(it) + } else { + 0 + }.toString(), + ) + } } - } - val job23 = launch { - viewModel.spotifyLogIn.collect { - if (it) { - binding.tvSpotifyLogin.text = getString(R.string.logged_in) - setEnabledAll(binding.btEnableCanvas, true) - setEnabledAll(binding.btEnableSpotifyLyrics, true) - } else { - binding.tvSpotifyLogin.text = getString(R.string.intro_login_to_spotify) - setEnabledAll(binding.btEnableCanvas, false) - setEnabledAll(binding.btEnableSpotifyLyrics, false) + val job23 = + launch { + viewModel.spotifyLogIn.collect { + if (it) { + binding.tvSpotifyLogin.text = getString(R.string.logged_in) + setEnabledAll(binding.btEnableCanvas, true) + setEnabledAll(binding.btEnableSpotifyLyrics, true) + } else { + binding.tvSpotifyLogin.text = getString(R.string.intro_login_to_spotify) + setEnabledAll(binding.btEnableCanvas, false) + setEnabledAll(binding.btEnableSpotifyLyrics, false) + } } } - } - val job24 = launch { - viewModel.spotifyLyrics.collect { - if (it) { - binding.swEnableSpotifyLyrics.isChecked = true - } else { - binding.swEnableSpotifyLyrics.isChecked = false + val job24 = + launch { + viewModel.spotifyLyrics.collect { + if (it) { + binding.swEnableSpotifyLyrics.isChecked = true + } else { + binding.swEnableSpotifyLyrics.isChecked = false + } } } - } - val job25 = launch { - viewModel.spotifyCanvas.collect { - if (it) { - binding.swEnableCanvas.isChecked = true - } else { - binding.swEnableCanvas.isChecked = false + val job25 = + launch { + viewModel.spotifyCanvas.collect { + if (it) { + binding.swEnableCanvas.isChecked = true + } else { + binding.swEnableCanvas.isChecked = false + } } } - } - val job26 = launch { - viewModel.homeLimit.collect { - binding.tvHomeLimit.text = it.toString() - if (it != null) { - binding.sliderHomeLimit.value = it.toFloat() + val job26 = + launch { + viewModel.homeLimit.collect { + binding.tvHomeLimit.text = it.toString() + if (it != null) { + binding.sliderHomeLimit.value = it.toFloat() + } } } - } - val job27 = launch { - viewModel.translucentBottomBar.collectLatest { translucent -> - binding.swEnableTranslucentNavBar.isChecked = if (translucent == DataStoreManager.TRUE) true else false + val job27 = + launch { + viewModel.translucentBottomBar.collectLatest { translucent -> + binding.swEnableTranslucentNavBar.isChecked = if (translucent == DataStoreManager.TRUE) true else false + } } - } job1.join() job2.join() job3.join() @@ -444,36 +482,38 @@ class SettingsFragment : Fragment() { job27.join() } } - binding.sliderHomeLimit.addOnSliderTouchListener(object : Slider.OnSliderTouchListener { - override fun onStartTrackingTouch(slider: Slider) { - // Responds to when slider's touch event is being started - } + binding.sliderHomeLimit.addOnSliderTouchListener( + object : Slider.OnSliderTouchListener { + override fun onStartTrackingTouch(slider: Slider) { + // Responds to when slider's touch event is being started + } - override fun onStopTrackingTouch(slider: Slider) { - viewModel.setHomeLimit(slider.value.toInt()) - } - }) + override fun onStopTrackingTouch(slider: Slider) { + viewModel.setHomeLimit(slider.value.toInt()) + } + }, + ) binding.btLimitPlayerCache.setOnClickListener { var checkedIndex = -1 - val dialog = MaterialAlertDialogBuilder(requireContext()) - .setSingleChoiceItems(LIMIT_CACHE_SIZE.items, -1) { _, which -> - checkedIndex = which - } - .setTitle(getString(R.string.limit_player_cache)) - .setNegativeButton(getString(R.string.cancel)) { dialog, _ -> - dialog.dismiss() - } - .setPositiveButton(getString(R.string.change)) { dialog, _ -> - if (checkedIndex != -1) { - viewModel.setPlayerCacheLimit(LIMIT_CACHE_SIZE.data[checkedIndex]) - Toast.makeText( - requireContext(), - getString(R.string.restart_app), - Toast.LENGTH_SHORT - ).show() + val dialog = + MaterialAlertDialogBuilder(requireContext()) + .setSingleChoiceItems(LIMIT_CACHE_SIZE.items, -1) { _, which -> + checkedIndex = which + }.setTitle(getString(R.string.limit_player_cache)) + .setNegativeButton(getString(R.string.cancel)) { dialog, _ -> + dialog.dismiss() + }.setPositiveButton(getString(R.string.change)) { dialog, _ -> + if (checkedIndex != -1) { + viewModel.setPlayerCacheLimit(LIMIT_CACHE_SIZE.data[checkedIndex]) + Toast + .makeText( + requireContext(), + getString(R.string.restart_app), + Toast.LENGTH_SHORT, + ).show() + } + dialog.dismiss() } - dialog.dismiss() - } dialog.show() } binding.btYouTubeAccount.setOnClickListener { @@ -487,15 +527,17 @@ class SettingsFragment : Fragment() { adapter = accountAdapter layoutManager = LinearLayoutManager(requireContext()) } - accountAdapter.setOnAccountClickListener(object : - AccountAdapter.OnAccountClickListener { - override fun onAccountClick(pos: Int) { - Log.w("Account", accountAdapter.getAccountList().getOrNull(pos).toString()) - if (accountAdapter.getAccountList().getOrNull(pos) != null) { - viewModel.setUsedAccount(accountAdapter.getAccountList().get(pos)) + accountAdapter.setOnAccountClickListener( + object : + AccountAdapter.OnAccountClickListener { + override fun onAccountClick(pos: Int) { + Log.w("Account", accountAdapter.getAccountList().getOrNull(pos).toString()) + if (accountAdapter.getAccountList().getOrNull(pos) != null) { + viewModel.setUsedAccount(accountAdapter.getAccountList().get(pos)) + } } - } - }) + }, + ) accountBinding.btAddAccount.setOnClickListener { findNavController().navigateSafe(R.id.action_global_logInFragment) alertDialog.dismiss() @@ -508,52 +550,54 @@ class SettingsFragment : Fragment() { alertDialog.dismiss() } accountBinding.btLogOut.setOnClickListener { - val subAlertDialogBuilder = MaterialAlertDialogBuilder(requireContext()) - .setTitle(getString(R.string.warning)) - .setMessage(getString(R.string.log_out_warning)) - .setNegativeButton(getString(R.string.cancel)) { dialog, _ -> - dialog.dismiss() - } - .setPositiveButton(getString(R.string.log_out)) { dialog, _ -> - viewModel.logOutAllYouTube() - } + val subAlertDialogBuilder = + MaterialAlertDialogBuilder(requireContext()) + .setTitle(getString(R.string.warning)) + .setMessage(getString(R.string.log_out_warning)) + .setNegativeButton(getString(R.string.cancel)) { dialog, _ -> + dialog.dismiss() + }.setPositiveButton(getString(R.string.log_out)) { dialog, _ -> + viewModel.logOutAllYouTube() + } subAlertDialogBuilder.show() } viewModel.getAllGoogleAccount() accountBinding.loadingLayout.visibility = View.VISIBLE lifecycleScope.launch { - val job2 = launch { - viewModel.loading.collectLatest { - if (it) { - accountBinding.loadingLayout.visibility = View.VISIBLE - accountBinding.apply { - setEnabledAll(btAddAccount, false) - setEnabledAll(btLogOut, false) - setEnabledAll(btGuest, false) - } - } else { - accountBinding.loadingLayout.visibility = View.GONE - accountBinding.apply { - setEnabledAll(btAddAccount, true) - setEnabledAll(btLogOut, true) - setEnabledAll(btGuest, true) + val job2 = + launch { + viewModel.loading.collectLatest { + if (it) { + accountBinding.loadingLayout.visibility = View.VISIBLE + accountBinding.apply { + setEnabledAll(btAddAccount, false) + setEnabledAll(btLogOut, false) + setEnabledAll(btGuest, false) + } + } else { + accountBinding.loadingLayout.visibility = View.GONE + accountBinding.apply { + setEnabledAll(btAddAccount, true) + setEnabledAll(btLogOut, true) + setEnabledAll(btGuest, true) + } } } } - } - val job1 = launch { - viewModel.googleAccounts.collect { - if (it != null) { - accountBinding.tvNoAccount.visibility = View.GONE - accountBinding.rvAccount.visibility = View.VISIBLE - accountAdapter.updateAccountList(it) - } else { - accountAdapter.updateAccountList(arrayListOf()) - accountBinding.tvNoAccount.visibility = View.VISIBLE - accountBinding.rvAccount.visibility = View.GONE + val job1 = + launch { + viewModel.googleAccounts.collect { + if (it != null) { + accountBinding.tvNoAccount.visibility = View.GONE + accountBinding.rvAccount.visibility = View.VISIBLE + accountAdapter.updateAccountList(it) + } else { + accountAdapter.updateAccountList(arrayListOf()) + accountBinding.tvNoAccount.visibility = View.VISIBLE + accountBinding.rvAccount.visibility = View.GONE + } } } - } job1.join() job2.join() } @@ -567,13 +611,12 @@ class SettingsFragment : Fragment() { binding.btVersion.setOnClickListener { findNavController().navigateSafe(R.id.action_global_creditFragment) } - + binding.btMusixmatchLogin.setOnClickListener { if (viewModel.musixmatchLoggedIn.value == DataStoreManager.TRUE) { viewModel.clearMusixmatchCookie() Toast.makeText(requireContext(), getString(R.string.logged_out), Toast.LENGTH_SHORT).show() - } - else if (viewModel.musixmatchLoggedIn.value == DataStoreManager.FALSE) { + } else if (viewModel.musixmatchLoggedIn.value == DataStoreManager.FALSE) { findNavController().navigateSafe(R.id.action_global_musixmatchFragment) } } @@ -588,181 +631,178 @@ class SettingsFragment : Fragment() { Log.d("EQ", resolveInfo.toString()) if (resolveInfo.isEmpty()) { Toast.makeText(requireContext(), getString(R.string.no_equalizer), Toast.LENGTH_SHORT).show() - } - else{ + } else { resultLauncher.launch(eqIntent) } } binding.btGithub.setOnClickListener { - val urlIntent = Intent( - Intent.ACTION_VIEW, - Uri.parse("https://github.com/maxrave-dev/") - ) + val urlIntent = + Intent( + Intent.ACTION_VIEW, + Uri.parse("https://github.com/maxrave-dev/"), + ) startActivity(urlIntent) } binding.btDonate.setOnClickListener { - val urlIntent = Intent( - Intent.ACTION_VIEW, - Uri.parse("https://buymeacoffee.com/maxrave") - ) + val urlIntent = + Intent( + Intent.ACTION_VIEW, + Uri.parse("https://buymeacoffee.com/maxrave"), + ) startActivity(urlIntent) } binding.btStoragePlayerCache.setOnClickListener { - val dialog = MaterialAlertDialogBuilder(requireContext()) - .setTitle(getString(R.string.clear_player_cache)) - .setNegativeButton(getString(R.string.cancel)) { dialog, _ -> - dialog.dismiss() - } - .setPositiveButton(getString(R.string.clear)) { dialog, _ -> - viewModel.clearPlayerCache() - dialog.dismiss() - } + val dialog = + MaterialAlertDialogBuilder(requireContext()) + .setTitle(getString(R.string.clear_player_cache)) + .setNegativeButton(getString(R.string.cancel)) { dialog, _ -> + dialog.dismiss() + }.setPositiveButton(getString(R.string.clear)) { dialog, _ -> + viewModel.clearPlayerCache() + dialog.dismiss() + } dialog.show() } binding.btContentCountry.setOnClickListener { var checkedIndex = -1 - val dialog = MaterialAlertDialogBuilder(requireContext()) - .setSingleChoiceItems(SUPPORTED_LOCATION.items, -1) { _, which -> - checkedIndex = which - } - .setTitle(getString(R.string.content_country)) - .setNegativeButton(getString(R.string.cancel)) { dialog, _ -> - dialog.dismiss() - } - .setPositiveButton(getString(R.string.change)) { dialog, _ -> - if (checkedIndex != -1) { - viewModel.changeLocation(SUPPORTED_LOCATION.items[checkedIndex].toString()) + val dialog = + MaterialAlertDialogBuilder(requireContext()) + .setSingleChoiceItems(SUPPORTED_LOCATION.items, -1) { _, which -> + checkedIndex = which + }.setTitle(getString(R.string.content_country)) + .setNegativeButton(getString(R.string.cancel)) { dialog, _ -> + dialog.dismiss() + }.setPositiveButton(getString(R.string.change)) { dialog, _ -> + if (checkedIndex != -1) { + viewModel.changeLocation(SUPPORTED_LOCATION.items[checkedIndex].toString()) + } + dialog.dismiss() } - dialog.dismiss() - } dialog.show() } binding.btLanguage.setOnClickListener { var checkedIndex = -1 - val dialog = MaterialAlertDialogBuilder(requireContext()) - .setSingleChoiceItems(SUPPORTED_LANGUAGE.items, -1) { _, which -> - checkedIndex = which - } - .setTitle(getString(R.string.language)) - .setNegativeButton(getString(R.string.cancel)) { dialog, _ -> - dialog.dismiss() - } - .setPositiveButton(getString(R.string.change)) { dialog, _ -> - if (checkedIndex != -1) { - val alertDialog = MaterialAlertDialogBuilder(requireContext()) - .setTitle(R.string.warning) - .setMessage(R.string.change_language_warning) - .setNegativeButton(getString(R.string.cancel)) { d, _ -> - d.dismiss() - dialog.dismiss() - } - .setPositiveButton(getString(R.string.change)) { d, _ -> - viewModel.changeLanguage(SUPPORTED_LANGUAGE.codes[checkedIndex]) - if (SUPPORTED_LANGUAGE.codes.getOrNull(checkedIndex) != null) { - runCatching { - SUPPORTED_LANGUAGE.items[SUPPORTED_LANGUAGE.codes.indexOf( - SUPPORTED_LANGUAGE.codes[checkedIndex] - )] - }.onSuccess { temp -> - binding.tvLanguage.text = temp - val code = SUPPORTED_LANGUAGE.codes.getOrNull(checkedIndex) - val localeList = LocaleListCompat.forLanguageTags( - if (code == "id-ID") { - if (Build.VERSION.SDK_INT >= 35) { - "id-ID" - } else { - "in-ID" - } - } - else { - code + val dialog = + MaterialAlertDialogBuilder(requireContext()) + .setSingleChoiceItems(SUPPORTED_LANGUAGE.items, -1) { _, which -> + checkedIndex = which + }.setTitle(getString(R.string.language)) + .setNegativeButton(getString(R.string.cancel)) { dialog, _ -> + dialog.dismiss() + }.setPositiveButton(getString(R.string.change)) { dialog, _ -> + if (checkedIndex != -1) { + val alertDialog = + MaterialAlertDialogBuilder(requireContext()) + .setTitle(R.string.warning) + .setMessage(R.string.change_language_warning) + .setNegativeButton(getString(R.string.cancel)) { d, _ -> + d.dismiss() + dialog.dismiss() + }.setPositiveButton(getString(R.string.change)) { d, _ -> + viewModel.changeLanguage(SUPPORTED_LANGUAGE.codes[checkedIndex]) + if (SUPPORTED_LANGUAGE.codes.getOrNull(checkedIndex) != null) { + runCatching { + SUPPORTED_LANGUAGE.items[ + SUPPORTED_LANGUAGE.codes.indexOf( + SUPPORTED_LANGUAGE.codes[checkedIndex], + ), + ] + }.onSuccess { temp -> + binding.tvLanguage.text = temp + val code = SUPPORTED_LANGUAGE.codes.getOrNull(checkedIndex) + val localeList = + LocaleListCompat.forLanguageTags( + if (code == "id-ID") { + if (Build.VERSION.SDK_INT >= 35) { + "id-ID" + } else { + "in-ID" + } + } else { + code + }, + ) + Log.d("Language", localeList.toString()) + sharedViewModel.activityRecreate() + AppCompatDelegate.setApplicationLocales(localeList) + }.onFailure { + Toast + .makeText( + requireContext(), + getString(R.string.invalid_language_code), + Toast.LENGTH_SHORT, + ).show() } - ) - Log.d("Language", localeList.toString()) - sharedViewModel.activityRecreate() - AppCompatDelegate.setApplicationLocales(localeList) - } - .onFailure { - Toast.makeText( - requireContext(), - getString(R.string.invalid_language_code), - Toast.LENGTH_SHORT - ).show() } - } - d.dismiss() - dialog.dismiss() - } - alertDialog.show() + d.dismiss() + dialog.dismiss() + } + alertDialog.show() + } + dialog.dismiss() } - dialog.dismiss() - } dialog.show() } binding.btQuality.setOnClickListener { var checkedIndex = -1 - val dialog = MaterialAlertDialogBuilder(requireContext()) - .setSingleChoiceItems(QUALITY.items, -1) { _, which -> - checkedIndex = which - } - .setTitle(getString(R.string.quality)) - .setNegativeButton(getString(R.string.cancel)) { dialog, _ -> - dialog.dismiss() - } - .setPositiveButton(getString(R.string.change)) { dialog, _ -> - if (checkedIndex != -1) { - viewModel.changeQuality(checkedIndex) + val dialog = + MaterialAlertDialogBuilder(requireContext()) + .setSingleChoiceItems(QUALITY.items, -1) { _, which -> + checkedIndex = which + }.setTitle(getString(R.string.quality)) + .setNegativeButton(getString(R.string.cancel)) { dialog, _ -> + dialog.dismiss() + }.setPositiveButton(getString(R.string.change)) { dialog, _ -> + if (checkedIndex != -1) { + viewModel.changeQuality(checkedIndex) + } + dialog.dismiss() } - dialog.dismiss() - } dialog.show() - } binding.btVideoQuality.setOnClickListener { var checkedIndex = -1 - val dialog = MaterialAlertDialogBuilder(requireContext()) - .setSingleChoiceItems(VIDEO_QUALITY.items, -1) { _, which -> - checkedIndex = which - } - .setTitle(getString(R.string.quality_video)) - .setNegativeButton(getString(R.string.cancel)) { dialog, _ -> - dialog.dismiss() - } - .setPositiveButton(getString(R.string.change)) { dialog, _ -> - if (checkedIndex != -1) { - viewModel.changeVideoQuality(checkedIndex) + val dialog = + MaterialAlertDialogBuilder(requireContext()) + .setSingleChoiceItems(VIDEO_QUALITY.items, -1) { _, which -> + checkedIndex = which + }.setTitle(getString(R.string.quality_video)) + .setNegativeButton(getString(R.string.cancel)) { dialog, _ -> + dialog.dismiss() + }.setPositiveButton(getString(R.string.change)) { dialog, _ -> + if (checkedIndex != -1) { + viewModel.changeVideoQuality(checkedIndex) + } + dialog.dismiss() } - dialog.dismiss() - } dialog.show() } binding.btMainLyricsProvider.setOnClickListener { var checkedIndex = -1 - val dialog = MaterialAlertDialogBuilder(requireContext()) - .setTitle(getString(R.string.main_lyrics_provider)) - .setSingleChoiceItems(LYRICS_PROVIDER.items, -1) { _, which -> - checkedIndex = which - } - .setNegativeButton(getString(R.string.cancel)) { dialog, _ -> - dialog.dismiss() - } - .setPositiveButton(getString(R.string.change)) { dialog, _ -> - if (checkedIndex != -1) { - if (checkedIndex == 0) { - viewModel.setLyricsProvider(DataStoreManager.MUSIXMATCH) - binding.tvMainLyricsProvider.text = DataStoreManager.MUSIXMATCH - } else if (checkedIndex == 1){ - viewModel.setLyricsProvider(DataStoreManager.YOUTUBE) - binding.tvMainLyricsProvider.text = DataStoreManager.YOUTUBE + val dialog = + MaterialAlertDialogBuilder(requireContext()) + .setTitle(getString(R.string.main_lyrics_provider)) + .setSingleChoiceItems(LYRICS_PROVIDER.items, -1) { _, which -> + checkedIndex = which + }.setNegativeButton(getString(R.string.cancel)) { dialog, _ -> + dialog.dismiss() + }.setPositiveButton(getString(R.string.change)) { dialog, _ -> + if (checkedIndex != -1) { + if (checkedIndex == 0) { + viewModel.setLyricsProvider(DataStoreManager.MUSIXMATCH) + binding.tvMainLyricsProvider.text = DataStoreManager.MUSIXMATCH + } else if (checkedIndex == 1) { + viewModel.setLyricsProvider(DataStoreManager.YOUTUBE) + binding.tvMainLyricsProvider.text = DataStoreManager.YOUTUBE + } } + viewModel.getLyricsProvider() + dialog.dismiss() } - viewModel.getLyricsProvider() - dialog.dismiss() - } dialog.show() } - binding.btTranslationLanguage.setOnClickListener{ + binding.btTranslationLanguage.setOnClickListener { val materialAlertDialogBuilder = MaterialAlertDialogBuilder(requireContext()) materialAlertDialogBuilder.setTitle(getString(R.string.translation_language)) materialAlertDialogBuilder.setMessage(getString(R.string.translation_language_message)) @@ -775,14 +815,13 @@ class SettingsFragment : Fragment() { if (editText.text.toString().isNotEmpty()) { if (editText.text.toString().length == 2) { viewModel.setTranslationLanguage(editText.text.toString()) - } - else { + } else { Toast.makeText(requireContext(), getString(R.string.invalid_language_code), Toast.LENGTH_SHORT).show() } - } - else { + } else { if (viewModel.language.value != null && viewModel.language.value!!.length >= 2) { - viewModel.language.value?.slice(0..1) + viewModel.language.value + ?.slice(0..1) ?.let { it1 -> viewModel.setTranslationLanguage(it1) } } } @@ -792,71 +831,70 @@ class SettingsFragment : Fragment() { } binding.btStorageDownloadedCache.setOnClickListener { - val dialog = MaterialAlertDialogBuilder(requireContext()) - .setTitle(getString(R.string.clear_downloaded_cache)) - .setNegativeButton(getString(R.string.cancel)) { dialog, _ -> - dialog.dismiss() - } - .setPositiveButton(getString(R.string.clear)) { dialog, _ -> - viewModel.clearDownloadedCache() - dialog.dismiss() - } + val dialog = + MaterialAlertDialogBuilder(requireContext()) + .setTitle(getString(R.string.clear_downloaded_cache)) + .setNegativeButton(getString(R.string.cancel)) { dialog, _ -> + dialog.dismiss() + }.setPositiveButton(getString(R.string.clear)) { dialog, _ -> + viewModel.clearDownloadedCache() + dialog.dismiss() + } dialog.show() } binding.btStorageThumbnailCache.setOnClickListener { - val dialog = MaterialAlertDialogBuilder(requireContext()) - .setTitle(getString(R.string.clear_thumbnail_cache)) - .setNegativeButton(getString(R.string.cancel)) { dialog, _ -> - dialog.dismiss() - } - .setPositiveButton(getString(R.string.clear)) { dialog, _ -> - viewModel.clearThumbnailCache() - dialog.dismiss() - } + val dialog = + MaterialAlertDialogBuilder(requireContext()) + .setTitle(getString(R.string.clear_thumbnail_cache)) + .setNegativeButton(getString(R.string.cancel)) { dialog, _ -> + dialog.dismiss() + }.setPositiveButton(getString(R.string.clear)) { dialog, _ -> + viewModel.clearThumbnailCache() + dialog.dismiss() + } dialog.show() } binding.btCategoriesSponsorBlock.setOnClickListener { Log.d("Check category", viewModel.sponsorBlockCategories.value.toString()) val selectedItem: ArrayList = arrayListOf() - val item: Array = Array(9) {i -> - getString(SPONSOR_BLOCK.listName[i]) - } - - val checked = BooleanArray(9) { i -> - if (!viewModel.sponsorBlockCategories.value.isNullOrEmpty()) { - viewModel.sponsorBlockCategories.value!!.contains(SPONSOR_BLOCK.list[i].toString()) + val item: Array = + Array(9) { i -> + getString(SPONSOR_BLOCK.listName[i]) } - else { - false - } - } - val dialog = MaterialAlertDialogBuilder(requireContext()) - .setTitle("Category") - .setMultiChoiceItems(item, checked) { _, i, b -> - if (b) { - if (!selectedItem.contains(SPONSOR_BLOCK.list[i].toString())) { - selectedItem.add(SPONSOR_BLOCK.list[i].toString()) - } + val checked = + BooleanArray(9) { i -> + if (!viewModel.sponsorBlockCategories.value.isNullOrEmpty()) { + viewModel.sponsorBlockCategories.value!!.contains(SPONSOR_BLOCK.list[i].toString()) } else { - if (selectedItem.contains(SPONSOR_BLOCK.list[i].toString())) { - selectedItem.remove(SPONSOR_BLOCK.list[i].toString()) - } + false } } - .setPositiveButton(getString(R.string.save)) { dialog, _ -> - viewModel.setSponsorBlockCategories(selectedItem) - Log.d("Check category", selectedItem.toString()) - viewModel.getSponsorBlockCategories() - } - .setNegativeButton(R.string.cancel) { dialog, _ -> - dialog.dismiss() - } + + val dialog = + MaterialAlertDialogBuilder(requireContext()) + .setTitle("Category") + .setMultiChoiceItems(item, checked) { _, i, b -> + if (b) { + if (!selectedItem.contains(SPONSOR_BLOCK.list[i].toString())) { + selectedItem.add(SPONSOR_BLOCK.list[i].toString()) + } + } else { + if (selectedItem.contains(SPONSOR_BLOCK.list[i].toString())) { + selectedItem.remove(SPONSOR_BLOCK.list[i].toString()) + } + } + }.setPositiveButton(getString(R.string.save)) { dialog, _ -> + viewModel.setSponsorBlockCategories(selectedItem) + Log.d("Check category", selectedItem.toString()) + viewModel.getSponsorBlockCategories() + }.setNegativeButton(R.string.cancel) { dialog, _ -> + dialog.dismiss() + } dialog.show() } - binding.topAppBar.setNavigationOnClickListener { findNavController().popBackStack() } @@ -887,22 +925,22 @@ class SettingsFragment : Fragment() { val test = viewModel.playVideoInsteadOfAudio.value val checkReal = (test == DataStoreManager.TRUE) != checked if (checkReal) { - val dialog = MaterialAlertDialogBuilder(requireContext()) - .setTitle(getString(R.string.warning)) - .setMessage(getString(R.string.play_video_instead_of_audio_warning)) - .setNegativeButton(getString(R.string.cancel)) { dialog, _ -> - binding.swEnableVideo.isChecked = false - dialog.dismiss() - } - .setPositiveButton(getString(R.string.change)) { dialog, _ -> - viewModel.clearPlayerCache() - if (checked) { - viewModel.setPlayVideoInsteadOfAudio(true) - } else { - viewModel.setPlayVideoInsteadOfAudio(false) + val dialog = + MaterialAlertDialogBuilder(requireContext()) + .setTitle(getString(R.string.warning)) + .setMessage(getString(R.string.play_video_instead_of_audio_warning)) + .setNegativeButton(getString(R.string.cancel)) { dialog, _ -> + binding.swEnableVideo.isChecked = false + dialog.dismiss() + }.setPositiveButton(getString(R.string.change)) { dialog, _ -> + viewModel.clearPlayerCache() + if (checked) { + viewModel.setPlayVideoInsteadOfAudio(true) + } else { + viewModel.setPlayVideoInsteadOfAudio(false) + } + dialog.dismiss() } - dialog.dismiss() - } dialog.show() } } @@ -950,15 +988,15 @@ class SettingsFragment : Fragment() { } binding.btSpotifyLogin.setOnClickListener { if (runBlocking { viewModel.spotifyLogIn.value }) { - val subAlertDialogBuilder = MaterialAlertDialogBuilder(requireContext()) - .setTitle(getString(R.string.warning)) - .setMessage(getString(R.string.log_out_warning)) - .setNegativeButton(getString(R.string.cancel)) { dialog, _ -> - dialog.dismiss() - } - .setPositiveButton(getString(R.string.log_out_from_spotify)) { dialog, _ -> - viewModel.setSpotifyLogIn(false) - } + val subAlertDialogBuilder = + MaterialAlertDialogBuilder(requireContext()) + .setTitle(getString(R.string.warning)) + .setMessage(getString(R.string.log_out_warning)) + .setNegativeButton(getString(R.string.cancel)) { dialog, _ -> + dialog.dismiss() + }.setPositiveButton(getString(R.string.log_out_from_spotify)) { dialog, _ -> + viewModel.setSpotifyLogIn(false) + } subAlertDialogBuilder.show() } else { findNavController().navigateSafe(R.id.action_global_spotifyLogInFragment) @@ -979,7 +1017,6 @@ class SettingsFragment : Fragment() { } } binding.bt3rdPartyLibraries.setOnClickListener { - val inputStream = requireContext().resources.openRawResource(R.raw.aboutlibraries) val scanner = Scanner(inputStream).useDelimiter("\\A") val stringBuilder = StringBuilder() @@ -988,19 +1025,20 @@ class SettingsFragment : Fragment() { } Log.w("AboutLibraries", stringBuilder.toString()) val localLib = Libs.Builder().withJson(stringBuilder.toString()).build() - val intent = LibsBuilder() - .withLicenseShown(true) - .withVersionShown(true) - .withActivityTitle(getString(R.string.third_party_libraries)) - .withSearchEnabled(true) - .withEdgeToEdge(true) - .withLibs( - localLib - ) - .intent(requireContext()) + val intent = + LibsBuilder() + .withLicenseShown(true) + .withVersionShown(true) + .withActivityTitle(getString(R.string.third_party_libraries)) + .withSearchEnabled(true) + .withEdgeToEdge(true) + .withLibs( + localLib, + ).intent(requireContext()) startActivity(intent) } } + private fun browseFiles(dir: File): Long { var dirSize: Long = 0 if (!dir.listFiles().isNullOrEmpty()) { @@ -1013,6 +1051,7 @@ class SettingsFragment : Fragment() { } return dirSize } + private fun drawDataStat() { lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.CREATED) { @@ -1020,7 +1059,6 @@ class SettingsFragment : Fragment() { val mStorageStatsManager = getSystemService(requireContext(), StorageStatsManager::class.java) if (mStorageStatsManager != null) { - val totalByte = mStorageStatsManager.getTotalBytes(StorageManager.UUID_DEFAULT) val freeSpace = @@ -1030,14 +1068,19 @@ class SettingsFragment : Fragment() { val otherApp = simpMusicSize.let { usedSpace.minus(it) } val databaseSize = simpMusicSize - viewModel.playerCache.cacheSpace - viewModel.downloadCache.cacheSpace - if (totalByte == freeSpace + otherApp + databaseSize + viewModel.playerCache.cacheSpace + viewModel.downloadCache.cacheSpace) { + if (totalByte == + freeSpace + otherApp + databaseSize + viewModel.playerCache.cacheSpace + viewModel.downloadCache.cacheSpace + ) { (binding.flexBox.getChildAt(0).layoutParams as FlexboxLayout.LayoutParams).flexBasisPercent = otherApp.toFloat().div(totalByte.toFloat()) (binding.flexBox.getChildAt(1).layoutParams as FlexboxLayout.LayoutParams).flexBasisPercent = - viewModel.downloadCache.cacheSpace.toFloat() + viewModel.downloadCache.cacheSpace + .toFloat() .div(totalByte.toFloat()) (binding.flexBox.getChildAt(2).layoutParams as FlexboxLayout.LayoutParams).flexBasisPercent = - viewModel.playerCache.cacheSpace.toFloat().div(totalByte.toFloat()) + viewModel.playerCache.cacheSpace + .toFloat() + .div(totalByte.toFloat()) (binding.flexBox.getChildAt(3).layoutParams as FlexboxLayout.LayoutParams).flexBasisPercent = databaseSize.toFloat().div(totalByte.toFloat()) (binding.flexBox.getChildAt(4).layoutParams as FlexboxLayout.LayoutParams).flexBasisPercent = diff --git a/app/src/main/java/com/maxrave/simpmusic/ui/fragment/library/DownloadedFragment.kt b/app/src/main/java/com/maxrave/simpmusic/ui/fragment/library/DownloadedFragment.kt index 385851b2..81353079 100644 --- a/app/src/main/java/com/maxrave/simpmusic/ui/fragment/library/DownloadedFragment.kt +++ b/app/src/main/java/com/maxrave/simpmusic/ui/fragment/library/DownloadedFragment.kt @@ -45,17 +45,15 @@ import com.maxrave.simpmusic.service.QueueData import com.maxrave.simpmusic.service.test.download.MusicDownloadService import com.maxrave.simpmusic.viewModel.DownloadedViewModel import com.maxrave.simpmusic.viewModel.SharedViewModel -import dagger.hilt.android.AndroidEntryPoint import dev.chrisbanes.insetter.applyInsetter import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChangedBy import kotlinx.coroutines.launch import java.time.LocalDateTime -@AndroidEntryPoint class DownloadedFragment : Fragment() { private var _binding: FragmentDownloadedBinding? = null - private val binding get() = _binding!! + val binding get() = _binding!! private val viewModel by viewModels() private val sharedViewModel by activityViewModels() diff --git a/app/src/main/java/com/maxrave/simpmusic/ui/fragment/library/FavoriteFragment.kt b/app/src/main/java/com/maxrave/simpmusic/ui/fragment/library/FavoriteFragment.kt index 71fae539..8c8d8bde 100644 --- a/app/src/main/java/com/maxrave/simpmusic/ui/fragment/library/FavoriteFragment.kt +++ b/app/src/main/java/com/maxrave/simpmusic/ui/fragment/library/FavoriteFragment.kt @@ -48,18 +48,16 @@ import com.maxrave.simpmusic.service.QueueData import com.maxrave.simpmusic.service.test.download.MusicDownloadService import com.maxrave.simpmusic.viewModel.FavoriteViewModel import com.maxrave.simpmusic.viewModel.SharedViewModel -import dagger.hilt.android.AndroidEntryPoint import dev.chrisbanes.insetter.applyInsetter import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChangedBy import kotlinx.coroutines.launch import java.time.LocalDateTime -@AndroidEntryPoint class FavoriteFragment : Fragment() { private var _binding: FragmentFavoriteBinding? = null - private val binding get() = _binding!! + val binding get() = _binding!! private val viewModel by viewModels() private val sharedViewModel by activityViewModels() @@ -287,7 +285,10 @@ class FavoriteFragment : Fragment() { if (playlist.tracks != null) { tempTrack.addAll(playlist.tracks) } - if (!tempTrack.contains(song.videoId) && playlist.syncedWithYouTubePlaylist == 1 && playlist.youtubePlaylistId != null) { + if (!tempTrack.contains(song.videoId) && + playlist.syncedWithYouTubePlaylist == 1 && + playlist.youtubePlaylistId != null + ) { viewModel.addToYouTubePlaylist(playlist.id, playlist.youtubePlaylistId, song.videoId) } if (!tempTrack.contains(song.videoId)) { diff --git a/app/src/main/java/com/maxrave/simpmusic/ui/fragment/library/FollowedFragment.kt b/app/src/main/java/com/maxrave/simpmusic/ui/fragment/library/FollowedFragment.kt index 977dbf32..8e946fe6 100644 --- a/app/src/main/java/com/maxrave/simpmusic/ui/fragment/library/FollowedFragment.kt +++ b/app/src/main/java/com/maxrave/simpmusic/ui/fragment/library/FollowedFragment.kt @@ -14,13 +14,11 @@ import com.maxrave.simpmusic.data.db.entities.ArtistEntity import com.maxrave.simpmusic.databinding.FragmentFollowedBinding import com.maxrave.simpmusic.extension.navigateSafe import com.maxrave.simpmusic.viewModel.FollowedViewModel -import dagger.hilt.android.AndroidEntryPoint import dev.chrisbanes.insetter.applyInsetter -@AndroidEntryPoint class FollowedFragment : Fragment() { private var _binding: FragmentFollowedBinding? = null - private val binding get() = _binding!! + val binding get() = _binding!! private val viewModel by viewModels() diff --git a/app/src/main/java/com/maxrave/simpmusic/ui/fragment/library/LibraryFragment.kt b/app/src/main/java/com/maxrave/simpmusic/ui/fragment/library/LibraryFragment.kt index 3e07304b..132d6385 100644 --- a/app/src/main/java/com/maxrave/simpmusic/ui/fragment/library/LibraryFragment.kt +++ b/app/src/main/java/com/maxrave/simpmusic/ui/fragment/library/LibraryFragment.kt @@ -47,17 +47,15 @@ import com.maxrave.simpmusic.service.PlaylistType import com.maxrave.simpmusic.service.QueueData import com.maxrave.simpmusic.viewModel.LibraryViewModel import com.maxrave.simpmusic.viewModel.SharedViewModel -import dagger.hilt.android.AndroidEntryPoint import dev.chrisbanes.insetter.applyInsetter import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChangedBy import kotlinx.coroutines.launch import java.time.LocalDateTime -@AndroidEntryPoint class LibraryFragment : Fragment() { private var _binding: FragmentLibraryBinding? = null - private val binding get() = _binding!! + val binding get() = _binding!! private val viewModel by viewModels() private val sharedViewModel by activityViewModels() diff --git a/app/src/main/java/com/maxrave/simpmusic/ui/fragment/library/MostPlayedFragment.kt b/app/src/main/java/com/maxrave/simpmusic/ui/fragment/library/MostPlayedFragment.kt index 23c5c526..c57e5b4a 100644 --- a/app/src/main/java/com/maxrave/simpmusic/ui/fragment/library/MostPlayedFragment.kt +++ b/app/src/main/java/com/maxrave/simpmusic/ui/fragment/library/MostPlayedFragment.kt @@ -47,17 +47,15 @@ import com.maxrave.simpmusic.service.QueueData import com.maxrave.simpmusic.service.test.download.MusicDownloadService import com.maxrave.simpmusic.viewModel.MostPlayedViewModel import com.maxrave.simpmusic.viewModel.SharedViewModel -import dagger.hilt.android.AndroidEntryPoint import dev.chrisbanes.insetter.applyInsetter import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChangedBy import kotlinx.coroutines.launch import java.time.LocalDateTime -@AndroidEntryPoint class MostPlayedFragment: Fragment() { private var _binding: FragmentMostPlayedBinding? = null - private val binding get() = _binding!! + val binding get() = _binding!! private val viewModel by viewModels() private val sharedViewModel by activityViewModels() diff --git a/app/src/main/java/com/maxrave/simpmusic/ui/fragment/login/LogInFragment.kt b/app/src/main/java/com/maxrave/simpmusic/ui/fragment/login/LogInFragment.kt index 444d0570..d30cb905 100644 --- a/app/src/main/java/com/maxrave/simpmusic/ui/fragment/login/LogInFragment.kt +++ b/app/src/main/java/com/maxrave/simpmusic/ui/fragment/login/LogInFragment.kt @@ -26,16 +26,14 @@ import com.maxrave.simpmusic.service.SimpleMediaService import com.maxrave.simpmusic.viewModel.LogInViewModel import com.maxrave.simpmusic.viewModel.SettingsViewModel import com.maxrave.simpmusic.viewModel.SharedViewModel -import dagger.hilt.android.AndroidEntryPoint import dev.chrisbanes.insetter.applyInsetter import kotlinx.coroutines.flow.first import kotlinx.coroutines.runBlocking -@AndroidEntryPoint class LogInFragment : Fragment() { private var _binding: FragmentLogInBinding? = null - private val binding get() = _binding!! + val binding get() = _binding!! private val viewModel by viewModels() private val settingsViewModel by activityViewModels() diff --git a/app/src/main/java/com/maxrave/simpmusic/ui/fragment/login/MusixmatchFragment.kt b/app/src/main/java/com/maxrave/simpmusic/ui/fragment/login/MusixmatchFragment.kt index 7824536c..bdbebc9c 100644 --- a/app/src/main/java/com/maxrave/simpmusic/ui/fragment/login/MusixmatchFragment.kt +++ b/app/src/main/java/com/maxrave/simpmusic/ui/fragment/login/MusixmatchFragment.kt @@ -22,7 +22,6 @@ import com.maxrave.simpmusic.extension.isMyServiceRunning import com.maxrave.simpmusic.service.SimpleMediaService import com.maxrave.simpmusic.viewModel.MusixmatchViewModel import com.maxrave.simpmusic.viewModel.SharedViewModel -import dagger.hilt.android.AndroidEntryPoint import dev.chrisbanes.insetter.applyInsetter import kotlinx.coroutines.delay import kotlinx.coroutines.flow.collectLatest @@ -30,7 +29,6 @@ import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking -@AndroidEntryPoint class MusixmatchFragment : Fragment() { private var _binding: FragmentMusixmatchBinding? = null @@ -62,7 +60,7 @@ class MusixmatchFragment : Fragment() { miniplayer.visibility = View.GONE binding.btLogIn.setOnClickListener { if (binding.etEmail.text.toString().isNotEmpty() && binding.etPassword.text.toString().isNotEmpty()) { - viewModel.loggin(binding.etEmail.text.toString(), binding.etPassword.text.toString()) + viewModel.login(binding.etEmail.text.toString(), binding.etPassword.text.toString()) } else { Toast.makeText(requireContext(), diff --git a/app/src/main/java/com/maxrave/simpmusic/ui/fragment/login/SpotifyLogInFragment.kt b/app/src/main/java/com/maxrave/simpmusic/ui/fragment/login/SpotifyLogInFragment.kt index c88dd85b..f73c14ec 100644 --- a/app/src/main/java/com/maxrave/simpmusic/ui/fragment/login/SpotifyLogInFragment.kt +++ b/app/src/main/java/com/maxrave/simpmusic/ui/fragment/login/SpotifyLogInFragment.kt @@ -26,16 +26,14 @@ import com.maxrave.simpmusic.service.SimpleMediaService import com.maxrave.simpmusic.viewModel.LogInViewModel import com.maxrave.simpmusic.viewModel.SettingsViewModel import com.maxrave.simpmusic.viewModel.SharedViewModel -import dagger.hilt.android.AndroidEntryPoint import dev.chrisbanes.insetter.applyInsetter import kotlinx.coroutines.flow.first import kotlinx.coroutines.runBlocking -@AndroidEntryPoint class SpotifyLogInFragment : Fragment() { private var _binding: FragmentSpotifyLogInBinding? = null - private val binding get() = _binding!! + val binding get() = _binding!! private val viewModel by viewModels() private val settingsViewModel by activityViewModels() diff --git a/app/src/main/java/com/maxrave/simpmusic/ui/fragment/other/AlbumFragment.kt b/app/src/main/java/com/maxrave/simpmusic/ui/fragment/other/AlbumFragment.kt index 26081bdb..d076f8b6 100644 --- a/app/src/main/java/com/maxrave/simpmusic/ui/fragment/other/AlbumFragment.kt +++ b/app/src/main/java/com/maxrave/simpmusic/ui/fragment/other/AlbumFragment.kt @@ -65,7 +65,6 @@ import com.maxrave.simpmusic.service.test.download.MusicDownloadService import com.maxrave.simpmusic.utils.Resource import com.maxrave.simpmusic.viewModel.AlbumViewModel import com.maxrave.simpmusic.viewModel.SharedViewModel -import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.delay import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine @@ -76,14 +75,13 @@ import java.time.LocalDateTime import kotlin.math.abs import kotlin.random.Random -@AndroidEntryPoint @UnstableApi class AlbumFragment : Fragment() { private val viewModel by activityViewModels() private val sharedViewModel by activityViewModels() private var _binding: FragmentAlbumBinding? = null - private val binding get() = _binding!! + val binding get() = _binding!! private var gradientDrawable: GradientDrawable? = null private var toolbarBackground: Int? = null diff --git a/app/src/main/java/com/maxrave/simpmusic/ui/fragment/other/ArtistFragment.kt b/app/src/main/java/com/maxrave/simpmusic/ui/fragment/other/ArtistFragment.kt index 37a9fbf9..de1e291d 100644 --- a/app/src/main/java/com/maxrave/simpmusic/ui/fragment/other/ArtistFragment.kt +++ b/app/src/main/java/com/maxrave/simpmusic/ui/fragment/other/ArtistFragment.kt @@ -63,7 +63,6 @@ import com.maxrave.simpmusic.service.QueueData import com.maxrave.simpmusic.utils.Resource import com.maxrave.simpmusic.viewModel.ArtistViewModel import com.maxrave.simpmusic.viewModel.SharedViewModel -import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged @@ -73,12 +72,11 @@ import kotlinx.coroutines.launch import java.time.LocalDateTime import kotlin.math.abs -@AndroidEntryPoint class ArtistFragment : Fragment() { private val viewModel by viewModels() private val sharedViewModel by activityViewModels() private var _binding: FragmentArtistBinding? = null - private val binding get() = _binding!! + val binding get() = _binding!! private lateinit var popularAdapter: PopularAdapter private lateinit var singlesAdapter: SinglesAdapter diff --git a/app/src/main/java/com/maxrave/simpmusic/ui/fragment/other/CreditFragment.kt b/app/src/main/java/com/maxrave/simpmusic/ui/fragment/other/CreditFragment.kt index acb9d6eb..8f407b05 100644 --- a/app/src/main/java/com/maxrave/simpmusic/ui/fragment/other/CreditFragment.kt +++ b/app/src/main/java/com/maxrave/simpmusic/ui/fragment/other/CreditFragment.kt @@ -3,23 +3,19 @@ package com.maxrave.simpmusic.ui.fragment.other import android.content.Intent import android.net.Uri import android.os.Bundle -import androidx.fragment.app.Fragment import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.view.WindowInsets import androidx.core.view.WindowInsetsCompat +import androidx.fragment.app.Fragment import androidx.navigation.fragment.findNavController -import com.maxrave.simpmusic.R import com.maxrave.simpmusic.databinding.FragmentCreditBinding -import dagger.hilt.android.AndroidEntryPoint import dev.chrisbanes.insetter.Insetter -@AndroidEntryPoint class CreditFragment : Fragment() { private var _binding: FragmentCreditBinding? = null - private val binding get() = _binding!! + val binding get() = _binding!! override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, diff --git a/app/src/main/java/com/maxrave/simpmusic/ui/fragment/other/LocalPlaylistFragment.kt b/app/src/main/java/com/maxrave/simpmusic/ui/fragment/other/LocalPlaylistFragment.kt index e05a33ec..54aaf238 100644 --- a/app/src/main/java/com/maxrave/simpmusic/ui/fragment/other/LocalPlaylistFragment.kt +++ b/app/src/main/java/com/maxrave/simpmusic/ui/fragment/other/LocalPlaylistFragment.kt @@ -1,5 +1,6 @@ package com.maxrave.simpmusic.ui.fragment.other +import android.annotation.SuppressLint import android.graphics.Bitmap import android.graphics.Color import android.graphics.drawable.ColorDrawable @@ -36,15 +37,13 @@ import com.maxrave.simpmusic.ui.screen.library.PlaylistScreen import com.maxrave.simpmusic.ui.theme.AppTheme import com.maxrave.simpmusic.viewModel.LocalPlaylistViewModel import com.maxrave.simpmusic.viewModel.SharedViewModel -import dagger.hilt.android.AndroidEntryPoint import java.time.format.DateTimeFormatter -@AndroidEntryPoint @UnstableApi @ExperimentalFoundationApi class LocalPlaylistFragment : Fragment() { private var _binding: FragmentLocalPlaylistBinding? = null - private val binding get() = _binding!! + val binding get() = _binding!! private val viewModel by activityViewModels() private val sharedViewModel by activityViewModels() @@ -71,6 +70,7 @@ class LocalPlaylistFragment : Fragment() { } } + @SuppressLint("UnusedMaterial3ScaffoldPaddingParameter") @UnstableApi override fun onViewCreated( view: View, diff --git a/app/src/main/java/com/maxrave/simpmusic/ui/fragment/other/MoreAlbumsFragment.kt b/app/src/main/java/com/maxrave/simpmusic/ui/fragment/other/MoreAlbumsFragment.kt index 2ffbbe2f..e5dca926 100644 --- a/app/src/main/java/com/maxrave/simpmusic/ui/fragment/other/MoreAlbumsFragment.kt +++ b/app/src/main/java/com/maxrave/simpmusic/ui/fragment/other/MoreAlbumsFragment.kt @@ -15,14 +15,12 @@ import com.maxrave.simpmusic.R import com.maxrave.simpmusic.adapter.artist.MoreAlbumAdapter import com.maxrave.simpmusic.databinding.FragmentMoreAlbumsBinding import com.maxrave.simpmusic.viewModel.MoreAlbumsViewModel -import dagger.hilt.android.AndroidEntryPoint import dev.chrisbanes.insetter.applyInsetter import kotlinx.coroutines.launch -@AndroidEntryPoint class MoreAlbumsFragment : Fragment() { private var _binding: FragmentMoreAlbumsBinding? = null - private val binding get() = _binding!! + val binding get() = _binding!! private val viewModel by viewModels() private var id: String? = null diff --git a/app/src/main/java/com/maxrave/simpmusic/ui/fragment/other/PlaylistFragment.kt b/app/src/main/java/com/maxrave/simpmusic/ui/fragment/other/PlaylistFragment.kt index 017f17b4..88bf84ed 100644 --- a/app/src/main/java/com/maxrave/simpmusic/ui/fragment/other/PlaylistFragment.kt +++ b/app/src/main/java/com/maxrave/simpmusic/ui/fragment/other/PlaylistFragment.kt @@ -61,7 +61,6 @@ import com.maxrave.simpmusic.service.test.download.MusicDownloadService import com.maxrave.simpmusic.viewModel.PlaylistUIState import com.maxrave.simpmusic.viewModel.PlaylistViewModel import com.maxrave.simpmusic.viewModel.SharedViewModel -import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.delay import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine @@ -74,14 +73,13 @@ import java.time.LocalDateTime import kotlin.math.abs import kotlin.random.Random -@AndroidEntryPoint class PlaylistFragment : Fragment() { - private val TAG = "PlaylistFragment" - + private val tag = "PlaylistFragment" + private val viewModel by activityViewModels() private val sharedViewModel by activityViewModels() private var _binding: FragmentPlaylistBinding? = null - private val binding get() = _binding!! + val binding get() = _binding!! private var gradientDrawable: GradientDrawable? = null private var toolbarBackground: Int? = null @@ -102,7 +100,7 @@ class PlaylistFragment : Fragment() { requireArguments().clear() setStatusBarsColor( ContextCompat.getColor(requireContext(), R.color.colorPrimaryDark), - requireActivity() + requireActivity(), ) _binding = null } @@ -135,7 +133,7 @@ class PlaylistFragment : Fragment() { val downloaded = arguments?.getInt("downloaded") val radioId = arguments?.getString("radioId") val channelId = arguments?.getString("channelId") - Log.w(TAG, "radioId: $radioId") + Log.w(tag, "radioId: $radioId") val videoId = arguments?.getString("videoId") // 3 truong hop: /* @@ -145,25 +143,22 @@ class PlaylistFragment : Fragment() { 4. Luc lai tu ViewModel */ if (id != null || radioId != null) { - Log.w(TAG, "id: $id") - Log.w(TAG, "radioId: $radioId") + Log.w(tag, "id: $id") + Log.w(tag, "radioId: $radioId") if (radioId != null || id?.startsWith("RDEM") == true || id?.startsWith("RDAMVM") == true - ) { + ) { viewModel.updateIsRadio(true) if (radioId != null) { fetchDataWithRadio(radioId, videoId, channelId) - } - else if (id != null) { + } else if (id != null) { fetchDataWithRadio(id) } - } - else if (id != null && id.startsWith("RDAT")) { + } else if (id != null && id.startsWith("RDAT")) { viewModel.updateIsRadio(true) fetchRDATRadio(id) - } - else if (id != null) { + } else if (id != null) { viewModel.updateIsRadio(false) if (!requireArguments().getBoolean("youtube")) { viewModel.checkSuccess() @@ -288,13 +283,13 @@ class PlaylistFragment : Fragment() { } } if (requireArguments().getBoolean("youtube")) { - Log.w(TAG, "id check: $id") + Log.w(tag, "id check: $id") moreView.btSync.visibility = View.VISIBLE viewModel.checkSyncedPlaylist(id) lifecycleScope.launch { viewModel.localPlaylistIfYouTubePlaylist.collectLatest { ytPlaylist -> - Log.w(TAG, "ytPlaylist: ${ytPlaylist?.youtubePlaylistId}") - Log.w(TAG, "id: $id") + Log.w(tag, "ytPlaylist: ${ytPlaylist?.youtubePlaylistId}") + Log.w(tag, "id: $id") if (ytPlaylist != null) { val tempId = ytPlaylist.youtubePlaylistId if (tempId == id) { @@ -371,22 +366,27 @@ class PlaylistFragment : Fragment() { sharedViewModel.simpleMediaServiceHandler?.setQueueData( QueueData( listTracks = (viewModel.playlistBrowse.value?.tracks ?: arrayListOf()) as ArrayList, - firstPlayedTrack = viewModel.playlistBrowse.value - ?.tracks?.get(0), - playlistId = viewModel.playlistBrowse.value - ?.id - ?.replaceFirst("VL", "") ?: "", + firstPlayedTrack = + viewModel.playlistBrowse.value + ?.tracks + ?.get(0), + playlistId = + viewModel.playlistBrowse.value + ?.id + ?.replaceFirst("VL", "") ?: "", playlistName = "Playlist \"${viewModel.playlistBrowse.value?.title}\"", playlistType = PlaylistType.PLAYLIST, - continuation = null - ) + continuation = null, + ), ) viewModel.playlistBrowse.value - ?.tracks?.get(0)?.let { + ?.tracks + ?.get(0) + ?.let { sharedViewModel.loadMediaItemFromTrack( it, type = Config.PLAYLIST_CLICK, - index = 0 + index = 0, ) } } else if (viewModel.playlistEntity.value != null && @@ -395,20 +395,24 @@ class PlaylistFragment : Fragment() { sharedViewModel.simpleMediaServiceHandler?.setQueueData( QueueData( listTracks = viewModel.listTrack.value.toArrayListTrack(), - firstPlayedTrack = viewModel.listTrack.value.firstOrNull()?.toTrack(), - playlistId = viewModel.playlistEntity.value - ?.id - ?.replaceFirst("VL", "") ?: "", + firstPlayedTrack = + viewModel.listTrack.value + .firstOrNull() + ?.toTrack(), + playlistId = + viewModel.playlistEntity.value + ?.id + ?.replaceFirst("VL", "") ?: "", playlistName = "Playlist \"${viewModel.playlistEntity.value?.title}\"", playlistType = PlaylistType.PLAYLIST, - continuation = null - ) + continuation = null, + ), ) viewModel.listTrack.value.firstOrNull()?.let { sharedViewModel.loadMediaItemFromTrack( it.toTrack(), type = Config.PLAYLIST_CLICK, - index = 0 + index = 0, ) } } else { @@ -424,21 +428,27 @@ class PlaylistFragment : Fragment() { sharedViewModel.simpleMediaServiceHandler?.setQueueData( QueueData( listTracks = (viewModel.playlistBrowse.value?.tracks ?: arrayListOf()) as ArrayList, - firstPlayedTrack = viewModel.playlistBrowse.value - ?.tracks?.get(0), - playlistId = viewModel.playlistBrowse.value?.id?.replaceFirst("VL", "") ?: "", + firstPlayedTrack = + viewModel.playlistBrowse.value + ?.tracks + ?.get(0), + playlistId = + viewModel.playlistBrowse.value + ?.id + ?.replaceFirst("VL", "") ?: "", playlistName = "${viewModel.playlistBrowse.value?.title}", playlistType = PlaylistType.RADIO, - continuation = viewModel.radioContinuation.value?.let { - if (it.first == viewModel.playlistBrowse.value?.id) it.second else null - } - ) + continuation = + viewModel.radioContinuation.value?.let { + if (it.first == viewModel.playlistBrowse.value?.id) it.second else null + }, + ), ) viewModel.playlistBrowse.value?.tracks?.firstOrNull()?.let { sharedViewModel.loadMediaItemFromTrack( it, type = Config.PLAYLIST_CLICK, - index = 0 + index = 0, ) } } else if (viewModel.playlistEntity.value != null && @@ -447,22 +457,27 @@ class PlaylistFragment : Fragment() { sharedViewModel.simpleMediaServiceHandler?.setQueueData( QueueData( listTracks = viewModel.listTrack.value.toArrayListTrack(), - firstPlayedTrack = viewModel.listTrack.value.firstOrNull()?.toTrack(), - playlistId = viewModel.playlistBrowse.value - ?.id - ?.replaceFirst("VL", ""), + firstPlayedTrack = + viewModel.listTrack.value + .firstOrNull() + ?.toTrack(), + playlistId = + viewModel.playlistBrowse.value + ?.id + ?.replaceFirst("VL", ""), playlistName = "${viewModel.playlistBrowse.value?.title}", playlistType = PlaylistType.RADIO, - continuation = viewModel.radioContinuation.value?.let { - if (it.first == viewModel.playlistBrowse.value?.id) it.second else null - } - ) + continuation = + viewModel.radioContinuation.value?.let { + if (it.first == viewModel.playlistBrowse.value?.id) it.second else null + }, + ), ) viewModel.listTrack.value.firstOrNull()?.let { sharedViewModel.loadMediaItemFromTrack( it.toTrack(), type = Config.PLAYLIST_CLICK, - index = 0 + index = 0, ) } } else { @@ -484,22 +499,25 @@ class PlaylistFragment : Fragment() { sharedViewModel.simpleMediaServiceHandler?.setQueueData( QueueData( listTracks = (viewModel.playlistBrowse.value?.tracks ?: arrayListOf()) as ArrayList, - firstPlayedTrack = viewModel.playlistBrowse.value - ?.tracks?.get(position), - playlistId = viewModel.playlistBrowse.value - ?.id - ?.replaceFirst("VL", "") ?: "", + firstPlayedTrack = + viewModel.playlistBrowse.value + ?.tracks + ?.get(position), + playlistId = + viewModel.playlistBrowse.value + ?.id + ?.replaceFirst("VL", "") ?: "", playlistName = "Playlist \"${viewModel.playlistBrowse.value?.title}\"", playlistType = PlaylistType.PLAYLIST, - continuation = null - ) + continuation = null, + ), ) viewModel.playlistBrowse.value?.tracks?.get(position)?.let { - Log.w(TAG, "track: $it") + Log.w(tag, "track: $it") sharedViewModel.loadMediaItemFromTrack( it, type = Config.PLAYLIST_CLICK, - index = position + index = position, ) } } else if (viewModel.playlistEntity.value != null && @@ -508,21 +526,25 @@ class PlaylistFragment : Fragment() { sharedViewModel.simpleMediaServiceHandler?.setQueueData( QueueData( listTracks = viewModel.listTrack.value.toArrayListTrack(), - firstPlayedTrack = viewModel.listTrack.value.getOrNull(position)?.toTrack(), - playlistId = viewModel.playlistEntity.value - ?.id - ?.replaceFirst("VL", "") ?: "", + firstPlayedTrack = + viewModel.listTrack.value + .getOrNull(position) + ?.toTrack(), + playlistId = + viewModel.playlistEntity.value + ?.id + ?.replaceFirst("VL", "") ?: "", playlistName = "Playlist \"${viewModel.playlistEntity.value?.title}\"", playlistType = PlaylistType.PLAYLIST, - continuation = null - ) + continuation = null, + ), ) viewModel.listTrack.value.getOrNull(position)?.let { - Log.w(TAG, "track: $it") + Log.w(tag, "track: $it") sharedViewModel.loadMediaItemFromTrack( it.toTrack(), type = Config.PLAYLIST_CLICK, - index = position + index = position, ) } } else { @@ -538,24 +560,28 @@ class PlaylistFragment : Fragment() { sharedViewModel.simpleMediaServiceHandler?.setQueueData( QueueData( listTracks = (viewModel.playlistBrowse.value?.tracks ?: arrayListOf()) as ArrayList, - firstPlayedTrack = viewModel.playlistBrowse.value - ?.tracks?.get(position), - playlistId = viewModel.playlistBrowse.value - ?.id - ?.replaceFirst("VL", "") ?: "", + firstPlayedTrack = + viewModel.playlistBrowse.value + ?.tracks + ?.get(position), + playlistId = + viewModel.playlistBrowse.value + ?.id + ?.replaceFirst("VL", "") ?: "", playlistName = "${viewModel.playlistBrowse.value?.title}", playlistType = PlaylistType.RADIO, - continuation = viewModel.radioContinuation.value?.let { - if (it.first == viewModel.playlistBrowse.value?.id) it.second else null - } - ) + continuation = + viewModel.radioContinuation.value?.let { + if (it.first == viewModel.playlistBrowse.value?.id) it.second else null + }, + ), ) viewModel.playlistBrowse.value?.tracks?.get(position)?.let { - Log.w(TAG, "track: $it") + Log.w(tag, "track: $it") sharedViewModel.loadMediaItemFromTrack( it, type = Config.PLAYLIST_CLICK, - index = position + index = position, ) } } else if (viewModel.playlistEntity.value != null && @@ -564,24 +590,28 @@ class PlaylistFragment : Fragment() { sharedViewModel.simpleMediaServiceHandler?.setQueueData( QueueData( listTracks = viewModel.listTrack.value.toArrayListTrack(), - firstPlayedTrack = viewModel.listTrack.value.getOrNull(position)?.toTrack(), - playlistId = viewModel.playlistBrowse.value - ?.id - ?.replaceFirst("VL", ""), + firstPlayedTrack = + viewModel.listTrack.value + .getOrNull(position) + ?.toTrack(), + playlistId = + viewModel.playlistBrowse.value + ?.id + ?.replaceFirst("VL", ""), playlistName = "${viewModel.playlistBrowse.value?.title}", playlistType = PlaylistType.RADIO, - continuation = viewModel.radioContinuation.value?.let { - if (it.first == viewModel.playlistBrowse.value?.id) it.second else null - }, - - ) + continuation = + viewModel.radioContinuation.value?.let { + if (it.first == viewModel.playlistBrowse.value?.id) it.second else null + }, + ), ) viewModel.listTrack.value.getOrNull(position)?.let { - Log.w(TAG, "track: $it") + Log.w(tag, "track: $it") sharedViewModel.loadMediaItemFromTrack( it.toTrack(), type = Config.PLAYLIST_CLICK, - index = position + index = position, ) } } else { @@ -806,7 +836,7 @@ class PlaylistFragment : Fragment() { viewModel.gradientDrawable.value ?.colors!! .first(), - requireActivity() + requireActivity(), ) } } @@ -816,7 +846,7 @@ class PlaylistFragment : Fragment() { binding.topAppBarLayout.background = viewModel.gradientDrawable.value setStatusBarsColor( ContextCompat.getColor(requireContext(), R.color.colorPrimaryDark), - requireActivity() + requireActivity(), ) } } @@ -825,8 +855,7 @@ class PlaylistFragment : Fragment() { val index = Random.nextInt( 0, - viewModel.playlistBrowse.value - !! + viewModel.playlistBrowse.value!! .tracks.size - 1, ) val shuffleList: ArrayList = arrayListOf() @@ -834,26 +863,33 @@ class PlaylistFragment : Fragment() { shuffleList.addAll(it) } shuffleList.shuffle() - val afterShuffleIndex = shuffleList.indexOf(viewModel.playlistBrowse.value?.tracks?.get(index)) + val afterShuffleIndex = + shuffleList.indexOf( + viewModel.playlistBrowse.value + ?.tracks + ?.get(index), + ) sharedViewModel.simpleMediaServiceHandler?.setQueueData( QueueData( listTracks = shuffleList, - firstPlayedTrack = viewModel.playlistBrowse.value - ?.tracks - ?.get(index), - playlistId = viewModel.playlistBrowse.value - ?.id - ?.replaceFirst("VL", "") ?: "", + firstPlayedTrack = + viewModel.playlistBrowse.value + ?.tracks + ?.get(index), + playlistId = + viewModel.playlistBrowse.value + ?.id + ?.replaceFirst("VL", "") ?: "", playlistName = "Playlist \"${viewModel.playlistBrowse.value?.title}\"", playlistType = PlaylistType.PLAYLIST, continuation = null, - ) + ), ) viewModel.playlistBrowse.value?.tracks?.get(index)?.let { sharedViewModel.loadMediaItemFromTrack( it, type = Config.PLAYLIST_CLICK, - index = afterShuffleIndex + index = afterShuffleIndex, ) } } else if (viewModel.playlistEntity.value != null && viewModel.playlistEntity.value?.downloadState == DownloadState.STATE_DOWNLOADED) { @@ -873,20 +909,27 @@ class PlaylistFragment : Fragment() { .getOrNull(index) ?.let { shuffleList.remove(it.toTrack()) } shuffleList.shuffle() - val afterShuffleIndex = shuffleList.indexOf(viewModel.listTrack.value.getOrNull(index)?.toTrack()) + val afterShuffleIndex = + shuffleList.indexOf( + viewModel.listTrack.value + .getOrNull(index) + ?.toTrack(), + ) sharedViewModel.simpleMediaServiceHandler?.setQueueData( QueueData( listTracks = shuffleList, - firstPlayedTrack = viewModel.listTrack.value - .getOrNull(index) - ?.toTrack(), - playlistId = viewModel.playlistEntity.value - ?.id - ?.replaceFirst("VL", "") ?: "", + firstPlayedTrack = + viewModel.listTrack.value + .getOrNull(index) + ?.toTrack(), + playlistId = + viewModel.playlistEntity.value + ?.id + ?.replaceFirst("VL", "") ?: "", playlistName = "Playlist \"${viewModel.playlistEntity.value?.title}\"", playlistType = PlaylistType.PLAYLIST, continuation = null, - ) + ), ) viewModel.listTrack.value .getOrNull(index) @@ -894,7 +937,7 @@ class PlaylistFragment : Fragment() { sharedViewModel.loadMediaItemFromTrack( it.toTrack(), type = Config.PLAYLIST_CLICK, - index = afterShuffleIndex + index = afterShuffleIndex, ) } } else { @@ -910,7 +953,11 @@ class PlaylistFragment : Fragment() { if (viewModel.playlistDownloadState.value == DownloadState.STATE_NOT_DOWNLOADED) { // if (!viewModel.prevPlaylistDownloading.value){ // viewModel.downloading() - if (viewModel.playlistBrowse.value?.tracks?.size != viewModel.listTrack.value.size && viewModel.listTrack.value.isNotEmpty()) { + if (viewModel.playlistBrowse.value + ?.tracks + ?.size != viewModel.listTrack.value.size && + viewModel.listTrack.value.isNotEmpty() + ) { for (i in viewModel.playlistBrowse.value?.tracks!!) { viewModel.insertSong(i.toSongEntity()) } @@ -918,7 +965,11 @@ class PlaylistFragment : Fragment() { delay(1000) viewModel.listJob.emit(arrayListOf()) } - viewModel.getListTrack(viewModel.playlistBrowse.value?.tracks?.toListVideoId()) + viewModel.getListTrack( + viewModel.playlistBrowse.value + ?.tracks + ?.toListVideoId(), + ) } viewModel.updatePlaylistDownloadState( id!!, @@ -998,8 +1049,7 @@ class PlaylistFragment : Fragment() { binding.btDownload.visibility = View.GONE binding.animationDownloading.visibility = View.GONE } - } - else { + } else { binding.btDownload.visibility = View.GONE binding.animationDownloading.visibility = View.GONE } @@ -1052,7 +1102,7 @@ class PlaylistFragment : Fragment() { // if (viewModel.gradientDrawable.value == null) { // viewModel.gradientDrawable.observe(viewLifecycleOwner) { gradient -> // // fullRootLayout.background = gradient -//// toolbarBackground = gradient?.colors?.get(0) +// // toolbarBackground = gradient?.colors?.get(0) // if (gradient != null) { // val start = // topAppBarLayout.background ?: ColorDrawable( @@ -1066,8 +1116,8 @@ class PlaylistFragment : Fragment() { // } // } // } else { -//// fullRootLayout.background = gradientDrawable -//// topAppBarLayout.background = ColorDrawable(toolbarBackground!!) +// // fullRootLayout.background = gradientDrawable +// // topAppBarLayout.background = ColorDrawable(toolbarBackground!!) // topAppBarLayout.background = gradientDrawable // } // binding.rootLayout.visibility = View.VISIBLE @@ -1121,43 +1171,45 @@ class PlaylistFragment : Fragment() { private fun collectUIState() { lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { - val uiStateJob = launch { - viewModel.uiState.collectLatest { state -> - when (state) { - is PlaylistUIState.Loading -> { - binding.rootLayout.visibility = View.GONE - binding.loadingLayout.visibility = View.VISIBLE - } + val uiStateJob = + launch { + viewModel.uiState.collectLatest { state -> + when (state) { + is PlaylistUIState.Loading -> { + binding.rootLayout.visibility = View.GONE + binding.loadingLayout.visibility = View.VISIBLE + } - is PlaylistUIState.Error -> { - Snackbar.make(binding.root, state.message ?: getString(R.string.error), Snackbar.LENGTH_LONG).show() - } + is PlaylistUIState.Error -> { + Snackbar.make(binding.root, state.message ?: getString(R.string.error), Snackbar.LENGTH_LONG).show() + } - is PlaylistUIState.Success -> { - binding.rootLayout.visibility = View.VISIBLE - binding.loadingLayout.visibility = View.GONE + is PlaylistUIState.Success -> { + binding.rootLayout.visibility = View.VISIBLE + binding.loadingLayout.visibility = View.GONE + } } } } - } - val bgJob = launch { - viewModel.gradientDrawable.collectLatest { gd -> - if (gd != null) { - with(binding) { - val start = - topAppBarLayout.background ?: ColorDrawable( - Color.TRANSPARENT, - ) - val transition = - TransitionDrawable(arrayOf(start, gd)) - transition.setDither(true) - topAppBarLayout.background = transition - transition.isCrossFadeEnabled = true - transition.startTransition(500) + val bgJob = + launch { + viewModel.gradientDrawable.collectLatest { gd -> + if (gd != null) { + with(binding) { + val start = + topAppBarLayout.background ?: ColorDrawable( + Color.TRANSPARENT, + ) + val transition = + TransitionDrawable(arrayOf(start, gd)) + transition.setDither(true) + topAppBarLayout.background = transition + transition.isCrossFadeEnabled = true + transition.startTransition(500) + } } } } - } uiStateJob.join() bgJob.join() } @@ -1168,211 +1220,219 @@ class PlaylistFragment : Fragment() { private fun collectListTrack() { lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { - val job1 = launch { - combine( - viewModel.listTrack, viewModel.playlistDownloadState - ) { listTrack, downloadState -> - Pair(listTrack, downloadState) - }.collectLatest { pair -> - val listTrack = pair.first - val downloadState = pair.second - val tempList = arrayListOf() - for (i in listTrack) { - tempList.add(i) - } - listTrack.let { - viewModel.checkAllSongDownloaded(it.toArrayListTrack()) - } - if (listTrack.isNotEmpty() && downloadState == DownloadState.STATE_PREPARING) { - val listJob: ArrayList = arrayListOf() - for (song in listTrack) { - if (song.downloadState == DownloadState.STATE_NOT_DOWNLOADED) { - listJob.add(song) - } - } - viewModel.listJob.value = listJob - listJob.forEach { job -> - val downloadRequest = - DownloadRequest - .Builder(job.videoId, job.videoId.toUri()) - .setData(job.title.toByteArray()) - .setCustomCacheKey(job.videoId) - .build() - viewModel.updateDownloadState( - job.videoId, - DownloadState.STATE_DOWNLOADING, - ) - DownloadService.sendAddDownload( - requireContext(), - MusicDownloadService::class.java, - downloadRequest, - false, - ) + val job1 = + launch { + combine( + viewModel.listTrack, + viewModel.playlistDownloadState, + ) { listTrack, downloadState -> + Pair(listTrack, downloadState) + }.collectLatest { pair -> + val listTrack = pair.first + val downloadState = pair.second + val tempList = arrayListOf() + for (i in listTrack) { + tempList.add(i) } - Log.d("PlaylistFragment", "ListJob: ${viewModel.listJob.value}") - viewModel.updatePlaylistDownloadState( - viewModel.id.value!!, - DownloadState.STATE_DOWNLOADING, - ) - } - } - } - val job2 = launch { - combine(viewModel.downloadedList, viewModel.listTrack) { downloadedList, listTrack -> - Pair(downloadedList, listTrack) - }.collectLatest { pair -> - val list = pair.second - val downloadList = pair.first - val temp = list.map { it.videoId }.toMutableSet().apply { - removeAll(downloadList.toSet()) - } - Log.w(TAG, "DownloadList: $downloadList") - Log.w(TAG, "Downloading and not download: $temp") - Log.w(TAG, "DownloadList size: ${downloadList.size}") - Log.w(TAG, "Downloading and not download size: ${temp.size}") - Log.w(TAG, "List size: ${list.size}") - playlistItemAdapter.setDownloadedList(downloadList) - if (list.isNotEmpty()) { - if (downloadList.containsAll(list.map { - it.videoId - }) && downloadList.isNotEmpty()) { - viewModel.updatePlaylistDownloadState( - viewModel.id.value!!, - DownloadState.STATE_DOWNLOADED, - ) - Log.w(TAG, "All downloaded") + listTrack.let { + viewModel.checkAllSongDownloaded(it.toArrayListTrack()) } - else if (viewModel.downloadUtils.downloadingVideoIds.value.containsAll(temp) && temp.isNotEmpty()) { + if (listTrack.isNotEmpty() && downloadState == DownloadState.STATE_PREPARING) { + val listJob: ArrayList = arrayListOf() + for (song in listTrack) { + if (song.downloadState == DownloadState.STATE_NOT_DOWNLOADED) { + listJob.add(song) + } + } + viewModel.listJob.value = listJob + listJob.forEach { job -> + val downloadRequest = + DownloadRequest + .Builder(job.videoId, job.videoId.toUri()) + .setData(job.title.toByteArray()) + .setCustomCacheKey(job.videoId) + .build() + viewModel.updateDownloadState( + job.videoId, + DownloadState.STATE_DOWNLOADING, + ) + DownloadService.sendAddDownload( + requireContext(), + MusicDownloadService::class.java, + downloadRequest, + false, + ) + } + Log.d("PlaylistFragment", "ListJob: ${viewModel.listJob.value}") viewModel.updatePlaylistDownloadState( viewModel.id.value!!, DownloadState.STATE_DOWNLOADING, ) - Log.w(TAG, "Downloading") } - else { + } + } + val job2 = + launch { + combine(viewModel.downloadedList, viewModel.listTrack) { downloadedList, listTrack -> + Pair(downloadedList, listTrack) + }.collectLatest { pair -> + val list = pair.second + val downloadList = pair.first + val temp = + list.map { it.videoId }.toMutableSet().apply { + removeAll(downloadList.toSet()) + } + Log.w(tag, "DownloadList: $downloadList") + Log.w(tag, "Downloading and not download: $temp") + Log.w(tag, "DownloadList size: ${downloadList.size}") + Log.w(tag, "Downloading and not download size: ${temp.size}") + Log.w(tag, "List size: ${list.size}") + playlistItemAdapter.setDownloadedList(downloadList) + if (list.isNotEmpty()) { + if (downloadList.containsAll( + list.map { + it.videoId + }, + ) && + downloadList.isNotEmpty() + ) { + viewModel.updatePlaylistDownloadState( + viewModel.id.value!!, + DownloadState.STATE_DOWNLOADED, + ) + Log.w(tag, "All downloaded") + } else if (viewModel.downloadUtils.downloadingVideoIds.value + .containsAll(temp) && + temp.isNotEmpty() + ) { + viewModel.updatePlaylistDownloadState( + viewModel.id.value!!, + DownloadState.STATE_DOWNLOADING, + ) + Log.w(tag, "Downloading") + } else { + viewModel.updatePlaylistDownloadState( + viewModel.id.value!!, + DownloadState.STATE_NOT_DOWNLOADED, + ) + Log.w(tag, "Not downloaded") + } + } else { viewModel.updatePlaylistDownloadState( viewModel.id.value!!, DownloadState.STATE_NOT_DOWNLOADED, ) - Log.w(TAG, "Not downloaded") + Log.w(tag, "Not downloaded") } } - else { - viewModel.updatePlaylistDownloadState( - viewModel.id.value!!, - DownloadState.STATE_NOT_DOWNLOADED, - ) - Log.w(TAG, "Not downloaded") - } } - } job1.join() job2.join() } } } - private fun collectPlaylist() { lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { - val job1 = launch { - combine(viewModel.playlistBrowse, viewModel.playlistEntity) { playlistBrowse, playlistEntity -> - Pair(playlistBrowse, playlistEntity) - }.collectLatest { pair -> - val playlistBrowse = pair.first - val playlistEntity = pair.second - if (playlistBrowse != null && playlistEntity != null) { - viewModel.checkAllSongDownloaded(playlistBrowse.tracks as ArrayList) - with(binding) { - if (playlistBrowse.id.startsWith("RDEM") || playlistBrowse.id.startsWith("RDAMVM")) { - btDownload.visibility = View.GONE - cbLove.visibility = View.GONE + val job1 = + launch { + combine(viewModel.playlistBrowse, viewModel.playlistEntity) { playlistBrowse, playlistEntity -> + Pair(playlistBrowse, playlistEntity) + }.collectLatest { pair -> + val playlistBrowse = pair.first + val playlistEntity = pair.second + if (playlistBrowse != null && playlistEntity != null) { + viewModel.checkAllSongDownloaded(playlistBrowse.tracks as ArrayList) + with(binding) { + if (playlistBrowse.id.startsWith("RDEM") || playlistBrowse.id.startsWith("RDAMVM")) { + btDownload.visibility = View.GONE + cbLove.visibility = View.GONE + } + collapsingToolbarLayout.title = playlistBrowse.title + tvTitle.text = playlistBrowse.title + tvTitle.isSelected = true + tvPlaylistAuthor.text = playlistBrowse.author.name + if (playlistBrowse.year != "") { + tvYearAndCategory.text = + requireContext().getString( + R.string.year_and_category, + playlistBrowse.year, + "Playlist", + ) + } else { + tvYearAndCategory.text = + requireContext().getString(R.string.playlist) + } + tvTrackCountAndDuration.text = + requireContext().getString( + R.string.album_length, + playlistBrowse.trackCount.toString(), + "", + ) + if (playlistBrowse.description != null && playlistBrowse.description != "") { + tvDescription.originalText = playlistBrowse.description + } else { + tvDescription.originalText = getString(R.string.no_description) + } + loadImage(playlistBrowse.thumbnails.lastOrNull()?.url) + val list: ArrayList = arrayListOf() + list.addAll(playlistBrowse.tracks) + playlistItemAdapter.updateList(list) } - collapsingToolbarLayout.title = playlistBrowse.title - tvTitle.text = playlistBrowse.title - tvTitle.isSelected = true - tvPlaylistAuthor.text = playlistBrowse.author.name - if (playlistBrowse.year != "") { + } else if (playlistBrowse == null && playlistEntity != null) { + with(binding) { + collapsingToolbarLayout.title = playlistEntity.title + tvTitle.text = playlistEntity.title + tvTitle.isSelected = true + tvPlaylistAuthor.text = playlistEntity.author tvYearAndCategory.text = requireContext().getString( R.string.year_and_category, - playlistBrowse.year, + playlistEntity.year.toString(), "Playlist", ) - } else { - tvYearAndCategory.text = - requireContext().getString(R.string.playlist) - } - tvTrackCountAndDuration.text = - requireContext().getString( - R.string.album_length, - playlistBrowse.trackCount.toString(), - "", - ) - if (playlistBrowse.description != null && playlistBrowse.description != "") { - tvDescription.originalText = playlistBrowse.description - } else { - tvDescription.originalText = getString(R.string.no_description) - } - loadImage(playlistBrowse.thumbnails.lastOrNull()?.url) - val list: ArrayList = arrayListOf() - list.addAll(playlistBrowse.tracks) - playlistItemAdapter.updateList(list) - } - } - else if (playlistBrowse == null && playlistEntity != null) { - with (binding) { - collapsingToolbarLayout.title = playlistEntity.title - tvTitle.text = playlistEntity.title - tvTitle.isSelected = true - tvPlaylistAuthor.text = playlistEntity.author - tvYearAndCategory.text = - requireContext().getString( - R.string.year_and_category, - playlistEntity.year.toString(), - "Playlist", - ) - tvTrackCountAndDuration.text = - requireContext().getString( - R.string.album_length, - playlistEntity.trackCount.toString(), - "", - ) - if (playlistEntity.description != "") { - tvDescription.originalText = playlistEntity.description - } else { - tvDescription.originalText = getString(R.string.no_description) + tvTrackCountAndDuration.text = + requireContext().getString( + R.string.album_length, + playlistEntity.trackCount.toString(), + "", + ) + if (playlistEntity.description != "") { + tvDescription.originalText = playlistEntity.description + } else { + tvDescription.originalText = getString(R.string.no_description) + } + loadImage(playlistEntity.thumbnails) } - loadImage(playlistEntity.thumbnails) } - } } - } - val job2 = launch { - combine(viewModel.playlistBrowse, viewModel.listTrack) { - playlistBrowse, listTrack -> Pair(playlistBrowse, listTrack) - }.collectLatest { pair -> - val playlistBrowse = pair.first - val listTrack = pair.second - if (playlistBrowse == null && listTrack.isNotEmpty()) { - val tempList = arrayListOf() - for (i in listTrack) { - tempList.add(i) - } - listTrack.let { - viewModel.checkAllSongDownloaded(it.toArrayListTrack()) + val job2 = + launch { + combine(viewModel.playlistBrowse, viewModel.listTrack) { playlistBrowse, listTrack -> + Pair(playlistBrowse, listTrack) + }.collectLatest { pair -> + val playlistBrowse = pair.first + val listTrack = pair.second + if (playlistBrowse == null && listTrack.isNotEmpty()) { + val tempList = arrayListOf() + for (i in listTrack) { + tempList.add(i) + } + listTrack.let { + viewModel.checkAllSongDownloaded(it.toArrayListTrack()) + } + playlistItemAdapter.updateList(tempList) } - playlistItemAdapter.updateList(tempList) } } - } job1.join() job2.join() } } } + private fun fetchRDATRadio(radioId: String) { viewModel.clearPlaylistBrowse() viewModel.clearPlaylistEntity() @@ -1429,8 +1489,8 @@ class PlaylistFragment : Fragment() { // playlistItemAdapter.updateList(list) // if (viewModel.gradientDrawable.value == null) { // viewModel.gradientDrawable.observe(viewLifecycleOwner) { gradient -> -//// fullRootLayout.background = gradient -//// toolbarBackground = gradient?.colors?.get(0) +// // fullRootLayout.background = gradient +// // toolbarBackground = gradient?.colors?.get(0) // if (gradient != null) { // val start = // topAppBarLayout.background ?: ColorDrawable( @@ -1445,8 +1505,8 @@ class PlaylistFragment : Fragment() { // } // } // } else { -//// fullRootLayout.background = gradientDrawable -//// topAppBarLayout.background = ColorDrawable(toolbarBackground!!) +// // fullRootLayout.background = gradientDrawable +// // topAppBarLayout.background = ColorDrawable(toolbarBackground!!) // topAppBarLayout.background = // gradientDrawable?.apply { // setDither(true) @@ -1521,9 +1581,9 @@ class PlaylistFragment : Fragment() { // playlistItemAdapter.updateList(list) // if (viewModel.gradientDrawable.value == null) { // viewModel.gradientDrawable.observe(viewLifecycleOwner) { gradient -> -//// fullRootLayout.background = gradient -//// toolbarBackground = gradient?.colors?.get(0) -//// topAppBarLayout.background = ColorDrawable(toolbarBackground!!) +// // fullRootLayout.background = gradient +// // toolbarBackground = gradient?.colors?.get(0) +// // topAppBarLayout.background = ColorDrawable(toolbarBackground!!) // if (gradient != null) { // val start = // topAppBarLayout.background ?: ColorDrawable( @@ -1538,8 +1598,8 @@ class PlaylistFragment : Fragment() { // } // } // } else { -//// fullRootLayout.background = gradientDrawable -//// topAppBarLayout.background = ColorDrawable(toolbarBackground!!) +// // fullRootLayout.background = gradientDrawable +// // topAppBarLayout.background = ColorDrawable(toolbarBackground!!) // topAppBarLayout.background = // gradientDrawable?.apply { // setDither(true) @@ -1635,9 +1695,9 @@ class PlaylistFragment : Fragment() { // loadImage(playlistEntity.thumbnails) // if (viewModel.gradientDrawable.value == null) { // viewModel.gradientDrawable.observe(viewLifecycleOwner) { gradient -> -//// fullRootLayout.background = gradient -//// toolbarBackground = gradient?.colors?.get(0) -//// topAppBarLayout.background = ColorDrawable(toolbarBackground!!) +// // fullRootLayout.background = gradient +// // toolbarBackground = gradient?.colors?.get(0) +// // topAppBarLayout.background = ColorDrawable(toolbarBackground!!) // if (gradient != null) { // val start = // topAppBarLayout.background @@ -1650,8 +1710,8 @@ class PlaylistFragment : Fragment() { // } // } // } else { -//// fullRootLayout.background = gradientDrawable -//// topAppBarLayout.background = ColorDrawable(toolbarBackground!!) +// // fullRootLayout.background = gradientDrawable +// // topAppBarLayout.background = ColorDrawable(toolbarBackground!!) // topAppBarLayout.background = // gradientDrawable?.apply { // setDither(true) @@ -1683,7 +1743,7 @@ class PlaylistFragment : Fragment() { crossfade(300) listener( onError = { _, er -> - Log.w(TAG, "Load Image Error ${er.throwable.message}") + Log.w(tag, "Load Image Error ${er.throwable.message}") }, onSuccess = { _, result -> binding.ivPlaylistArt.setImageDrawable(result.drawable) @@ -1717,7 +1777,7 @@ class PlaylistFragment : Fragment() { gd.gradientType = GradientDrawable.LINEAR_GRADIENT gd.gradientRadius = 0.5f viewModel.setGradientDrawable(gd) - } + }, ) } } diff --git a/app/src/main/java/com/maxrave/simpmusic/ui/fragment/other/PodcastFragment.kt b/app/src/main/java/com/maxrave/simpmusic/ui/fragment/other/PodcastFragment.kt index 63ad5bc7..124837dc 100644 --- a/app/src/main/java/com/maxrave/simpmusic/ui/fragment/other/PodcastFragment.kt +++ b/app/src/main/java/com/maxrave/simpmusic/ui/fragment/other/PodcastFragment.kt @@ -39,11 +39,9 @@ import com.maxrave.simpmusic.service.QueueData import com.maxrave.simpmusic.utils.Resource import com.maxrave.simpmusic.viewModel.PodcastViewModel import com.maxrave.simpmusic.viewModel.SharedViewModel -import dagger.hilt.android.AndroidEntryPoint import kotlin.math.abs import kotlin.random.Random -@AndroidEntryPoint @UnstableApi class PodcastFragment : Fragment() { diff --git a/app/src/main/java/com/maxrave/simpmusic/ui/fragment/player/FullscreenFragment.kt b/app/src/main/java/com/maxrave/simpmusic/ui/fragment/player/FullscreenFragment.kt index 0c6df5b6..620d87f6 100644 --- a/app/src/main/java/com/maxrave/simpmusic/ui/fragment/player/FullscreenFragment.kt +++ b/app/src/main/java/com/maxrave/simpmusic/ui/fragment/player/FullscreenFragment.kt @@ -52,7 +52,6 @@ import com.maxrave.simpmusic.service.RepeatState import com.maxrave.simpmusic.service.test.download.MusicDownloadService import com.maxrave.simpmusic.viewModel.SharedViewModel import com.maxrave.simpmusic.viewModel.UIEvent -import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.flow.collectLatest @@ -62,7 +61,7 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import java.time.LocalDateTime -@AndroidEntryPoint +@UnstableApi class FullscreenFragment : Fragment() { private val viewModel: SharedViewModel by activityViewModels() private val binding by lazy { BottomSheetFullscreenBinding.inflate(layoutInflater) } diff --git a/app/src/main/java/com/maxrave/simpmusic/ui/fragment/player/InfoFragment.kt b/app/src/main/java/com/maxrave/simpmusic/ui/fragment/player/InfoFragment.kt index cc38306a..07ace48b 100644 --- a/app/src/main/java/com/maxrave/simpmusic/ui/fragment/player/InfoFragment.kt +++ b/app/src/main/java/com/maxrave/simpmusic/ui/fragment/player/InfoFragment.kt @@ -21,14 +21,12 @@ import com.maxrave.simpmusic.extension.connectArtists import com.maxrave.simpmusic.extension.navigateSafe import com.maxrave.simpmusic.extension.toListName import com.maxrave.simpmusic.viewModel.SharedViewModel -import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch -@AndroidEntryPoint class InfoFragment: BottomSheetDialogFragment(){ private var _binding: InfoFragmentBinding? = null - private val binding get() = _binding!! + val binding get() = _binding!! private val viewModel by activityViewModels() override fun onCreate(savedInstanceState: Bundle?) { diff --git a/app/src/main/java/com/maxrave/simpmusic/ui/fragment/player/NowPlayingFragment.kt b/app/src/main/java/com/maxrave/simpmusic/ui/fragment/player/NowPlayingFragment.kt index ddffa8e1..54935072 100644 --- a/app/src/main/java/com/maxrave/simpmusic/ui/fragment/player/NowPlayingFragment.kt +++ b/app/src/main/java/com/maxrave/simpmusic/ui/fragment/player/NowPlayingFragment.kt @@ -19,10 +19,8 @@ import androidx.navigation.findNavController import com.maxrave.simpmusic.ui.screen.player.NowPlayingScreen import com.maxrave.simpmusic.ui.theme.AppTheme import com.maxrave.simpmusic.viewModel.SharedViewModel -import dagger.hilt.android.AndroidEntryPoint @UnstableApi -@AndroidEntryPoint class NowPlayingFragment : Fragment() { val viewModel by activityViewModels() private lateinit var composeView: ComposeView @@ -34,15 +32,15 @@ class NowPlayingFragment : Fragment() { // // private var isFullScreen = false // -//// private lateinit var songChangeListener: OnNowPlayingSongChangeListener -//// override fun onAttach(context: Context) { -//// super.onAttach(context) -//// if (context is OnNowPlayingSongChangeListener) { -//// songChangeListener = context -//// } else { -//// throw RuntimeException("$context must implement OnNowPlayingSongChangeListener") -//// } -//// } +// // private lateinit var songChangeListener: OnNowPlayingSongChangeListener +// // override fun onAttach(context: Context) { +// // super.onAttach(context) +// // if (context is OnNowPlayingSongChangeListener) { +// // songChangeListener = context +// // } else { +// // throw RuntimeException("$context must implement OnNowPlayingSongChangeListener") +// // } +// // } // // override fun onResume() { // super.onResume() @@ -149,7 +147,10 @@ class NowPlayingFragment : Fragment() { } @OptIn(ExperimentalMaterial3Api::class) - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + override fun onViewCreated( + view: View, + savedInstanceState: Bundle?, + ) { super.onViewCreated(view, savedInstanceState) // val activity = requireActivity() // val bottom = activity.findViewById(R.id.bottom_navigation_view) @@ -226,9 +227,9 @@ class NowPlayingFragment : Fragment() { // layoutParams = layoutParamsCopy // Log.w("Check Height", layoutParams.height.toString()) // } -//// val location = IntArray(2) -//// binding.belowControllerButtonLayout.getLocationInWindow(location) -//// val y = binding.belowControllerButtonLayout.top +// // val location = IntArray(2) +// // binding.belowControllerButtonLayout.getLocationInWindow(location) +// // val y = binding.belowControllerButtonLayout.top // // // val activity = requireActivity() @@ -290,312 +291,312 @@ class NowPlayingFragment : Fragment() { // } // binding.playerView.player = viewModel.simpleMediaServiceHandler?.player // -//// when (type) { -//// SONG_CLICK -> { -//// viewModel.playlistId.value = null -//// if (viewModel.videoId.value == videoId) { -//// gradientDrawable = viewModel.gradientDrawable.value -//// lyricsBackground = viewModel.lyricsBackground.value -//// metadataCurSong = viewModel.metadata.value?.data -//// updateUIfromCurrentMediaItem(viewModel.getCurrentMediaItem()) -//// } else { -//// Log.i("Now Playing Fragment", "Song Click") -//// binding.ivArt.setImageResource(0) -//// binding.loadingArt.visibility = View.VISIBLE -//// viewModel.gradientDrawable.postValue(null) -//// viewModel.lyricsBackground.postValue(null) -//// binding.tvSongTitle.visibility = View.GONE -//// binding.tvSongArtist.visibility = View.GONE -//// Queue.getNowPlaying()?.let { -////// lifecycleScope.launch { -////// repeatOnLifecycle(Lifecycle.State.CREATED) { -////// viewModel.firstTrackAdded.collect { added -> -////// if (added && type == Config.SONG_CLICK) { -////// viewModel.changeFirstTrackAddedToFalse() -////// // viewModel.getFormat(it.videoId) -////// getRelated(it.videoId) -////// } -////// } -////// } -////// } -//// viewModel.simpleMediaServiceHandler?.reset() -////// if (requireContext().isMyServiceRunning(FetchQueue::class.java)) { -////// requireActivity().stopService( -////// Intent( -////// requireContext(), -////// FetchQueue::class.java -////// ) -////// ) -////// } -//// -////// viewModel.loadMediaItemFromTrack(it, SONG_CLICK) -//// viewModel.videoId.postValue(it.videoId) -//// viewModel.from.postValue(from) -//// updateUIfromQueueNowPlaying() -//// } -//// } -//// } -//// -//// SHARE -> { -//// viewModel.playlistId.value = null -//// viewModel.stopPlayer() -//// binding.ivArt.setImageResource(0) -//// binding.loadingArt.visibility = View.VISIBLE -//// viewModel.gradientDrawable.postValue(null) -//// viewModel.lyricsBackground.postValue(null) -//// binding.tvSongTitle.visibility = View.GONE -//// binding.tvSongArtist.visibility = View.GONE -//// if (videoId != null) { -//// Log.d("Check Video ID", videoId!!) -//// Log.d("Check videoId in ViewModel", viewModel.videoId.value.toString()) -//// viewModel.getSongFull(videoId!!) -//// viewModel.songFull.observe(viewLifecycleOwner) { -//// Log.w("Check Song Full", it?.videoDetails?.title.toString()) -//// if (it != null && it.videoDetails?.videoId == videoId && it.videoDetails?.videoId != null) { -//// val track = it.toTrack() -////// Queue.clear() -//// Queue.setNowPlaying(track) -//// viewModel.simpleMediaServiceHandler?.reset() -////// if (requireContext().isMyServiceRunning(FetchQueue::class.java)) { -////// requireActivity().stopService( -////// Intent( -////// requireContext(), -////// FetchQueue::class.java -////// ) -////// ) -////// } -////// viewModel.loadMediaItemFromTrack(track, SHARE) -//// viewModel.videoId.postValue(track.videoId) -//// viewModel.from.postValue(from) -//// updateUIfromQueueNowPlaying() -//// miniplayer.visibility = View.GONE -//// bottom.visibility = View.GONE -////// lifecycleScope.launch { -////// repeatOnLifecycle(Lifecycle.State.CREATED) { -////// viewModel.firstTrackAdded.collect { added -> -////// if (added && type == Config.SHARE) { -////// viewModel.changeFirstTrackAddedToFalse() -////// // viewModel.getFormat(track.videoId) -////// getRelated(track.videoId) -////// } -////// } -////// } -////// } -//// } -//// } -//// } -//// } -//// -//// VIDEO_CLICK -> { -//// viewModel.playlistId.value = null -//// if (viewModel.videoId.value == videoId) { -//// gradientDrawable = viewModel.gradientDrawable.value -//// lyricsBackground = viewModel.lyricsBackground.value -//// metadataCurSong = viewModel.metadata.value?.data -//// updateUIfromCurrentMediaItem(viewModel.getCurrentMediaItem()) -//// } else { -////// if (!viewModel.songTransitions.value){ -//// Log.i("Now Playing Fragment", "Video Click") -//// binding.ivArt.setImageResource(0) -//// binding.loadingArt.visibility = View.VISIBLE -//// viewModel.gradientDrawable.postValue(null) -//// viewModel.lyricsBackground.postValue(null) -//// binding.tvSongTitle.visibility = View.GONE -//// binding.tvSongArtist.visibility = View.GONE -//// Queue.getNowPlaying()?.let { -//// viewModel.simpleMediaServiceHandler?.reset() -////// viewModel.resetRelated() -////// if (requireContext().isMyServiceRunning(FetchQueue::class.java)) { -////// requireActivity().stopService( -////// Intent( -////// requireContext(), -////// FetchQueue::class.java -////// ) -////// ) -////// } -////// viewModel.loadMediaItemFromTrack(it, VIDEO_CLICK) -//// viewModel.videoId.postValue(it.videoId) -//// viewModel.from.postValue(from) -//// updateUIfromQueueNowPlaying() -////// lifecycleScope.launch { -////// repeatOnLifecycle(Lifecycle.State.CREATED) { -////// viewModel.firstTrackAdded.collect { added -> -////// if (added && type == Config.VIDEO_CLICK) { -////// viewModel.changeFirstTrackAddedToFalse() -////// // viewModel.getFormat(it.videoId) -////// getRelated(it.videoId) -////// } -////// } -////// } -////// } -//// } -//// // } -////// viewModel.loadMediaItems(videoId!!) -//// } -//// } -//// -//// ALBUM_CLICK -> { -//// if (playlistId != null) { -//// viewModel.playlistId.value = playlistId -//// } -////// if (!viewModel.songTransitions.value){ -//// Log.i("Now Playing Fragment", "Album Click") -//// binding.ivArt.setImageResource(0) -//// binding.loadingArt.visibility = View.VISIBLE -//// viewModel.gradientDrawable.postValue(null) -//// viewModel.lyricsBackground.postValue(null) -//// binding.tvSongTitle.visibility = View.GONE -//// binding.tvSongArtist.visibility = View.GONE -//// Queue.getNowPlaying()?.let { -//// viewModel.simpleMediaServiceHandler?.reset() -////// if (requireContext().isMyServiceRunning(FetchQueue::class.java)) { -////// requireActivity().stopService( -////// Intent( -////// requireContext(), -////// FetchQueue::class.java -////// ) -////// ) -////// } -////// viewModel.loadMediaItemFromTrack(it, ALBUM_CLICK, index) -//// viewModel.videoId.postValue(it.videoId) -//// viewModel.from.postValue(from) -////// viewModel.resetLyrics() -////// viewModel.getLyrics(it.title + " " + it.artists?.first()?.name) -//// updateUIfromQueueNowPlaying() -////// viewModel._lyrics.observe(viewLifecycleOwner){ resourceLyrics -> -////// when(resourceLyrics){ -////// is Resource.Success -> { -////// if (resourceLyrics.data != null) { -////// viewModel.insertLyrics(resourceLyrics.data.toLyricsEntity(it.videoId)) -////// viewModel.parseLyrics(resourceLyrics.data) -////// } -////// } -////// is Resource.Error -> { -////// viewModel.getSavedLyrics(it.videoId) -////// } -////// } -////// } -//// Log.d("check index", index.toString()) -////// lifecycleScope.launch { -////// repeatOnLifecycle(Lifecycle.State.CREATED) { -////// viewModel.firstTrackAdded.collect { added -> -////// if (added && type == Config.ALBUM_CLICK) { -////// viewModel.changeFirstTrackAddedToFalse() -////// // viewModel.getFormat(it.videoId) -////// if (index == null) { -////// fetchSourceFromQueue(downloaded = downloaded ?: 0) -////// } else { -////// fetchSourceFromQueue(index!!, downloaded = downloaded ?: 0) -////// } -////// } -////// } -////// } -////// } -//// } -//// } -//// -//// PLAYLIST_CLICK -> { -//// if (playlistId != null) { -//// viewModel.playlistId.value = playlistId -//// } -//// Log.i("Now Playing Fragment", "Playlist Click") -//// binding.ivArt.setImageResource(0) -//// binding.loadingArt.visibility = View.VISIBLE -//// viewModel.gradientDrawable.postValue(null) -//// viewModel.lyricsBackground.postValue(null) -//// binding.tvSongTitle.visibility = View.GONE -//// binding.tvSongArtist.visibility = View.GONE -//// Queue.getNowPlaying()?.let { -//// viewModel.simpleMediaServiceHandler?.reset() -////// viewModel.resetRelated() -////// if (requireContext().isMyServiceRunning(FetchQueue::class.java)) { -////// requireActivity().stopService( -////// Intent( -////// requireContext(), -////// FetchQueue::class.java -////// ) -////// ) -////// } -//// Log.d("check index", index.toString()) -////// viewModel.loadMediaItemFromTrack(it, PLAYLIST_CLICK, index) -//// viewModel.videoId.postValue(it.videoId) -//// viewModel.from.postValue(from) -////// viewModel.resetLyrics() -////// viewModel.getLyrics(it.title + " " + it.artists?.first()?.name) -//// updateUIfromQueueNowPlaying() -////// viewModel._lyrics.observe(viewLifecycleOwner){ resourceLyrics -> -////// when(resourceLyrics){ -////// is Resource.Success -> { -////// if (resourceLyrics.data != null) { -////// viewModel.insertLyrics(resourceLyrics.data.toLyricsEntity(it.videoId)) -////// viewModel.parseLyrics(resourceLyrics.data) -////// } -////// } -////// is Resource.Error -> { -////// viewModel.getSavedLyrics(it.videoId) -////// } -////// } -////// } -////// lifecycleScope.launch { -////// repeatOnLifecycle(Lifecycle.State.CREATED) { -////// viewModel.firstTrackAdded.collect { added -> -////// if (added && type == Config.PLAYLIST_CLICK) { -////// viewModel.changeFirstTrackAddedToFalse() -////// // viewModel.getFormat(it.videoId) -////// if (index == null) { -////// fetchSourceFromQueue(downloaded = downloaded ?: 0) -////// } else { -////// fetchSourceFromQueue(index!!, downloaded = downloaded ?: 0) -////// } -////// } -////// } -////// } -////// } -//// } -//// } -//// -//// MINIPLAYER_CLICK -> { -//// videoId = viewModel.videoId.value -//// from = viewModel.from.value -//// metadataCurSong = viewModel.metadata.value?.data -//// gradientDrawable = viewModel.gradientDrawable.value -//// lyricsBackground = viewModel.lyricsBackground.value -////// if (viewModel.progress.value in 0.0..1.0) { -////// binding.progressSong.value = viewModel.progress.value * 100 -////// } -//// if (videoId == null) { -//// videoId = runBlocking { viewModel.nowPlayingMediaItem.first()?.mediaId } -//// viewModel.videoId.postValue(videoId) -//// } -//// updateUIfromCurrentMediaItem(viewModel.getCurrentMediaItem()) -//// } -//// } +// // when (type) { +// // SONG_CLICK -> { +// // viewModel.playlistId.value = null +// // if (viewModel.videoId.value == videoId) { +// // gradientDrawable = viewModel.gradientDrawable.value +// // lyricsBackground = viewModel.lyricsBackground.value +// // metadataCurSong = viewModel.metadata.value?.data +// // updateUIfromCurrentMediaItem(viewModel.getCurrentMediaItem()) +// // } else { +// // Log.i("Now Playing Fragment", "Song Click") +// // binding.ivArt.setImageResource(0) +// // binding.loadingArt.visibility = View.VISIBLE +// // viewModel.gradientDrawable.postValue(null) +// // viewModel.lyricsBackground.postValue(null) +// // binding.tvSongTitle.visibility = View.GONE +// // binding.tvSongArtist.visibility = View.GONE +// // Queue.getNowPlaying()?.let { +// //// lifecycleScope.launch { +// //// repeatOnLifecycle(Lifecycle.State.CREATED) { +// //// viewModel.firstTrackAdded.collect { added -> +// //// if (added && type == Config.SONG_CLICK) { +// //// viewModel.changeFirstTrackAddedToFalse() +// //// // viewModel.getFormat(it.videoId) +// //// getRelated(it.videoId) +// //// } +// //// } +// //// } +// //// } +// // viewModel.simpleMediaServiceHandler?.reset() +// //// if (requireContext().isMyServiceRunning(FetchQueue::class.java)) { +// //// requireActivity().stopService( +// //// Intent( +// //// requireContext(), +// //// FetchQueue::class.java +// //// ) +// //// ) +// //// } +// // +// //// viewModel.loadMediaItemFromTrack(it, SONG_CLICK) +// // viewModel.videoId.postValue(it.videoId) +// // viewModel.from.postValue(from) +// // updateUIfromQueueNowPlaying() +// // } +// // } +// // } +// // +// // SHARE -> { +// // viewModel.playlistId.value = null +// // viewModel.stopPlayer() +// // binding.ivArt.setImageResource(0) +// // binding.loadingArt.visibility = View.VISIBLE +// // viewModel.gradientDrawable.postValue(null) +// // viewModel.lyricsBackground.postValue(null) +// // binding.tvSongTitle.visibility = View.GONE +// // binding.tvSongArtist.visibility = View.GONE +// // if (videoId != null) { +// // Log.d("Check Video ID", videoId!!) +// // Log.d("Check videoId in ViewModel", viewModel.videoId.value.toString()) +// // viewModel.getSongFull(videoId!!) +// // viewModel.songFull.observe(viewLifecycleOwner) { +// // Log.w("Check Song Full", it?.videoDetails?.title.toString()) +// // if (it != null && it.videoDetails?.videoId == videoId && it.videoDetails?.videoId != null) { +// // val track = it.toTrack() +// //// Queue.clear() +// // Queue.setNowPlaying(track) +// // viewModel.simpleMediaServiceHandler?.reset() +// //// if (requireContext().isMyServiceRunning(FetchQueue::class.java)) { +// //// requireActivity().stopService( +// //// Intent( +// //// requireContext(), +// //// FetchQueue::class.java +// //// ) +// //// ) +// //// } +// //// viewModel.loadMediaItemFromTrack(track, SHARE) +// // viewModel.videoId.postValue(track.videoId) +// // viewModel.from.postValue(from) +// // updateUIfromQueueNowPlaying() +// // miniplayer.visibility = View.GONE +// // bottom.visibility = View.GONE +// //// lifecycleScope.launch { +// //// repeatOnLifecycle(Lifecycle.State.CREATED) { +// //// viewModel.firstTrackAdded.collect { added -> +// //// if (added && type == Config.SHARE) { +// //// viewModel.changeFirstTrackAddedToFalse() +// //// // viewModel.getFormat(track.videoId) +// //// getRelated(track.videoId) +// //// } +// //// } +// //// } +// //// } +// // } +// // } +// // } +// // } +// // +// // VIDEO_CLICK -> { +// // viewModel.playlistId.value = null +// // if (viewModel.videoId.value == videoId) { +// // gradientDrawable = viewModel.gradientDrawable.value +// // lyricsBackground = viewModel.lyricsBackground.value +// // metadataCurSong = viewModel.metadata.value?.data +// // updateUIfromCurrentMediaItem(viewModel.getCurrentMediaItem()) +// // } else { +// //// if (!viewModel.songTransitions.value){ +// // Log.i("Now Playing Fragment", "Video Click") +// // binding.ivArt.setImageResource(0) +// // binding.loadingArt.visibility = View.VISIBLE +// // viewModel.gradientDrawable.postValue(null) +// // viewModel.lyricsBackground.postValue(null) +// // binding.tvSongTitle.visibility = View.GONE +// // binding.tvSongArtist.visibility = View.GONE +// // Queue.getNowPlaying()?.let { +// // viewModel.simpleMediaServiceHandler?.reset() +// //// viewModel.resetRelated() +// //// if (requireContext().isMyServiceRunning(FetchQueue::class.java)) { +// //// requireActivity().stopService( +// //// Intent( +// //// requireContext(), +// //// FetchQueue::class.java +// //// ) +// //// ) +// //// } +// //// viewModel.loadMediaItemFromTrack(it, VIDEO_CLICK) +// // viewModel.videoId.postValue(it.videoId) +// // viewModel.from.postValue(from) +// // updateUIfromQueueNowPlaying() +// //// lifecycleScope.launch { +// //// repeatOnLifecycle(Lifecycle.State.CREATED) { +// //// viewModel.firstTrackAdded.collect { added -> +// //// if (added && type == Config.VIDEO_CLICK) { +// //// viewModel.changeFirstTrackAddedToFalse() +// //// // viewModel.getFormat(it.videoId) +// //// getRelated(it.videoId) +// //// } +// //// } +// //// } +// //// } +// // } +// // // } +// //// viewModel.loadMediaItems(videoId!!) +// // } +// // } +// // +// // ALBUM_CLICK -> { +// // if (playlistId != null) { +// // viewModel.playlistId.value = playlistId +// // } +// //// if (!viewModel.songTransitions.value){ +// // Log.i("Now Playing Fragment", "Album Click") +// // binding.ivArt.setImageResource(0) +// // binding.loadingArt.visibility = View.VISIBLE +// // viewModel.gradientDrawable.postValue(null) +// // viewModel.lyricsBackground.postValue(null) +// // binding.tvSongTitle.visibility = View.GONE +// // binding.tvSongArtist.visibility = View.GONE +// // Queue.getNowPlaying()?.let { +// // viewModel.simpleMediaServiceHandler?.reset() +// //// if (requireContext().isMyServiceRunning(FetchQueue::class.java)) { +// //// requireActivity().stopService( +// //// Intent( +// //// requireContext(), +// //// FetchQueue::class.java +// //// ) +// //// ) +// //// } +// //// viewModel.loadMediaItemFromTrack(it, ALBUM_CLICK, index) +// // viewModel.videoId.postValue(it.videoId) +// // viewModel.from.postValue(from) +// //// viewModel.resetLyrics() +// //// viewModel.getLyrics(it.title + " " + it.artists?.first()?.name) +// // updateUIfromQueueNowPlaying() +// //// viewModel._lyrics.observe(viewLifecycleOwner){ resourceLyrics -> +// //// when(resourceLyrics){ +// //// is Resource.Success -> { +// //// if (resourceLyrics.data != null) { +// //// viewModel.insertLyrics(resourceLyrics.data.toLyricsEntity(it.videoId)) +// //// viewModel.parseLyrics(resourceLyrics.data) +// //// } +// //// } +// //// is Resource.Error -> { +// //// viewModel.getSavedLyrics(it.videoId) +// //// } +// //// } +// //// } +// // Log.d("check index", index.toString()) +// //// lifecycleScope.launch { +// //// repeatOnLifecycle(Lifecycle.State.CREATED) { +// //// viewModel.firstTrackAdded.collect { added -> +// //// if (added && type == Config.ALBUM_CLICK) { +// //// viewModel.changeFirstTrackAddedToFalse() +// //// // viewModel.getFormat(it.videoId) +// //// if (index == null) { +// //// fetchSourceFromQueue(downloaded = downloaded ?: 0) +// //// } else { +// //// fetchSourceFromQueue(index!!, downloaded = downloaded ?: 0) +// //// } +// //// } +// //// } +// //// } +// //// } +// // } +// // } +// // +// // PLAYLIST_CLICK -> { +// // if (playlistId != null) { +// // viewModel.playlistId.value = playlistId +// // } +// // Log.i("Now Playing Fragment", "Playlist Click") +// // binding.ivArt.setImageResource(0) +// // binding.loadingArt.visibility = View.VISIBLE +// // viewModel.gradientDrawable.postValue(null) +// // viewModel.lyricsBackground.postValue(null) +// // binding.tvSongTitle.visibility = View.GONE +// // binding.tvSongArtist.visibility = View.GONE +// // Queue.getNowPlaying()?.let { +// // viewModel.simpleMediaServiceHandler?.reset() +// //// viewModel.resetRelated() +// //// if (requireContext().isMyServiceRunning(FetchQueue::class.java)) { +// //// requireActivity().stopService( +// //// Intent( +// //// requireContext(), +// //// FetchQueue::class.java +// //// ) +// //// ) +// //// } +// // Log.d("check index", index.toString()) +// //// viewModel.loadMediaItemFromTrack(it, PLAYLIST_CLICK, index) +// // viewModel.videoId.postValue(it.videoId) +// // viewModel.from.postValue(from) +// //// viewModel.resetLyrics() +// //// viewModel.getLyrics(it.title + " " + it.artists?.first()?.name) +// // updateUIfromQueueNowPlaying() +// //// viewModel._lyrics.observe(viewLifecycleOwner){ resourceLyrics -> +// //// when(resourceLyrics){ +// //// is Resource.Success -> { +// //// if (resourceLyrics.data != null) { +// //// viewModel.insertLyrics(resourceLyrics.data.toLyricsEntity(it.videoId)) +// //// viewModel.parseLyrics(resourceLyrics.data) +// //// } +// //// } +// //// is Resource.Error -> { +// //// viewModel.getSavedLyrics(it.videoId) +// //// } +// //// } +// //// } +// //// lifecycleScope.launch { +// //// repeatOnLifecycle(Lifecycle.State.CREATED) { +// //// viewModel.firstTrackAdded.collect { added -> +// //// if (added && type == Config.PLAYLIST_CLICK) { +// //// viewModel.changeFirstTrackAddedToFalse() +// //// // viewModel.getFormat(it.videoId) +// //// if (index == null) { +// //// fetchSourceFromQueue(downloaded = downloaded ?: 0) +// //// } else { +// //// fetchSourceFromQueue(index!!, downloaded = downloaded ?: 0) +// //// } +// //// } +// //// } +// //// } +// //// } +// // } +// // } +// // +// // MINIPLAYER_CLICK -> { +// // videoId = viewModel.videoId.value +// // from = viewModel.from.value +// // metadataCurSong = viewModel.metadata.value?.data +// // gradientDrawable = viewModel.gradientDrawable.value +// // lyricsBackground = viewModel.lyricsBackground.value +// //// if (viewModel.progress.value in 0.0..1.0) { +// //// binding.progressSong.value = viewModel.progress.value * 100 +// //// } +// // if (videoId == null) { +// // videoId = runBlocking { viewModel.nowPlayingMediaItem.first()?.mediaId } +// // viewModel.videoId.postValue(videoId) +// // } +// // updateUIfromCurrentMediaItem(viewModel.getCurrentMediaItem()) +// // } +// // } // // lifecycleScope.launch { -//// val job7 = launch { -//// viewModel.songTransitions.collectLatest { isChanged -> -//// if (isChanged) { -//// val song = viewModel.getCurrentMediaItem() -//// if (song != null) { -//// Log.i("Now Playing Fragment", "Bên dưới") -//// Log.d("Song Transition", "Song Transition") -//// videoId = viewModel.videoId.value -//// binding.ivArt.setImageResource(0) -//// binding.loadingArt.visibility = View.VISIBLE -//// Log.d("Check Lyrics", viewModel._lyrics.value?.data.toString()) -//// updateUIfromCurrentMediaItem(song) -//// simpleMediaServiceHandler.setCurrentSongIndex(viewModel.getCurrentMediaItemIndex()) -//// viewModel.changeSongTransitionToFalse() -//// } -//// } -//// } -//// } +// // val job7 = launch { +// // viewModel.songTransitions.collectLatest { isChanged -> +// // if (isChanged) { +// // val song = viewModel.getCurrentMediaItem() +// // if (song != null) { +// // Log.i("Now Playing Fragment", "Bên dưới") +// // Log.d("Song Transition", "Song Transition") +// // videoId = viewModel.videoId.value +// // binding.ivArt.setImageResource(0) +// // binding.loadingArt.visibility = View.VISIBLE +// // Log.d("Check Lyrics", viewModel._lyrics.value?.data.toString()) +// // updateUIfromCurrentMediaItem(song) +// // simpleMediaServiceHandler.setCurrentSongIndex(viewModel.getCurrentMediaItemIndex()) +// // viewModel.changeSongTransitionToFalse() +// // } +// // } +// // } +// // } // repeatOnLifecycle(Lifecycle.State.CREATED) { // val job7 = // launch { // viewModel.nowPlayingMediaItem.collectLatest { song -> // if (song != null) { -//// viewModel.getFormat(song.mediaId) +// // viewModel.getFormat(song.mediaId) // Log.i("Now Playing Fragment", "song ${song.mediaMetadata.title}") // Log.w("Now Playing Fragment", "song ${song.mediaMetadata.description}") // videoId = viewModel.videoId.value @@ -658,10 +659,10 @@ class NowPlayingFragment : Fragment() { // launch { // viewModel.progressString.collect { // binding.tvCurrentTime.text = it -//// if (viewModel.progress.value * 100 in 0f..100f) { -//// binding.progressSong.value = viewModel.progress.value * 100 -//// // songChangeListener.onUpdateProgressBar(viewModel.progress.value * 100) -//// } +// // if (viewModel.progress.value * 100 in 0f..100f) { +// // binding.progressSong.value = viewModel.progress.value * 100 +// // // songChangeListener.onUpdateProgressBar(viewModel.progress.value * 100) +// // } // } // } // val job2 = @@ -672,12 +673,12 @@ class NowPlayingFragment : Fragment() { // binding.btPlayPause.setImageResource( // R.drawable.baseline_pause_circle_24, // ) -//// songChangeListener.onIsPlayingChange() +// // songChangeListener.onIsPlayingChange() // } else { // binding.btPlayPause.setImageResource( // R.drawable.baseline_play_circle_24, // ) -//// songChangeListener.onIsPlayingChange() +// // songChangeListener.onIsPlayingChange() // } // } // } @@ -686,7 +687,7 @@ class NowPlayingFragment : Fragment() { // launch { // viewModel.bufferedPercentage.collect { // binding.buffered.progress = it -//// Log.d("buffered", it.toString()) +// // Log.d("buffered", it.toString()) // } // } // // Check if song is ready to play. And make progress bar indeterminate @@ -700,7 +701,7 @@ class NowPlayingFragment : Fragment() { // launch { // viewModel.progressMillis.collect { // if (viewModel._lyrics.value?.data != null) { -//// val temp = viewModel.getLyricsString(it) +// // val temp = viewModel.getLyricsString(it) // val lyrics = viewModel._lyrics.value!!.data // binding.tvSyncState.text = // when (viewModel.getLyricsSyncState()) { @@ -712,9 +713,9 @@ class NowPlayingFragment : Fragment() { // // Config.SyncState.UNSYNCED -> getString(R.string.unsynced) // } -//// viewModel._lyrics.value?.data?.let { -//// -//// } +// // viewModel._lyrics.value?.data?.let { +// // +// // } // val index = viewModel.getActiveLyrics(it) // if (index != null) { // if (lyrics?.lines?.get(0)?.words == "Lyrics not found") { @@ -731,7 +732,7 @@ class NowPlayingFragment : Fragment() { // ) // lyricsFullAdapter.updateOriginalLyrics(it1) // if (viewModel.getLyricsSyncState() == Config.SyncState.LINE_SYNCED && lyricsAdapter.index != index) { -//// binding.rvLyrics.addOnItemTouchListener(disableScrolling) +// // binding.rvLyrics.addOnItemTouchListener(disableScrolling) // lyricsAdapter.setActiveLyrics(index) // lyricsFullAdapter.setActiveLyrics(index) // if (index == -1) { @@ -742,13 +743,13 @@ class NowPlayingFragment : Fragment() { // } else if (viewModel.getLyricsSyncState() == Config.SyncState.UNSYNCED && lyricsAdapter.index != -1) { // lyricsAdapter.setActiveLyrics(-1) // lyricsFullAdapter.setActiveLyrics(-1) -//// binding.rvLyrics.removeOnItemTouchListener(disableScrolling) +// // binding.rvLyrics.removeOnItemTouchListener(disableScrolling) // } -//// it1.lines?.find { line -> line.words == temp.nowLyric } -//// ?.let { it2 -> -//// lyricsAdapter.setActiveLyrics(it2) -//// binding.rvLyrics.smoothScrollToPosition(it1.lines.indexOf(it2)) -//// } +// // it1.lines?.find { line -> line.words == temp.nowLyric } +// // ?.let { it2 -> +// // lyricsAdapter.setActiveLyrics(it2) +// // binding.rvLyrics.smoothScrollToPosition(it1.lines.indexOf(it2)) +// // } // } // // if (binding.btFull.text == getString(R.string.show)) { @@ -759,34 +760,34 @@ class NowPlayingFragment : Fragment() { // if (binding.lyricsLayout.visibility != View.VISIBLE) { // binding.lyricsLayout.visibility = View.VISIBLE // } -//// if (temp.nowLyric != null) { -//// binding.tvNowLyrics.visibility = View.VISIBLE -//// binding.tvNowLyrics.text = temp.nowLyric -//// } else { -//// binding.tvNowLyrics.visibility = View.GONE -//// } -//// if (temp.prevLyrics != null) { -//// binding.tvPrevLyrics.visibility = View.VISIBLE -//// if (temp.prevLyrics.size > 1) { -//// val txt = temp.prevLyrics[0] + "\n" + temp.prevLyrics[1] -//// binding.tvPrevLyrics.text = txt -//// } else { -//// binding.tvPrevLyrics.text = temp.prevLyrics[0] -//// } -//// } else { -//// binding.tvPrevLyrics.visibility = View.GONE -//// } -//// if (temp.nextLyric != null) { -//// binding.tvNextLyrics.visibility = View.VISIBLE -//// if (temp.nextLyric.size > 1) { -//// val txt = temp.nextLyric[0] + "\n" + temp.nextLyric[1] -//// binding.tvNextLyrics.text = txt -//// } else { -//// binding.tvNextLyrics.text = temp.nextLyric[0] -//// } -//// } else { -//// binding.tvNextLyrics.visibility = View.GONE -//// } +// // if (temp.nowLyric != null) { +// // binding.tvNowLyrics.visibility = View.VISIBLE +// // binding.tvNowLyrics.text = temp.nowLyric +// // } else { +// // binding.tvNowLyrics.visibility = View.GONE +// // } +// // if (temp.prevLyrics != null) { +// // binding.tvPrevLyrics.visibility = View.VISIBLE +// // if (temp.prevLyrics.size > 1) { +// // val txt = temp.prevLyrics[0] + "\n" + temp.prevLyrics[1] +// // binding.tvPrevLyrics.text = txt +// // } else { +// // binding.tvPrevLyrics.text = temp.prevLyrics[0] +// // } +// // } else { +// // binding.tvPrevLyrics.visibility = View.GONE +// // } +// // if (temp.nextLyric != null) { +// // binding.tvNextLyrics.visibility = View.VISIBLE +// // if (temp.nextLyric.size > 1) { +// // val txt = temp.nextLyric[0] + "\n" + temp.nextLyric[1] +// // binding.tvNextLyrics.text = txt +// // } else { +// // binding.tvNextLyrics.text = temp.nextLyric[0] +// // } +// // } else { +// // binding.tvNextLyrics.visibility = View.GONE +// // } // } // } // } else { @@ -910,49 +911,49 @@ class NowPlayingFragment : Fragment() { // } // } // } -//// val job16 = launch { -//// viewModel.firstTrackAdded.collectLatest { added -> -//// if (added) { -//// when(type) { -//// Config.SONG_CLICK -> { -//// viewModel.nowPLaying.first()?.let { getRelated(it.mediaId) } -//// viewModel.changeFirstTrackAddedToFalse() -//// } -//// Config.SHARE -> { -//// viewModel.nowPLaying.first()?.let { getRelated(it.mediaId) } -//// viewModel.changeFirstTrackAddedToFalse() -//// } -//// Config.VIDEO_CLICK -> { -//// -//// // viewModel.getFormat(it.videoId) -//// viewModel.nowPLaying.first()?.let { getRelated(it.mediaId) } -//// viewModel.changeFirstTrackAddedToFalse() -//// } -//// Config.ALBUM_CLICK -> { -//// if (index == null) { -//// // fetchSourceFromQueue(downloaded = downloaded ?: 0) -//// viewModel.loadPlaylistOrAlbum(downloaded = downloaded ?: 0) -//// } else { -//// // fetchSourceFromQueue(index!!, downloaded = downloaded ?: 0) -//// viewModel.loadPlaylistOrAlbum(downloaded = downloaded ?: 0, index = index) -//// } -//// -//// viewModel.changeFirstTrackAddedToFalse() -//// } -//// Config.PLAYLIST_CLICK -> { -//// if (index == null) { -//// // fetchSourceFromQueue(downloaded = downloaded ?: 0) -//// viewModel.loadPlaylistOrAlbum(downloaded = downloaded ?: 0) -//// } else { -//// // fetchSourceFromQueue(index!!, downloaded = downloaded ?: 0) -//// viewModel.loadPlaylistOrAlbum(downloaded = downloaded ?: 0, index = index) -//// } -//// viewModel.changeFirstTrackAddedToFalse() -//// } -//// } -//// } -//// } -//// } +// // val job16 = launch { +// // viewModel.firstTrackAdded.collectLatest { added -> +// // if (added) { +// // when(type) { +// // Config.SONG_CLICK -> { +// // viewModel.nowPLaying.first()?.let { getRelated(it.mediaId) } +// // viewModel.changeFirstTrackAddedToFalse() +// // } +// // Config.SHARE -> { +// // viewModel.nowPLaying.first()?.let { getRelated(it.mediaId) } +// // viewModel.changeFirstTrackAddedToFalse() +// // } +// // Config.VIDEO_CLICK -> { +// // +// // // viewModel.getFormat(it.videoId) +// // viewModel.nowPLaying.first()?.let { getRelated(it.mediaId) } +// // viewModel.changeFirstTrackAddedToFalse() +// // } +// // Config.ALBUM_CLICK -> { +// // if (index == null) { +// // // fetchSourceFromQueue(downloaded = downloaded ?: 0) +// // viewModel.loadPlaylistOrAlbum(downloaded = downloaded ?: 0) +// // } else { +// // // fetchSourceFromQueue(index!!, downloaded = downloaded ?: 0) +// // viewModel.loadPlaylistOrAlbum(downloaded = downloaded ?: 0, index = index) +// // } +// // +// // viewModel.changeFirstTrackAddedToFalse() +// // } +// // Config.PLAYLIST_CLICK -> { +// // if (index == null) { +// // // fetchSourceFromQueue(downloaded = downloaded ?: 0) +// // viewModel.loadPlaylistOrAlbum(downloaded = downloaded ?: 0) +// // } else { +// // // fetchSourceFromQueue(index!!, downloaded = downloaded ?: 0) +// // viewModel.loadPlaylistOrAlbum(downloaded = downloaded ?: 0, index = index) +// // } +// // viewModel.changeFirstTrackAddedToFalse() +// // } +// // } +// // } +// // } +// // } // val job16 = // launch { // viewModel.translateLyrics.collect { @@ -1291,28 +1292,28 @@ class NowPlayingFragment : Fragment() { // isFullScreen = true // findNavController().navigateSafe(R.id.action_global_fullscreenFragment) // -//// requireActivity().requestedOrientation = -//// ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE -//// val parent = fullscreen.root.parent as View -//// parent.fitsSystemWindows = true -//// val params = -//// parent.layoutParams as CoordinatorLayout.LayoutParams -//// val behavior = params.behavior -//// if (behavior != null && behavior is BottomSheetBehavior<*>) { -//// behavior.peekHeight = binding.root.height -//// behavior.state = BottomSheetBehavior.STATE_EXPANDED -//// behavior.addBottomSheetCallback(object : BottomSheetCallback() { -//// override fun onStateChanged( bottomSheet: View, newState: Int) { -//// if (newState == BottomSheetBehavior.STATE_DRAGGING) { -//// behavior.state = BottomSheetBehavior.STATE_EXPANDED -//// } -//// } -//// -//// override fun onSlide(bottomSheet: View, slideOffset: Float) {} -//// }) -//// } +// // requireActivity().requestedOrientation = +// // ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE +// // val parent = fullscreen.root.parent as View +// // parent.fitsSystemWindows = true +// // val params = +// // parent.layoutParams as CoordinatorLayout.LayoutParams +// // val behavior = params.behavior +// // if (behavior != null && behavior is BottomSheetBehavior<*>) { +// // behavior.peekHeight = binding.root.height +// // behavior.state = BottomSheetBehavior.STATE_EXPANDED +// // behavior.addBottomSheetCallback(object : BottomSheetCallback() { +// // override fun onStateChanged( bottomSheet: View, newState: Int) { +// // if (newState == BottomSheetBehavior.STATE_DRAGGING) { +// // behavior.state = BottomSheetBehavior.STATE_EXPANDED +// // } +// // } +// // +// // override fun onSlide(bottomSheet: View, slideOffset: Float) {} +// // }) +// // } // -//// } +// // } // } // binding.playerLayout.setOnClickListener { // val shortAnimationDuration = @@ -1913,7 +1914,7 @@ class NowPlayingFragment : Fragment() { // Log.d("CHECK QUEUE", "updateUIfromQueueNowPlaying: ${Queue.getQueue()}") // val nowPlaying = Queue.getNowPlaying() // if (nowPlaying != null) { -//// viewModel.getFormat(nowPlaying.videoId) +// // viewModel.getFormat(nowPlaying.videoId) // binding.ivArt.setImageResource(0) // binding.loadingArt.visibility = View.VISIBLE // Log.d("Update UI", "current: ${nowPlaying.title}") @@ -1954,7 +1955,7 @@ class NowPlayingFragment : Fragment() { // } // Log.d("Check Start Color", "transform: $startColor") // } -//// val centerColor = 0x6C6C6C +// // val centerColor = 0x6C6C6C // val endColor = 0x1b1a1f // val gd = // GradientDrawable( @@ -1979,100 +1980,100 @@ class NowPlayingFragment : Fragment() { // transition.isCrossFadeEnabled = true // transition.startTransition(500) // -//// viewModel.lyricsBackground.observe(viewLifecycleOwner, Observer { color -> -//// binding.lyricsLayout.setCardBackgroundColor(color) -//// Log.d("Update UI", "Lyrics: $color") -//// updateStatusBarColor(color) -//// }) +// // viewModel.lyricsBackground.observe(viewLifecycleOwner, Observer { color -> +// // binding.lyricsLayout.setCardBackgroundColor(color) +// // Log.d("Update UI", "Lyrics: $color") +// // updateStatusBarColor(color) +// // }) // binding.lyricsLayout.setCardBackgroundColor( // bg, // ) // binding.infoLayout.setCardBackgroundColor(bg) -//// songChangeListener.onNowPlayingSongChange() +// // songChangeListener.onNowPlayingSongChange() // }, // ) // transformations( // RoundedCornersTransformation(8f), // ) // } -//// val request = ImageRequest.Builder(requireContext()) -//// .data(Uri.parse(thumbUrl)) -//// .diskCacheKey(nowPlaying.videoId) -//// .diskCachePolicy(CachePolicy.ENABLED) -//// .target( -//// onStart = { -//// binding.ivArt.setImageResource(0) -//// binding.loadingArt.visibility = View.VISIBLE -//// Log.d("Update UI", "onStart: ") -//// }, -//// onSuccess = { result -> -//// binding.ivArt.visibility = View.VISIBLE -//// binding.loadingArt.visibility = View.GONE -//// binding.ivArt.setImageDrawable(result) -//// Log.d("Update UI", "onSuccess: ") -//// if (viewModel.gradientDrawable.value != null) { -//// viewModel.gradientDrawable.observe(viewLifecycleOwner) { -//// binding.rootLayout.background = it -//// // viewModel.lyricsBackground.observe(viewLifecycleOwner, Observer { color -> -//// // binding.lyricsLayout.setCardBackgroundColor(color) -//// // Log.d("Update UI", "Lyrics: $color") -//// // updateStatusBarColor(color) -//// // }) -//// viewModel.lyricsBackground.value?.let { it1 -> -//// binding.lyricsLayout.setCardBackgroundColor( -//// it1 -//// ) -//// } -//// } -//// Log.d("Update UI", "updateUI: NULL") -//// } -//// // songChangeListener.onNowPlayingSongChange() -//// }, -//// ) -//// .transformations(object : Transformation { -//// override val cacheKey: String -//// get() = nowPlaying.videoId -//// -//// override suspend fun transform(input: Bitmap, size: Size): Bitmap { -//// val p = Palette.from(input).generate() -//// val defaultColor = 0x000000 -//// var startColor = p.getDarkVibrantColor(defaultColor) -//// Log.d("Check Start Color", "transform: $startColor") -//// if (startColor == defaultColor) { -//// startColor = p.getDarkMutedColor(defaultColor) -//// if (startColor == defaultColor) { -//// startColor = p.getVibrantColor(defaultColor) -//// if (startColor == defaultColor) { -//// startColor = p.getMutedColor(defaultColor) -//// if (startColor == defaultColor) { -//// startColor = p.getLightVibrantColor(defaultColor) -//// if (startColor == defaultColor) { -//// startColor = p.getLightMutedColor(defaultColor) -//// } -//// } -//// } -//// } -//// Log.d("Check Start Color", "transform: $startColor") -//// } -//// // val centerColor = 0x6C6C6C -//// val endColor = 0x1b1a1f -//// val gd = GradientDrawable( -//// GradientDrawable.Orientation.TOP_BOTTOM, -//// intArrayOf(startColor, endColor) -//// ) -//// gd.cornerRadius = 0f -//// gd.gradientType = GradientDrawable.LINEAR_GRADIENT -//// gd.gradientRadius = 0.5f -//// gd.alpha = 150 -//// val bg = ColorUtils.setAlphaComponent(startColor, 230) -//// viewModel.gradientDrawable.postValue(gd) -//// viewModel.lyricsBackground.postValue(bg) -//// return input -//// } -//// -//// }) -//// .build() -//// ImageLoader(requireContext()).enqueue(request) +// // val request = ImageRequest.Builder(requireContext()) +// // .data(Uri.parse(thumbUrl)) +// // .diskCacheKey(nowPlaying.videoId) +// // .diskCachePolicy(CachePolicy.ENABLED) +// // .target( +// // onStart = { +// // binding.ivArt.setImageResource(0) +// // binding.loadingArt.visibility = View.VISIBLE +// // Log.d("Update UI", "onStart: ") +// // }, +// // onSuccess = { result -> +// // binding.ivArt.visibility = View.VISIBLE +// // binding.loadingArt.visibility = View.GONE +// // binding.ivArt.setImageDrawable(result) +// // Log.d("Update UI", "onSuccess: ") +// // if (viewModel.gradientDrawable.value != null) { +// // viewModel.gradientDrawable.observe(viewLifecycleOwner) { +// // binding.rootLayout.background = it +// // // viewModel.lyricsBackground.observe(viewLifecycleOwner, Observer { color -> +// // // binding.lyricsLayout.setCardBackgroundColor(color) +// // // Log.d("Update UI", "Lyrics: $color") +// // // updateStatusBarColor(color) +// // // }) +// // viewModel.lyricsBackground.value?.let { it1 -> +// // binding.lyricsLayout.setCardBackgroundColor( +// // it1 +// // ) +// // } +// // } +// // Log.d("Update UI", "updateUI: NULL") +// // } +// // // songChangeListener.onNowPlayingSongChange() +// // }, +// // ) +// // .transformations(object : Transformation { +// // override val cacheKey: String +// // get() = nowPlaying.videoId +// // +// // override suspend fun transform(input: Bitmap, size: Size): Bitmap { +// // val p = Palette.from(input).generate() +// // val defaultColor = 0x000000 +// // var startColor = p.getDarkVibrantColor(defaultColor) +// // Log.d("Check Start Color", "transform: $startColor") +// // if (startColor == defaultColor) { +// // startColor = p.getDarkMutedColor(defaultColor) +// // if (startColor == defaultColor) { +// // startColor = p.getVibrantColor(defaultColor) +// // if (startColor == defaultColor) { +// // startColor = p.getMutedColor(defaultColor) +// // if (startColor == defaultColor) { +// // startColor = p.getLightVibrantColor(defaultColor) +// // if (startColor == defaultColor) { +// // startColor = p.getLightMutedColor(defaultColor) +// // } +// // } +// // } +// // } +// // Log.d("Check Start Color", "transform: $startColor") +// // } +// // // val centerColor = 0x6C6C6C +// // val endColor = 0x1b1a1f +// // val gd = GradientDrawable( +// // GradientDrawable.Orientation.TOP_BOTTOM, +// // intArrayOf(startColor, endColor) +// // ) +// // gd.cornerRadius = 0f +// // gd.gradientType = GradientDrawable.LINEAR_GRADIENT +// // gd.gradientRadius = 0.5f +// // gd.alpha = 150 +// // val bg = ColorUtils.setAlphaComponent(startColor, 230) +// // viewModel.gradientDrawable.postValue(gd) +// // viewModel.lyricsBackground.postValue(bg) +// // return input +// // } +// // +// // }) +// // .build() +// // ImageLoader(requireContext()).enqueue(request) // binding.topAppBar.subtitle = from // viewModel.from.postValue(from) // binding.tvSongTitle.text = nowPlaying.title @@ -2095,7 +2096,7 @@ class NowPlayingFragment : Fragment() { // if (mediaItem != null) { // binding.ivArt.setImageResource(0) // binding.loadingArt.visibility = View.VISIBLE -//// viewModel.getFormat(mediaItem.mediaId) +// // viewModel.getFormat(mediaItem.mediaId) // Log.d("Update UI", "current: ${mediaItem.mediaMetadata.title}") // binding.tvSongTitle.visibility = View.VISIBLE // binding.tvSongArtist.visibility = View.VISIBLE @@ -2139,7 +2140,7 @@ class NowPlayingFragment : Fragment() { // } // Log.d("Check Start Color", "transform: $startColor") // } -//// val centerColor = 0x6C6C6C +// // val centerColor = 0x6C6C6C // val endColor = 0x1b1a1f // val gd = // GradientDrawable( @@ -2163,108 +2164,108 @@ class NowPlayingFragment : Fragment() { // binding.rootLayout.background = transition // transition.isCrossFadeEnabled = true // transition.startTransition(500) -//// viewModel.lyricsBackground.observe(viewLifecycleOwner, Observer { color -> -//// binding.lyricsLayout.setCardBackgroundColor(color) -//// Log.d("Update UI", "Lyrics: $color") -//// updateStatusBarColor(color) -//// }) +// // viewModel.lyricsBackground.observe(viewLifecycleOwner, Observer { color -> +// // binding.lyricsLayout.setCardBackgroundColor(color) +// // Log.d("Update UI", "Lyrics: $color") +// // updateStatusBarColor(color) +// // }) // binding.lyricsLayout.setCardBackgroundColor( // bg // ) // binding.infoLayout.setCardBackgroundColor(bg) -//// songChangeListener.onNowPlayingSongChange() +// // songChangeListener.onNowPlayingSongChange() // }, // ) // transformations( // RoundedCornersTransformation(8f) // ) // } -//// val request = ImageRequest.Builder(requireContext()) -//// .data(mediaItem.mediaMetadata.artworkUri) -//// .diskCacheKey(mediaItem.mediaId) -//// .diskCachePolicy(CachePolicy.ENABLED) -//// .target( -//// onStart = { -//// binding.ivArt.setImageResource(0) -//// binding.loadingArt.visibility = View.VISIBLE -//// Log.d("Update UI", "onStart: ") -//// }, -//// onSuccess = { result -> -//// binding.ivArt.visibility = View.VISIBLE -//// binding.loadingArt.visibility = View.GONE -//// binding.ivArt.setImageDrawable(result) -//// Log.d("Update UI", "onSuccess: ") -//// if (viewModel.gradientDrawable.value != null) { -//// viewModel.gradientDrawable.observe(viewLifecycleOwner) { -//// if (it != null) { -//// val start = binding.rootLayout.background -//// val transition = TransitionDrawable(arrayOf(start, it)) -//// binding.rootLayout.background = transition -//// transition.isCrossFadeEnabled = true -//// transition.startTransition(500) -//// } -//// // viewModel.lyricsBackground.observe(viewLifecycleOwner, Observer { color -> -//// // binding.lyricsLayout.setCardBackgroundColor(color) -//// // Log.d("Update UI", "Lyrics: $color") -//// // updateStatusBarColor(color) -//// // }) -//// viewModel.lyricsBackground.value?.let { it1 -> -//// binding.lyricsLayout.setCardBackgroundColor( -//// it1 -//// ) -//// } -//// } -//// Log.d("Update UI", "updateUI: NULL") -//// } -//// // songChangeListener.onNowPlayingSongChange() -//// }, -//// ) -//// .diskCacheKey(mediaItem.mediaMetadata.artworkUri.toString()) -//// .diskCachePolicy(CachePolicy.ENABLED) -//// .transformations(object : Transformation { -//// override val cacheKey: String -//// get() = "paletteArtTransformer" -//// -//// override suspend fun transform(input: Bitmap, size: Size): Bitmap { -//// val p = Palette.from(input).generate() -//// val defaultColor = 0x000000 -//// var startColor = p.getDarkVibrantColor(defaultColor) -//// Log.d("Check Start Color", "transform: $startColor") -//// if (startColor == defaultColor) { -//// startColor = p.getDarkMutedColor(defaultColor) -//// if (startColor == defaultColor) { -//// startColor = p.getVibrantColor(defaultColor) -//// if (startColor == defaultColor) { -//// startColor = p.getMutedColor(defaultColor) -//// if (startColor == defaultColor) { -//// startColor = p.getLightVibrantColor(defaultColor) -//// if (startColor == defaultColor) { -//// startColor = p.getLightMutedColor(defaultColor) -//// } -//// } -//// } -//// } -//// Log.d("Check Start Color", "transform: $startColor") -//// } -//// // val centerColor = 0x6C6C6C -//// val endColor = 0x1b1a1f -//// val gd = GradientDrawable( -//// GradientDrawable.Orientation.TOP_BOTTOM, -//// intArrayOf(startColor, endColor) -//// ) -//// gd.cornerRadius = 0f -//// gd.gradientType = GradientDrawable.LINEAR_GRADIENT -//// gd.gradientRadius = 0.5f -//// gd.alpha = 150 -//// val bg = ColorUtils.setAlphaComponent(startColor, 230) -//// viewModel.gradientDrawable.postValue(gd) -//// viewModel.lyricsBackground.postValue(bg) -//// return input -//// } -//// -//// }) -//// .build() -//// ImageLoader(requireContext()).enqueue(request) +// // val request = ImageRequest.Builder(requireContext()) +// // .data(mediaItem.mediaMetadata.artworkUri) +// // .diskCacheKey(mediaItem.mediaId) +// // .diskCachePolicy(CachePolicy.ENABLED) +// // .target( +// // onStart = { +// // binding.ivArt.setImageResource(0) +// // binding.loadingArt.visibility = View.VISIBLE +// // Log.d("Update UI", "onStart: ") +// // }, +// // onSuccess = { result -> +// // binding.ivArt.visibility = View.VISIBLE +// // binding.loadingArt.visibility = View.GONE +// // binding.ivArt.setImageDrawable(result) +// // Log.d("Update UI", "onSuccess: ") +// // if (viewModel.gradientDrawable.value != null) { +// // viewModel.gradientDrawable.observe(viewLifecycleOwner) { +// // if (it != null) { +// // val start = binding.rootLayout.background +// // val transition = TransitionDrawable(arrayOf(start, it)) +// // binding.rootLayout.background = transition +// // transition.isCrossFadeEnabled = true +// // transition.startTransition(500) +// // } +// // // viewModel.lyricsBackground.observe(viewLifecycleOwner, Observer { color -> +// // // binding.lyricsLayout.setCardBackgroundColor(color) +// // // Log.d("Update UI", "Lyrics: $color") +// // // updateStatusBarColor(color) +// // // }) +// // viewModel.lyricsBackground.value?.let { it1 -> +// // binding.lyricsLayout.setCardBackgroundColor( +// // it1 +// // ) +// // } +// // } +// // Log.d("Update UI", "updateUI: NULL") +// // } +// // // songChangeListener.onNowPlayingSongChange() +// // }, +// // ) +// // .diskCacheKey(mediaItem.mediaMetadata.artworkUri.toString()) +// // .diskCachePolicy(CachePolicy.ENABLED) +// // .transformations(object : Transformation { +// // override val cacheKey: String +// // get() = "paletteArtTransformer" +// // +// // override suspend fun transform(input: Bitmap, size: Size): Bitmap { +// // val p = Palette.from(input).generate() +// // val defaultColor = 0x000000 +// // var startColor = p.getDarkVibrantColor(defaultColor) +// // Log.d("Check Start Color", "transform: $startColor") +// // if (startColor == defaultColor) { +// // startColor = p.getDarkMutedColor(defaultColor) +// // if (startColor == defaultColor) { +// // startColor = p.getVibrantColor(defaultColor) +// // if (startColor == defaultColor) { +// // startColor = p.getMutedColor(defaultColor) +// // if (startColor == defaultColor) { +// // startColor = p.getLightVibrantColor(defaultColor) +// // if (startColor == defaultColor) { +// // startColor = p.getLightMutedColor(defaultColor) +// // } +// // } +// // } +// // } +// // Log.d("Check Start Color", "transform: $startColor") +// // } +// // // val centerColor = 0x6C6C6C +// // val endColor = 0x1b1a1f +// // val gd = GradientDrawable( +// // GradientDrawable.Orientation.TOP_BOTTOM, +// // intArrayOf(startColor, endColor) +// // ) +// // gd.cornerRadius = 0f +// // gd.gradientType = GradientDrawable.LINEAR_GRADIENT +// // gd.gradientRadius = 0.5f +// // gd.alpha = 150 +// // val bg = ColorUtils.setAlphaComponent(startColor, 230) +// // viewModel.gradientDrawable.postValue(gd) +// // viewModel.lyricsBackground.postValue(bg) +// // return input +// // } +// // +// // }) +// // .build() +// // ImageLoader(requireContext()).enqueue(request) // } // } diff --git a/app/src/main/java/com/maxrave/simpmusic/ui/fragment/player/QueueFragment.kt b/app/src/main/java/com/maxrave/simpmusic/ui/fragment/player/QueueFragment.kt index ee17dc92..4f20e018 100644 --- a/app/src/main/java/com/maxrave/simpmusic/ui/fragment/player/QueueFragment.kt +++ b/app/src/main/java/com/maxrave/simpmusic/ui/fragment/player/QueueFragment.kt @@ -27,18 +27,14 @@ import com.maxrave.simpmusic.databinding.QueueBottomSheetBinding import com.maxrave.simpmusic.extension.setEnabledAll import com.maxrave.simpmusic.service.StateSource import com.maxrave.simpmusic.viewModel.SharedViewModel -import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking - -@AndroidEntryPoint -class QueueFragment: BottomSheetDialogFragment() { - +class QueueFragment : BottomSheetDialogFragment() { private val viewModel by activityViewModels() private var _binding: QueueBottomSheetBinding? = null - private val binding get() = _binding!! + val binding get() = _binding!! private lateinit var queueAdapter: QueueAdapter override fun onCreate(savedInstanceState: Bundle?) { @@ -54,7 +50,6 @@ class QueueFragment: BottomSheetDialogFragment() { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { val dialog = BottomSheetDialog(requireContext(), theme) dialog.setOnShowListener { - val bottomSheetDialog = it as BottomSheetDialog val parentLayout = bottomSheetDialog.findViewById(com.google.android.material.R.id.design_bottom_sheet) @@ -77,14 +72,17 @@ class QueueFragment: BottomSheetDialogFragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? + savedInstanceState: Bundle?, ): View { _binding = QueueBottomSheetBinding.inflate(inflater, container, false) return binding.root } @UnstableApi - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + override fun onViewCreated( + view: View, + savedInstanceState: Bundle?, + ) { super.onViewCreated(view, savedInstanceState) binding.loadingQueue.visibility = View.VISIBLE binding.rvQueue.visibility = View.GONE @@ -100,60 +98,74 @@ class QueueFragment: BottomSheetDialogFragment() { val callback: ItemTouchHelper.Callback = RecyclerRowMoveCallback(queueAdapter) val touchHelper = ItemTouchHelper(callback) touchHelper.attachToRecyclerView(binding.rvQueue) - val queue = runBlocking { viewModel.simpleMediaServiceHandler?.queueData?.first()?.listTracks } + val queue = + runBlocking { + viewModel.simpleMediaServiceHandler + ?.queueData + ?.first() + ?.listTracks + } if (!queue.isNullOrEmpty()) { queueAdapter.updateList(queue) } lifecycleScope.launch { - val job1 = launch { - viewModel.simpleMediaServiceHandler?.stateFlow?.collect { state -> - when (state) { - StateSource.STATE_INITIALIZING -> { - binding.loadingQueue.visibility = View.VISIBLE - binding.rvQueue.visibility = View.VISIBLE - } - StateSource.STATE_ERROR -> { - binding.loadingQueue.visibility = View.GONE - binding.rvQueue.visibility = View.VISIBLE - } - StateSource.STATE_INITIALIZED -> { - binding.loadingQueue.visibility = View.GONE - binding.rvQueue.visibility = View.VISIBLE - queueAdapter.updateList(viewModel.simpleMediaServiceHandler?.queueData?.first()?.listTracks ?: arrayListOf()) - } - else -> { - binding.loadingQueue.visibility = View.VISIBLE - binding.rvQueue.visibility = View.VISIBLE + val job1 = + launch { + viewModel.simpleMediaServiceHandler?.stateFlow?.collect { state -> + when (state) { + StateSource.STATE_INITIALIZING -> { + binding.loadingQueue.visibility = View.VISIBLE + binding.rvQueue.visibility = View.VISIBLE + } + StateSource.STATE_ERROR -> { + binding.loadingQueue.visibility = View.GONE + binding.rvQueue.visibility = View.VISIBLE + } + StateSource.STATE_INITIALIZED -> { + binding.loadingQueue.visibility = View.GONE + binding.rvQueue.visibility = View.VISIBLE + queueAdapter.updateList( + viewModel.simpleMediaServiceHandler + ?.queueData + ?.first() + ?.listTracks ?: arrayListOf(), + ) + } + else -> { + binding.loadingQueue.visibility = View.VISIBLE + binding.rvQueue.visibility = View.VISIBLE + } } } } - } - val job2 = launch { - viewModel.nowPlayingState.collect { - if (it != null){ - binding.ivThumbnail.load(it.mediaItem.mediaMetadata.artworkUri) - binding.tvSongTitle.text = it.mediaItem.mediaMetadata.title - binding.tvSongTitle.isSelected = true - binding.tvSongArtist.text = it.mediaItem.mediaMetadata.artist - binding.tvSongArtist.isSelected = true - if (viewModel.simpleMediaServiceHandler?.stateFlow?.first() == StateSource.STATE_INITIALIZED || - viewModel.simpleMediaServiceHandler?.stateFlow?.first() == StateSource.STATE_INITIALIZING){ - val index = it.songEntity?.videoId?.let { it1 -> queueAdapter.getIndexOf(it1) } - if (index != null) { - binding.rvQueue.smoothScrollToPosition(index) - queueAdapter.setCurrentPlaying(index) + val job2 = + launch { + viewModel.nowPlayingState.collect { + if (it != null) { + binding.ivThumbnail.load(it.mediaItem.mediaMetadata.artworkUri) + binding.tvSongTitle.text = it.mediaItem.mediaMetadata.title + binding.tvSongTitle.isSelected = true + binding.tvSongArtist.text = it.mediaItem.mediaMetadata.artist + binding.tvSongArtist.isSelected = true + if (viewModel.simpleMediaServiceHandler?.stateFlow?.first() == StateSource.STATE_INITIALIZED || + viewModel.simpleMediaServiceHandler?.stateFlow?.first() == StateSource.STATE_INITIALIZING + ) { + val index = it.songEntity?.videoId?.let { it1 -> queueAdapter.getIndexOf(it1) } + if (index != null) { + binding.rvQueue.smoothScrollToPosition(index) + queueAdapter.setCurrentPlaying(index) + } } } } } - } - val job3 = launch { - viewModel.simpleMediaServiceHandler?.currentSongIndex?.collect{ index -> - Log.d("QueueFragment", "onViewCreated: $index") - + val job3 = + launch { + viewModel.simpleMediaServiceHandler?.currentSongIndex?.collect { index -> + Log.d("QueueFragment", "onViewCreated: $index") + } } - } // val job4 = launch { // viewModel.simpleMediaServiceHandler?.added?.collect { isAdded -> // Log.d("Check Added in Queue", "$isAdded") @@ -183,94 +195,129 @@ class QueueFragment: BottomSheetDialogFragment() { binding.tvSongTitle.isSelected = true binding.tvSongArtist.isSelected = true - binding.rvQueue.addOnScrollListener(object : RecyclerView.OnScrollListener() { - - override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { - val layoutManager = recyclerView.layoutManager as LinearLayoutManager? - if (viewModel.simpleMediaServiceHandler?.stateFlow?.value != StateSource.STATE_INITIALIZING && layoutManager != null - && layoutManager.findLastCompletelyVisibleItemPosition() == queueAdapter.itemCount - 1) { - viewModel.simpleMediaServiceHandler?.loadMore() + binding.rvQueue.addOnScrollListener( + object : RecyclerView.OnScrollListener() { + override fun onScrolled( + recyclerView: RecyclerView, + dx: Int, + dy: Int, + ) { + val layoutManager = recyclerView.layoutManager as LinearLayoutManager? + if (viewModel.simpleMediaServiceHandler?.stateFlow?.value != StateSource.STATE_INITIALIZING && + layoutManager != null && + layoutManager.findLastCompletelyVisibleItemPosition() == queueAdapter.itemCount - 1 + ) { + viewModel.simpleMediaServiceHandler?.loadMore() + } } - } - }) + }, + ) - queueAdapter.setOnClickListener(object : QueueAdapter.OnItemClickListener { - override fun onItemClick(position: Int) { - viewModel.playMediaItemInMediaSource(position) - dismiss() - - } - }) - queueAdapter.setOnSwapListener(object : QueueAdapter.OnSwapListener { - override fun onSwap(from: Int, to: Int) { - lifecycleScope.launch { - viewModel.simpleMediaServiceHandler?.swap(from, to) - queueAdapter.updateList(viewModel.simpleMediaServiceHandler?.queueData?.first()?.listTracks ?: arrayListOf()) + queueAdapter.setOnClickListener( + object : QueueAdapter.OnItemClickListener { + override fun onItemClick(position: Int) { + viewModel.playMediaItemInMediaSource(position) + dismiss() } - } - }) - queueAdapter.setOnOptionClickListener(object : QueueAdapter.OnOptionClickListener { - override fun onOptionClick(position: Int) { - val dialog = BottomSheetDialog(requireContext()) - val dialogView = BottomSheetQueueTrackOptionBinding.inflate(layoutInflater) - if (viewModel.simpleMediaServiceHandler != null) { + }, + ) + queueAdapter.setOnSwapListener( + object : QueueAdapter.OnSwapListener { + override fun onSwap( + from: Int, + to: Int, + ) { lifecycleScope.launch { - repeatOnLifecycle(Lifecycle.State.CREATED) { - viewModel.simpleMediaServiceHandler?.queueData?.collect { queueData -> - val queueList = queueData?.listTracks ?: arrayListOf() - if (queueList.size > 1) { - when (position) { - 0 -> { - setEnabledAll(dialogView.btMoveUp, false) - setEnabledAll(dialogView.btMoveDown, true) - } + viewModel.simpleMediaServiceHandler?.swap(from, to) + queueAdapter.updateList( + viewModel.simpleMediaServiceHandler + ?.queueData + ?.first() + ?.listTracks ?: arrayListOf(), + ) + } + } + }, + ) + queueAdapter.setOnOptionClickListener( + object : QueueAdapter.OnOptionClickListener { + override fun onOptionClick(position: Int) { + val dialog = BottomSheetDialog(requireContext()) + val dialogView = BottomSheetQueueTrackOptionBinding.inflate(layoutInflater) + if (viewModel.simpleMediaServiceHandler != null) { + lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.CREATED) { + viewModel.simpleMediaServiceHandler?.queueData?.collect { queueData -> + val queueList = queueData?.listTracks ?: arrayListOf() + if (queueList.size > 1) { + when (position) { + 0 -> { + setEnabledAll(dialogView.btMoveUp, false) + setEnabledAll(dialogView.btMoveDown, true) + } - queueList.size - 1 -> { - setEnabledAll(dialogView.btMoveUp, true) - setEnabledAll(dialogView.btMoveDown, false) - } + queueList.size - 1 -> { + setEnabledAll(dialogView.btMoveUp, true) + setEnabledAll(dialogView.btMoveDown, false) + } - else -> { - setEnabledAll(dialogView.btMoveUp, true) - setEnabledAll(dialogView.btMoveDown, true) + else -> { + setEnabledAll(dialogView.btMoveUp, true) + setEnabledAll(dialogView.btMoveDown, true) + } } + } else { + setEnabledAll(dialogView.btMoveUp, false) + setEnabledAll(dialogView.btMoveDown, false) + setEnabledAll(dialogView.btDelete, false) } - } else { - setEnabledAll(dialogView.btMoveUp, false) - setEnabledAll(dialogView.btMoveDown, false) - setEnabledAll(dialogView.btDelete, false) } } } - } - with(dialogView) { - btMoveUp.setOnClickListener { - lifecycleScope.launch { - viewModel.simpleMediaServiceHandler?.moveItemUp(position) - queueAdapter.updateList(viewModel.simpleMediaServiceHandler?.queueData?.first()?.listTracks ?: arrayListOf()) + with(dialogView) { + btMoveUp.setOnClickListener { + lifecycleScope.launch { + viewModel.simpleMediaServiceHandler?.moveItemUp(position) + queueAdapter.updateList( + viewModel.simpleMediaServiceHandler + ?.queueData + ?.first() + ?.listTracks ?: arrayListOf(), + ) + } + dialog.dismiss() } - dialog.dismiss() - } - btMoveDown.setOnClickListener { - lifecycleScope.launch { - viewModel.simpleMediaServiceHandler?.moveItemDown(position) - queueAdapter.updateList(viewModel.simpleMediaServiceHandler?.queueData?.first()?.listTracks ?: arrayListOf()) + btMoveDown.setOnClickListener { + lifecycleScope.launch { + viewModel.simpleMediaServiceHandler?.moveItemDown(position) + queueAdapter.updateList( + viewModel.simpleMediaServiceHandler + ?.queueData + ?.first() + ?.listTracks ?: arrayListOf(), + ) + } + dialog.dismiss() } - dialog.dismiss() - } - btDelete.setOnClickListener { - viewModel.simpleMediaServiceHandler?.removeMediaItem(position) - lifecycleScope.launch { - queueAdapter.updateList(viewModel.simpleMediaServiceHandler?.queueData?.first()?.listTracks ?: arrayListOf()) + btDelete.setOnClickListener { + viewModel.simpleMediaServiceHandler?.removeMediaItem(position) + lifecycleScope.launch { + queueAdapter.updateList( + viewModel.simpleMediaServiceHandler + ?.queueData + ?.first() + ?.listTracks ?: arrayListOf(), + ) + } + dialog.dismiss() } - dialog.dismiss() } } + dialog.setCancelable(true) + dialog.setContentView(dialogView.root) + dialog.show() } - dialog.setCancelable(true) - dialog.setContentView(dialogView.root) - dialog.show() - } - }) + }, + ) } } \ No newline at end of file diff --git a/app/src/main/java/com/maxrave/simpmusic/viewModel/AlbumViewModel.kt b/app/src/main/java/com/maxrave/simpmusic/viewModel/AlbumViewModel.kt index e55c2ae5..1fac105f 100644 --- a/app/src/main/java/com/maxrave/simpmusic/viewModel/AlbumViewModel.kt +++ b/app/src/main/java/com/maxrave/simpmusic/viewModel/AlbumViewModel.kt @@ -4,7 +4,6 @@ import android.app.Application import android.graphics.drawable.GradientDrawable import android.util.Log import android.widget.Toast -import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope @@ -13,17 +12,15 @@ import androidx.media3.exoplayer.offline.Download import com.maxrave.simpmusic.R import com.maxrave.simpmusic.common.DownloadState import com.maxrave.simpmusic.common.SELECTED_LANGUAGE -import com.maxrave.simpmusic.data.dataStore.DataStoreManager import com.maxrave.simpmusic.data.db.entities.AlbumEntity import com.maxrave.simpmusic.data.db.entities.LocalPlaylistEntity import com.maxrave.simpmusic.data.db.entities.PairSongLocalPlaylist import com.maxrave.simpmusic.data.db.entities.SongEntity import com.maxrave.simpmusic.data.model.browse.album.AlbumBrowse import com.maxrave.simpmusic.data.model.browse.album.Track -import com.maxrave.simpmusic.data.repository.MainRepository import com.maxrave.simpmusic.service.test.download.DownloadUtils import com.maxrave.simpmusic.utils.Resource -import dagger.hilt.android.lifecycle.HiltViewModel +import com.maxrave.simpmusic.viewModel.base.BaseViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -32,17 +29,19 @@ import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext +import org.koin.android.annotation.KoinViewModel +import org.koin.core.component.inject import java.time.LocalDateTime -import javax.inject.Inject -@HiltViewModel -class AlbumViewModel @Inject constructor( - private var dataStoreManager: DataStoreManager, - private val mainRepository: MainRepository, +@KoinViewModel +@UnstableApi +class AlbumViewModel( private val application: Application -): AndroidViewModel(application) { - @Inject - lateinit var downloadUtils: DownloadUtils +): BaseViewModel(application) { + + private val downloadUtils: DownloadUtils by inject() + + override val tag: String = "AlbumViewModel" var gradientDrawable: MutableLiveData = MutableLiveData() private var _loading: MutableStateFlow = MutableStateFlow(false) diff --git a/app/src/main/java/com/maxrave/simpmusic/viewModel/ArtistViewModel.kt b/app/src/main/java/com/maxrave/simpmusic/viewModel/ArtistViewModel.kt index aab3f73c..08dfabd9 100644 --- a/app/src/main/java/com/maxrave/simpmusic/viewModel/ArtistViewModel.kt +++ b/app/src/main/java/com/maxrave/simpmusic/viewModel/ArtistViewModel.kt @@ -4,22 +4,19 @@ import android.app.Application import android.graphics.drawable.GradientDrawable import android.util.Log import android.widget.Toast -import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope import com.maxrave.simpmusic.R import com.maxrave.simpmusic.common.DownloadState import com.maxrave.simpmusic.common.SELECTED_LANGUAGE -import com.maxrave.simpmusic.data.dataStore.DataStoreManager import com.maxrave.simpmusic.data.db.entities.ArtistEntity import com.maxrave.simpmusic.data.db.entities.LocalPlaylistEntity import com.maxrave.simpmusic.data.db.entities.PairSongLocalPlaylist import com.maxrave.simpmusic.data.db.entities.SongEntity import com.maxrave.simpmusic.data.model.browse.artist.ArtistBrowse -import com.maxrave.simpmusic.data.repository.MainRepository import com.maxrave.simpmusic.utils.Resource -import dagger.hilt.android.lifecycle.HiltViewModel +import com.maxrave.simpmusic.viewModel.base.BaseViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -27,11 +24,15 @@ import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext +import org.koin.android.annotation.KoinViewModel import java.time.LocalDateTime -import javax.inject.Inject -@HiltViewModel -class ArtistViewModel @Inject constructor(private val application: Application, private val mainRepository: MainRepository, private var dataStoreManager: DataStoreManager): AndroidViewModel(application){ +@KoinViewModel +class ArtistViewModel (private val application: Application): BaseViewModel(application){ + + override val tag: String + get() = "ArtistViewModel" + var gradientDrawable: MutableLiveData = MutableLiveData() private val _artistBrowse: MutableStateFlow?> = MutableStateFlow(null) var artistBrowse: StateFlow?> = _artistBrowse diff --git a/app/src/main/java/com/maxrave/simpmusic/viewModel/DownloadedViewModel.kt b/app/src/main/java/com/maxrave/simpmusic/viewModel/DownloadedViewModel.kt index 2c861c86..5d34ac4d 100644 --- a/app/src/main/java/com/maxrave/simpmusic/viewModel/DownloadedViewModel.kt +++ b/app/src/main/java/com/maxrave/simpmusic/viewModel/DownloadedViewModel.kt @@ -2,7 +2,6 @@ package com.maxrave.simpmusic.viewModel import android.app.Application import android.widget.Toast -import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope @@ -11,19 +10,22 @@ import com.maxrave.simpmusic.common.DownloadState import com.maxrave.simpmusic.data.db.entities.LocalPlaylistEntity import com.maxrave.simpmusic.data.db.entities.PairSongLocalPlaylist import com.maxrave.simpmusic.data.db.entities.SongEntity -import com.maxrave.simpmusic.data.repository.MainRepository -import dagger.hilt.android.lifecycle.HiltViewModel +import com.maxrave.simpmusic.viewModel.base.BaseViewModel import kotlinx.coroutines.launch +import org.koin.android.annotation.KoinViewModel import java.time.LocalDateTime -import javax.inject.Inject -@HiltViewModel -class DownloadedViewModel @Inject constructor(private val application: Application, private val mainRepository: MainRepository): AndroidViewModel(application) { +@KoinViewModel +class DownloadedViewModel(private val application: Application): BaseViewModel(application) { + + override val tag: String + get() = "DownloadedViewModel" + private var _listDownloadedSong: MutableLiveData> = MutableLiveData() val listDownloadedSong: LiveData> get() = _listDownloadedSong - private var _listLocalPlaylist: MutableLiveData> = MutableLiveData() - val localPlaylist: LiveData> = _listLocalPlaylist + private var _localPlaylist: MutableLiveData> = MutableLiveData() + val localPlaylist: LiveData> = _localPlaylist private var _songEntity: MutableLiveData = MutableLiveData() val songEntity: LiveData = _songEntity @@ -63,7 +65,7 @@ class DownloadedViewModel @Inject constructor(private val application: Applicati fun getAllLocalPlaylist() { viewModelScope.launch { mainRepository.getAllLocalPlaylists().collect { values -> - _listLocalPlaylist.postValue(values) + _localPlaylist.postValue(values) } } } diff --git a/app/src/main/java/com/maxrave/simpmusic/viewModel/FavoriteViewModel.kt b/app/src/main/java/com/maxrave/simpmusic/viewModel/FavoriteViewModel.kt index 92479354..f9e25144 100644 --- a/app/src/main/java/com/maxrave/simpmusic/viewModel/FavoriteViewModel.kt +++ b/app/src/main/java/com/maxrave/simpmusic/viewModel/FavoriteViewModel.kt @@ -3,7 +3,6 @@ package com.maxrave.simpmusic.viewModel import android.app.Application import android.util.Log import android.widget.Toast -import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope @@ -14,27 +13,31 @@ import com.maxrave.simpmusic.common.DownloadState import com.maxrave.simpmusic.data.db.entities.LocalPlaylistEntity import com.maxrave.simpmusic.data.db.entities.PairSongLocalPlaylist import com.maxrave.simpmusic.data.db.entities.SongEntity -import com.maxrave.simpmusic.data.repository.MainRepository import com.maxrave.simpmusic.service.test.download.DownloadUtils -import dagger.hilt.android.lifecycle.HiltViewModel +import com.maxrave.simpmusic.viewModel.base.BaseViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch +import org.koin.android.annotation.KoinViewModel +import org.koin.core.component.inject import java.time.LocalDateTime -import javax.inject.Inject -@HiltViewModel -class FavoriteViewModel @Inject constructor(private val application: Application, private val mainRepository: MainRepository): AndroidViewModel(application) { - @Inject - lateinit var downloadUtils: DownloadUtils +@KoinViewModel +@UnstableApi +class FavoriteViewModel(private val application: Application): BaseViewModel(application) { + + override val tag: String + get() = "FavoriteViewModel" + + private val downloadUtils: DownloadUtils by inject() private var _listLikedSong: MutableLiveData> = MutableLiveData() val listLikedSong: LiveData> get() = _listLikedSong - private var _listLocalPlaylist: MutableLiveData> = MutableLiveData() - val localPlaylist: LiveData> = _listLocalPlaylist + private var _localPlaylist: MutableLiveData> = MutableLiveData() + val localPlaylist: LiveData> = _localPlaylist private val _songEntity: MutableLiveData = MutableLiveData() val songEntity: LiveData = _songEntity @@ -56,7 +59,7 @@ class FavoriteViewModel @Inject constructor(private val application: Application fun getAllLocalPlaylist() { viewModelScope.launch { mainRepository.getAllLocalPlaylists().collect { values -> - _listLocalPlaylist.postValue(values) + _localPlaylist.postValue(values) } } } diff --git a/app/src/main/java/com/maxrave/simpmusic/viewModel/FollowedViewModel.kt b/app/src/main/java/com/maxrave/simpmusic/viewModel/FollowedViewModel.kt index da0538fd..e39df830 100644 --- a/app/src/main/java/com/maxrave/simpmusic/viewModel/FollowedViewModel.kt +++ b/app/src/main/java/com/maxrave/simpmusic/viewModel/FollowedViewModel.kt @@ -1,18 +1,20 @@ package com.maxrave.simpmusic.viewModel import android.app.Application -import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope import com.maxrave.simpmusic.data.db.entities.ArtistEntity -import com.maxrave.simpmusic.data.repository.MainRepository -import dagger.hilt.android.lifecycle.HiltViewModel +import com.maxrave.simpmusic.viewModel.base.BaseViewModel import kotlinx.coroutines.launch -import javax.inject.Inject +import org.koin.android.annotation.KoinViewModel + +@KoinViewModel +class FollowedViewModel(application: Application): BaseViewModel(application) { + + override val tag: String + get() = "FollowedViewModel" -@HiltViewModel -class FollowedViewModel @Inject constructor(application: Application, private val mainRepository: MainRepository): AndroidViewModel(application) { private var _listFollowedArtist: MutableLiveData> = MutableLiveData() val listFollowedArtist: LiveData> get() = _listFollowedArtist diff --git a/app/src/main/java/com/maxrave/simpmusic/viewModel/GenreViewModel.kt b/app/src/main/java/com/maxrave/simpmusic/viewModel/GenreViewModel.kt index 3eebfeec..517733ed 100644 --- a/app/src/main/java/com/maxrave/simpmusic/viewModel/GenreViewModel.kt +++ b/app/src/main/java/com/maxrave/simpmusic/viewModel/GenreViewModel.kt @@ -1,23 +1,26 @@ package com.maxrave.simpmusic.viewModel import android.app.Application -import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope import com.maxrave.simpmusic.common.SELECTED_LANGUAGE -import com.maxrave.simpmusic.data.dataStore.DataStoreManager import com.maxrave.simpmusic.data.model.explore.mood.genre.GenreObject -import com.maxrave.simpmusic.data.repository.MainRepository import com.maxrave.simpmusic.utils.Resource +import com.maxrave.simpmusic.viewModel.base.BaseViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext -import javax.inject.Inject +import org.koin.android.annotation.KoinViewModel + +@KoinViewModel +class GenreViewModel(application: Application) : BaseViewModel(application) { + + override val tag: String + get() = "GenreViewModel" -class GenreViewModel @Inject constructor(private val mainRepository: MainRepository, application: Application, private var dataStoreManager: DataStoreManager) : AndroidViewModel(application) { private val _genreObject: MutableLiveData> = MutableLiveData() var genreObject: LiveData> = _genreObject var loading = MutableLiveData() diff --git a/app/src/main/java/com/maxrave/simpmusic/viewModel/HomeViewModel.kt b/app/src/main/java/com/maxrave/simpmusic/viewModel/HomeViewModel.kt index 39d004f9..75e369bf 100644 --- a/app/src/main/java/com/maxrave/simpmusic/viewModel/HomeViewModel.kt +++ b/app/src/main/java/com/maxrave/simpmusic/viewModel/HomeViewModel.kt @@ -3,7 +3,6 @@ package com.maxrave.simpmusic.viewModel import android.app.Application import android.util.Log import android.widget.Toast -import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope import androidx.media3.common.util.UnstableApi @@ -12,7 +11,6 @@ import com.maxrave.simpmusic.R import com.maxrave.simpmusic.common.DownloadState import com.maxrave.simpmusic.common.SELECTED_LANGUAGE import com.maxrave.simpmusic.common.SUPPORTED_LANGUAGE -import com.maxrave.simpmusic.data.dataStore.DataStoreManager import com.maxrave.simpmusic.data.db.entities.LocalPlaylistEntity import com.maxrave.simpmusic.data.db.entities.PairSongLocalPlaylist import com.maxrave.simpmusic.data.db.entities.SongEntity @@ -21,11 +19,10 @@ import com.maxrave.simpmusic.data.model.explore.mood.Mood import com.maxrave.simpmusic.data.model.home.HomeDataCombine import com.maxrave.simpmusic.data.model.home.HomeItem import com.maxrave.simpmusic.data.model.home.chart.Chart -import com.maxrave.simpmusic.data.repository.MainRepository import com.maxrave.simpmusic.extension.toSongEntity import com.maxrave.simpmusic.service.test.download.DownloadUtils import com.maxrave.simpmusic.utils.Resource -import dagger.hilt.android.lifecycle.HiltViewModel +import com.maxrave.simpmusic.viewModel.base.BaseViewModel import kotlinx.coroutines.Job import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow @@ -38,20 +35,20 @@ import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking +import org.koin.android.annotation.KoinViewModel +import org.koin.core.component.inject import java.time.LocalDateTime -import javax.inject.Inject @UnstableApi -@HiltViewModel -class HomeViewModel -@Inject -constructor( - private val mainRepository: MainRepository, - private val application: Application, - private var dataStoreManager: DataStoreManager, -) : AndroidViewModel(application) { - @Inject - lateinit var downloadUtils: DownloadUtils +@KoinViewModel +class HomeViewModel( + private val application: Application +) : BaseViewModel(application) { + + override val tag: String + get() = "HomeViewModel" + + private val downloadUtils: DownloadUtils by inject() private val _homeItemList: MutableStateFlow> = MutableStateFlow(arrayListOf()) @@ -272,16 +269,16 @@ constructor( } } - private var _listLocalPlaylist: MutableStateFlow> = + private var _localPlaylist: MutableStateFlow> = MutableStateFlow( listOf(), ) - val localPlaylist: StateFlow> = _listLocalPlaylist + val localPlaylist: StateFlow> = _localPlaylist fun getAllLocalPlaylist() { viewModelScope.launch { mainRepository.getAllLocalPlaylists().collect { values -> - _listLocalPlaylist.emit(values) + _localPlaylist.emit(values) } } } diff --git a/app/src/main/java/com/maxrave/simpmusic/viewModel/LibraryViewModel.kt b/app/src/main/java/com/maxrave/simpmusic/viewModel/LibraryViewModel.kt index 35cc0242..dc7f63d4 100644 --- a/app/src/main/java/com/maxrave/simpmusic/viewModel/LibraryViewModel.kt +++ b/app/src/main/java/com/maxrave/simpmusic/viewModel/LibraryViewModel.kt @@ -2,7 +2,6 @@ package com.maxrave.simpmusic.viewModel import android.app.Application import android.widget.Toast -import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope @@ -15,23 +14,22 @@ import com.maxrave.simpmusic.data.db.entities.LocalPlaylistEntity import com.maxrave.simpmusic.data.db.entities.PairSongLocalPlaylist import com.maxrave.simpmusic.data.db.entities.PlaylistEntity import com.maxrave.simpmusic.data.db.entities.SongEntity -import com.maxrave.simpmusic.data.repository.MainRepository -import dagger.hilt.android.lifecycle.HiltViewModel +import com.maxrave.simpmusic.viewModel.base.BaseViewModel import kotlinx.coroutines.delay import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking +import org.koin.android.annotation.KoinViewModel import java.time.LocalDateTime -import javax.inject.Inject - -@HiltViewModel -class LibraryViewModel - @Inject - constructor( - private val mainRepository: MainRepository, - private val application: Application, - private val dataStoreManager: DataStoreManager, - ) : AndroidViewModel(application) { + +@KoinViewModel +class LibraryViewModel( + private val application: Application + ) : BaseViewModel(application) { + + override val tag: String + get() = "LibraryViewModel" + private var _listRecentlyAdded: MutableLiveData> = MutableLiveData() val listRecentlyAdded: LiveData> = _listRecentlyAdded diff --git a/app/src/main/java/com/maxrave/simpmusic/viewModel/LocalPlaylistViewModel.kt b/app/src/main/java/com/maxrave/simpmusic/viewModel/LocalPlaylistViewModel.kt index f15cdaff..c288d5c9 100644 --- a/app/src/main/java/com/maxrave/simpmusic/viewModel/LocalPlaylistViewModel.kt +++ b/app/src/main/java/com/maxrave/simpmusic/viewModel/LocalPlaylistViewModel.kt @@ -5,7 +5,6 @@ import android.graphics.drawable.GradientDrawable import android.util.Log import android.widget.Toast import androidx.compose.ui.graphics.Color -import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope import androidx.media3.common.util.UnstableApi @@ -20,12 +19,11 @@ import com.maxrave.simpmusic.data.db.entities.PairSongLocalPlaylist import com.maxrave.simpmusic.data.db.entities.SetVideoIdEntity import com.maxrave.simpmusic.data.db.entities.SongEntity import com.maxrave.simpmusic.data.model.browse.album.Track -import com.maxrave.simpmusic.data.repository.MainRepository import com.maxrave.simpmusic.extension.toListVideoId import com.maxrave.simpmusic.extension.toSongEntity import com.maxrave.simpmusic.service.test.download.DownloadUtils import com.maxrave.simpmusic.utils.Resource -import dagger.hilt.android.lifecycle.HiltViewModel +import com.maxrave.simpmusic.viewModel.base.BaseViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow @@ -38,19 +36,20 @@ import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext +import org.koin.android.annotation.KoinViewModel +import org.koin.core.component.inject import java.time.LocalDateTime -import javax.inject.Inject @UnstableApi -@HiltViewModel -class LocalPlaylistViewModel - @Inject - constructor( - private val mainRepository: MainRepository, +@KoinViewModel +class LocalPlaylistViewModel( private val application: Application, - ) : AndroidViewModel(application) { - @Inject - lateinit var downloadUtils: DownloadUtils + ) : BaseViewModel(application) { + + override val tag: String + get() = "LocalPlaylistViewModel" + + private val downloadUtils: DownloadUtils by inject() val id: MutableLiveData = MutableLiveData() diff --git a/app/src/main/java/com/maxrave/simpmusic/viewModel/LogInViewModel.kt b/app/src/main/java/com/maxrave/simpmusic/viewModel/LogInViewModel.kt index 001512f8..72692d71 100644 --- a/app/src/main/java/com/maxrave/simpmusic/viewModel/LogInViewModel.kt +++ b/app/src/main/java/com/maxrave/simpmusic/viewModel/LogInViewModel.kt @@ -1,18 +1,21 @@ package com.maxrave.simpmusic.viewModel +import android.app.Application import android.util.Log import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.maxrave.kotlinytmusicscraper.YouTube -import com.maxrave.simpmusic.data.dataStore.DataStoreManager -import dagger.hilt.android.lifecycle.HiltViewModel +import com.maxrave.simpmusic.viewModel.base.BaseViewModel import kotlinx.coroutines.launch -import javax.inject.Inject +import org.koin.android.annotation.KoinViewModel -@HiltViewModel -class LogInViewModel @Inject constructor(private val dataStore: DataStoreManager) : ViewModel() { +@KoinViewModel +class LogInViewModel(private val application: Application) : BaseViewModel(application) { + + override val tag: String + get() = "LogInViewModel" + private val _status: MutableLiveData = MutableLiveData(false) var status: LiveData = _status @@ -22,8 +25,8 @@ class LogInViewModel @Inject constructor(private val dataStore: DataStoreManager fun saveCookie(cookie: String) { viewModelScope.launch { Log.d("LogInViewModel", "saveCookie: $cookie") - dataStore.setCookie(cookie) - dataStore.setLoggedIn(true) + dataStoreManager.setCookie(cookie) + dataStoreManager.setLoggedIn(true) YouTube.cookie = cookie _status.postValue(true) } @@ -37,7 +40,7 @@ class LogInViewModel @Inject constructor(private val dataStore: DataStoreManager val (key, value) = it.split("=") key to value }.let { - dataStore.setSpdc(it["sp_dc"] ?: "") + dataStoreManager.setSpdc(it["sp_dc"] ?: "") _spotifyStatus.postValue(true) } } diff --git a/app/src/main/java/com/maxrave/simpmusic/viewModel/MoodViewModel.kt b/app/src/main/java/com/maxrave/simpmusic/viewModel/MoodViewModel.kt index 6852521b..6d941c6b 100644 --- a/app/src/main/java/com/maxrave/simpmusic/viewModel/MoodViewModel.kt +++ b/app/src/main/java/com/maxrave/simpmusic/viewModel/MoodViewModel.kt @@ -2,14 +2,11 @@ package com.maxrave.simpmusic.viewModel import android.app.Application import android.util.Log -import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope import com.maxrave.simpmusic.common.SELECTED_LANGUAGE -import com.maxrave.simpmusic.data.dataStore.DataStoreManager import com.maxrave.simpmusic.data.model.explore.mood.moodmoments.MoodsMomentObject -import com.maxrave.simpmusic.data.repository.MainRepository import com.maxrave.simpmusic.utils.Resource -import dagger.hilt.android.lifecycle.HiltViewModel +import com.maxrave.simpmusic.viewModel.base.BaseViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -17,14 +14,16 @@ import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext -import javax.inject.Inject - -@HiltViewModel -class MoodViewModel @Inject constructor( - private val mainRepository: MainRepository, - application: Application, - private var dataStoreManager: DataStoreManager -) : AndroidViewModel(application) { +import org.koin.android.annotation.KoinViewModel + +@KoinViewModel +class MoodViewModel( + application: Application +) : BaseViewModel(application) { + + override val tag: String + get() = "MoodViewModel" + private val _moodsMomentObject: MutableStateFlow = MutableStateFlow(null) var moodsMomentObject: StateFlow = _moodsMomentObject var loading = MutableStateFlow(false) diff --git a/app/src/main/java/com/maxrave/simpmusic/viewModel/MoreAlbumsViewModel.kt b/app/src/main/java/com/maxrave/simpmusic/viewModel/MoreAlbumsViewModel.kt index b46ba8e7..f26bb11e 100644 --- a/app/src/main/java/com/maxrave/simpmusic/viewModel/MoreAlbumsViewModel.kt +++ b/app/src/main/java/com/maxrave/simpmusic/viewModel/MoreAlbumsViewModel.kt @@ -1,18 +1,20 @@ package com.maxrave.simpmusic.viewModel import android.app.Application -import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope import com.maxrave.kotlinytmusicscraper.pages.BrowseResult -import com.maxrave.simpmusic.data.repository.MainRepository -import dagger.hilt.android.lifecycle.HiltViewModel +import com.maxrave.simpmusic.viewModel.base.BaseViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch -import javax.inject.Inject +import org.koin.android.annotation.KoinViewModel + +@KoinViewModel +class MoreAlbumsViewModel(application: Application) : BaseViewModel(application) { + + override val tag: String + get() = "MoreAlbumsViewModel" -@HiltViewModel -class MoreAlbumsViewModel @Inject constructor(application: Application, private val mainRepository: MainRepository): AndroidViewModel(application) { private var _browseResult: MutableStateFlow = MutableStateFlow(null) val browseResult: StateFlow = _browseResult diff --git a/app/src/main/java/com/maxrave/simpmusic/viewModel/MostPlayedViewModel.kt b/app/src/main/java/com/maxrave/simpmusic/viewModel/MostPlayedViewModel.kt index 69ee8d09..4e6ec23e 100644 --- a/app/src/main/java/com/maxrave/simpmusic/viewModel/MostPlayedViewModel.kt +++ b/app/src/main/java/com/maxrave/simpmusic/viewModel/MostPlayedViewModel.kt @@ -5,7 +5,6 @@ import android.util.Log import android.widget.Toast import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import androidx.media3.common.util.UnstableApi import androidx.media3.exoplayer.offline.Download @@ -14,21 +13,25 @@ import com.maxrave.simpmusic.common.DownloadState import com.maxrave.simpmusic.data.db.entities.LocalPlaylistEntity import com.maxrave.simpmusic.data.db.entities.PairSongLocalPlaylist import com.maxrave.simpmusic.data.db.entities.SongEntity -import com.maxrave.simpmusic.data.repository.MainRepository import com.maxrave.simpmusic.service.test.download.DownloadUtils -import dagger.hilt.android.lifecycle.HiltViewModel +import com.maxrave.simpmusic.viewModel.base.BaseViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch +import org.koin.android.annotation.KoinViewModel +import org.koin.core.component.inject import java.time.LocalDateTime -import javax.inject.Inject -@HiltViewModel -class MostPlayedViewModel @Inject constructor(private val mainRepository: MainRepository, private val application: Application): ViewModel() { - @Inject - lateinit var downloadUtils: DownloadUtils +@KoinViewModel +@UnstableApi +class MostPlayedViewModel(private val application: Application): BaseViewModel(application) { + + override val tag: String + get() = "MostPlayedViewModel" + + private val downloadUtils: DownloadUtils by inject() private var _listMostPlayedSong: MutableLiveData> = MutableLiveData() val listMostPlayedSong: LiveData> get() = _listMostPlayedSong diff --git a/app/src/main/java/com/maxrave/simpmusic/viewModel/MusixmatchViewModel.kt b/app/src/main/java/com/maxrave/simpmusic/viewModel/MusixmatchViewModel.kt index 2973b157..5fe3e4d5 100644 --- a/app/src/main/java/com/maxrave/simpmusic/viewModel/MusixmatchViewModel.kt +++ b/app/src/main/java/com/maxrave/simpmusic/viewModel/MusixmatchViewModel.kt @@ -1,36 +1,26 @@ package com.maxrave.simpmusic.viewModel -import androidx.lifecycle.ViewModel +import android.app.Application import androidx.lifecycle.viewModelScope import com.maxrave.kotlinytmusicscraper.YouTube import com.maxrave.kotlinytmusicscraper.models.musixmatch.MusixmatchCredential -import com.maxrave.simpmusic.data.dataStore.DataStoreManager -import com.maxrave.simpmusic.data.repository.MainRepository -import dagger.hilt.android.lifecycle.HiltViewModel +import com.maxrave.simpmusic.viewModel.base.BaseViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import javax.inject.Inject +import org.koin.android.annotation.KoinViewModel -@HiltViewModel -class MusixmatchViewModel @Inject constructor(private val dataStore: DataStoreManager, private val mainRepository: MainRepository) : ViewModel() { -// private val _status: MutableLiveData = MutableLiveData(false) -// var status: LiveData = _status -// -// fun saveCookie(cookie: String) { -// viewModelScope.launch { -// Log.d("LogInViewModel", "saveCookie: $cookie") -// dataStore.setCookie(cookie) -// dataStore.setLoggedIn(true) -// YouTube.cookie = cookie -// _status.postValue(true) -// } -// } +@KoinViewModel +class MusixmatchViewModel(application: Application) : BaseViewModel(application) { + + override val tag: String + get() = "MusixmatchViewModel" + var loading: MutableStateFlow = MutableStateFlow(null) private var _data: MutableStateFlow = MutableStateFlow(null) val data: MutableStateFlow = _data - fun loggin(email: String, password: String) { + fun login(email: String, password: String) { loading.value = true viewModelScope.launch { mainRepository.loginToMusixMatch(email, password).collect { @@ -40,8 +30,8 @@ class MusixmatchViewModel @Inject constructor(private val dataStore: DataStoreMa } fun saveCookie(cookie: String) { viewModelScope.launch { - dataStore.setMusixmatchCookie(cookie) - dataStore.setMusixmatchLoggedIn(true) + dataStoreManager.setMusixmatchCookie(cookie) + dataStoreManager.setMusixmatchLoggedIn(true) YouTube.musixMatchCookie = cookie withContext(Dispatchers.Main) { loading.value = false diff --git a/app/src/main/java/com/maxrave/simpmusic/viewModel/NotificationViewModel.kt b/app/src/main/java/com/maxrave/simpmusic/viewModel/NotificationViewModel.kt index df31a247..32709719 100644 --- a/app/src/main/java/com/maxrave/simpmusic/viewModel/NotificationViewModel.kt +++ b/app/src/main/java/com/maxrave/simpmusic/viewModel/NotificationViewModel.kt @@ -1,23 +1,20 @@ package com.maxrave.simpmusic.viewModel import android.app.Application -import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope import com.maxrave.simpmusic.data.db.entities.NotificationEntity -import com.maxrave.simpmusic.data.repository.MainRepository -import dagger.hilt.android.lifecycle.HiltViewModel +import com.maxrave.simpmusic.viewModel.base.BaseViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch -import javax.inject.Inject +import org.koin.android.annotation.KoinViewModel + +@KoinViewModel +class NotificationViewModel(application: Application) : BaseViewModel(application) { + + override val tag: String + get() = "NotificationViewModel" -@HiltViewModel -class NotificationViewModel - @Inject - constructor( - private val application: Application, - mainRepository: MainRepository, - ) : AndroidViewModel(application) { private var _listNotification: MutableStateFlow?> = MutableStateFlow(null) val listNotification: StateFlow?> = _listNotification diff --git a/app/src/main/java/com/maxrave/simpmusic/viewModel/PlaylistViewModel.kt b/app/src/main/java/com/maxrave/simpmusic/viewModel/PlaylistViewModel.kt index 06360a5d..e6c3dacd 100644 --- a/app/src/main/java/com/maxrave/simpmusic/viewModel/PlaylistViewModel.kt +++ b/app/src/main/java/com/maxrave/simpmusic/viewModel/PlaylistViewModel.kt @@ -4,7 +4,6 @@ import android.app.Application import android.graphics.drawable.GradientDrawable import android.util.Log import android.widget.Toast -import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope @@ -13,20 +12,18 @@ import androidx.media3.exoplayer.offline.Download import com.maxrave.simpmusic.R import com.maxrave.simpmusic.common.DownloadState import com.maxrave.simpmusic.common.SELECTED_LANGUAGE -import com.maxrave.simpmusic.data.dataStore.DataStoreManager import com.maxrave.simpmusic.data.db.entities.LocalPlaylistEntity import com.maxrave.simpmusic.data.db.entities.PairSongLocalPlaylist import com.maxrave.simpmusic.data.db.entities.PlaylistEntity import com.maxrave.simpmusic.data.db.entities.SongEntity import com.maxrave.simpmusic.data.model.browse.album.Track import com.maxrave.simpmusic.data.model.browse.playlist.PlaylistBrowse -import com.maxrave.simpmusic.data.repository.MainRepository import com.maxrave.simpmusic.extension.toPlaylistEntity import com.maxrave.simpmusic.extension.toSongEntity import com.maxrave.simpmusic.extension.toVideoIdList import com.maxrave.simpmusic.service.test.download.DownloadUtils import com.maxrave.simpmusic.utils.Resource -import dagger.hilt.android.lifecycle.HiltViewModel +import com.maxrave.simpmusic.viewModel.base.BaseViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.delay @@ -41,130 +38,104 @@ import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext +import org.koin.android.annotation.KoinViewModel +import org.koin.core.component.inject import java.time.LocalDateTime -import javax.inject.Inject -@HiltViewModel -class PlaylistViewModel - @Inject - constructor( - private val mainRepository: MainRepository, - private val application: Application, - private var dataStoreManager: DataStoreManager, - ) : AndroidViewModel(application) { - @Inject - @UnstableApi - lateinit var downloadUtils: DownloadUtils +@KoinViewModel +@UnstableApi +class PlaylistViewModel( + private val application: Application, +) : BaseViewModel(application) { + override val tag: String + get() = "PlaylistViewModel" - private var _gradientDrawable: MutableStateFlow = MutableStateFlow(null) - var gradientDrawable: StateFlow = _gradientDrawable + val downloadUtils: DownloadUtils by inject() - private var _uiState = MutableStateFlow(PlaylistUIState.Loading) - val uiState: StateFlow = _uiState + private var _gradientDrawable: MutableStateFlow = MutableStateFlow(null) + var gradientDrawable: StateFlow = _gradientDrawable - private val _playlistBrowse: MutableStateFlow = MutableStateFlow(null) - var playlistBrowse: StateFlow = _playlistBrowse + private var _uiState = MutableStateFlow(PlaylistUIState.Loading) + val uiState: StateFlow = _uiState - private val _id: MutableLiveData = MutableLiveData() - var id: LiveData = _id + private val _playlistBrowse: MutableStateFlow = MutableStateFlow(null) + var playlistBrowse: StateFlow = _playlistBrowse - private val _isRadio: MutableStateFlow = MutableStateFlow(false) - var isRadio: StateFlow = _isRadio + private val _id: MutableLiveData = MutableLiveData() + var id: LiveData = _id - private var _radioContinuation: MutableStateFlow?> = MutableStateFlow(null) - var radioContinuation: StateFlow?> = _radioContinuation + private val _isRadio: MutableStateFlow = MutableStateFlow(false) + var isRadio: StateFlow = _isRadio - private var _playlistEntity: MutableStateFlow = MutableStateFlow(null) - var playlistEntity: StateFlow = _playlistEntity + private var _radioContinuation: MutableStateFlow?> = MutableStateFlow(null) + var radioContinuation: StateFlow?> = _radioContinuation - private var _liked: MutableStateFlow = MutableStateFlow(false) - var liked: MutableStateFlow = _liked + private var _playlistEntity: MutableStateFlow = MutableStateFlow(null) + var playlistEntity: StateFlow = _playlistEntity - private var _songEntity: MutableLiveData = MutableLiveData() - val songEntity: LiveData = _songEntity - private var _listLocalPlaylist: MutableLiveData> = MutableLiveData() - val listLocalPlaylist: LiveData> = _listLocalPlaylist + private var _liked: MutableStateFlow = MutableStateFlow(false) + var liked: MutableStateFlow = _liked - private var regionCode: String? = null - private var language: String? = null + private var _songEntity: MutableLiveData = MutableLiveData() + val songEntity: LiveData = _songEntity + private var _listLocalPlaylist: MutableLiveData> = MutableLiveData() + val listLocalPlaylist: LiveData> = _listLocalPlaylist - private var collectDownloadedJob: Job? = null - private var _downloadedList = MutableStateFlow>(emptyList()) - val downloadedList: StateFlow> = _downloadedList + private var regionCode: String? = null + private var language: String? = null - init { - regionCode = runBlocking { dataStoreManager.location.first() } - language = runBlocking { dataStoreManager.getString(SELECTED_LANGUAGE).first() } - } + private var collectDownloadedJob: Job? = null + private var _downloadedList = MutableStateFlow>(emptyList()) + val downloadedList: StateFlow> = _downloadedList - fun updateId(id: String) { - _id.value = id - } + init { + regionCode = runBlocking { dataStoreManager.location.first() } + language = runBlocking { dataStoreManager.getString(SELECTED_LANGUAGE).first() } + } - fun updateIsRadio(isRadio: Boolean) { - _isRadio.value = isRadio - } + fun updateId(id: String) { + _id.value = id + } + + fun updateIsRadio(isRadio: Boolean) { + _isRadio.value = isRadio + } - fun browsePlaylist(id: String) { - _uiState.value = PlaylistUIState.Loading - viewModelScope.launch { + fun browsePlaylist(id: String) { + _uiState.value = PlaylistUIState.Loading + viewModelScope.launch { // mainRepository.browsePlaylist(id, regionCode!!, SUPPORTED_LANGUAGE.serverCodes[SUPPORTED_LANGUAGE.codes.indexOf(language!!)]).collect{ values -> // _playlistBrowse.value = values // } - mainRepository.getPlaylistData(id).collect { - when(it) { - is Resource.Success -> { - _playlistBrowse.value = it.data - it.data?.let { playlistEntity -> - getPlaylist(id, playlistEntity, false) - } - } - is Resource.Error -> { - Log.w("PlaylistViewModel", "Error: ${it.message}") - _playlistBrowse.value = null - getPlaylist(id, null, false, it.message) + mainRepository.getPlaylistData(id).collect { + when (it) { + is Resource.Success -> { + _playlistBrowse.value = it.data + it.data?.let { playlistEntity -> + getPlaylist(id, playlistEntity, false) } } + is Resource.Error -> { + Log.w("PlaylistViewModel", "Error: ${it.message}") + _playlistBrowse.value = null + getPlaylist(id, null, false, it.message) + } } } } + } - fun getRadio( - radioId: String, - videoId: String? = null, - channelId: String? = null, - ) { - _uiState.value = PlaylistUIState.Loading - viewModelScope.launch { - if (videoId != null) { - mainRepository.getSongById(videoId).collectLatest { song -> - if (song != null) { - mainRepository.getRadio(radioId, song).collect { - when (it) { - is Resource.Success -> { - _playlistBrowse.value = it.data?.first - it.data?.first?.id?.let { id -> - _radioContinuation.value = Pair(id, it.data.second) - } - it.data?.first?.let { playlistEntity -> - getPlaylist(radioId, playlistEntity, true) - } - } - is Resource.Error -> { - Log.w("PlaylistViewModel", "Error: ${it.message}") - _playlistBrowse.value = null - _radioContinuation.value = null - withContext(Dispatchers.Main) { - _uiState.value = PlaylistUIState.Error(it.message) - } - } - } - } - } - } - } else if (channelId != null) { - mainRepository.getArtistById(channelId).collectLatest { artist -> - mainRepository.getRadio(radioId = radioId, artist = artist).collect { + fun getRadio( + radioId: String, + videoId: String? = null, + channelId: String? = null, + ) { + _uiState.value = PlaylistUIState.Loading + viewModelScope.launch { + if (videoId != null) { + mainRepository.getSongById(videoId).collectLatest { song -> + if (song != null) { + mainRepository.getRadio(radioId, song).collect { when (it) { is Resource.Success -> { _playlistBrowse.value = it.data?.first @@ -178,6 +149,7 @@ class PlaylistViewModel is Resource.Error -> { Log.w("PlaylistViewModel", "Error: ${it.message}") _playlistBrowse.value = null + _radioContinuation.value = null withContext(Dispatchers.Main) { _uiState.value = PlaylistUIState.Error(it.message) } @@ -186,8 +158,9 @@ class PlaylistViewModel } } } - else { - mainRepository.getRDATRadioData(radioId).collect { + } else if (channelId != null) { + mainRepository.getArtistById(channelId).collectLatest { artist -> + mainRepository.getRadio(radioId = radioId, artist = artist).collect { when (it) { is Resource.Success -> { _playlistBrowse.value = it.data?.first @@ -208,171 +181,196 @@ class PlaylistViewModel } } } + } else { + mainRepository.getRDATRadioData(radioId).collect { + when (it) { + is Resource.Success -> { + _playlistBrowse.value = it.data?.first + it.data?.first?.id?.let { id -> + _radioContinuation.value = Pair(id, it.data.second) + } + it.data?.first?.let { playlistEntity -> + getPlaylist(radioId, playlistEntity, true) + } + } + is Resource.Error -> { + Log.w("PlaylistViewModel", "Error: ${it.message}") + _playlistBrowse.value = null + withContext(Dispatchers.Main) { + _uiState.value = PlaylistUIState.Error(it.message) + } + } + } + } } } + } - fun insertPlaylist(playlistEntity: PlaylistEntity) { - viewModelScope.launch { - mainRepository.insertPlaylist(playlistEntity) - mainRepository.getPlaylist(playlistEntity.id).collect { values -> - _playlistEntity.value = values - if (values != null) { - _liked.value = values.liked - } + fun insertPlaylist(playlistEntity: PlaylistEntity) { + viewModelScope.launch { + mainRepository.insertPlaylist(playlistEntity) + mainRepository.getPlaylist(playlistEntity.id).collect { values -> + _playlistEntity.value = values + if (values != null) { + _liked.value = values.liked } } } + } - fun getPlaylist(id: String, playlistBrowse: PlaylistBrowse?, isRadio: Boolean?, message: String? = null) { - viewModelScope.launch { - mainRepository.updatePlaylistInLibrary( - LocalDateTime.now(), - id - ) - mainRepository.getPlaylist(id).collect { values -> - if (values != null) { - _playlistEntity.value = values - _liked.value = values.liked - playlistDownloadState.value = values.downloadState - val list = values.tracks - var count = 0 - list?.forEach { track -> - mainRepository.getSongById(track).singleOrNull()?.let { song -> - if (song.downloadState == DownloadState.STATE_DOWNLOADED) { - count++ - } + fun getPlaylist( + id: String, + playlistBrowse: PlaylistBrowse?, + isRadio: Boolean?, + message: String? = null, + ) { + viewModelScope.launch { + mainRepository.updatePlaylistInLibrary( + LocalDateTime.now(), + id, + ) + mainRepository.getPlaylist(id).collect { values -> + if (values != null) { + _playlistEntity.value = values + _liked.value = values.liked + playlistDownloadState.value = values.downloadState + val list = values.tracks + var count = 0 + list?.forEach { track -> + mainRepository.getSongById(track).singleOrNull()?.let { song -> + if (song.downloadState == DownloadState.STATE_DOWNLOADED) { + count++ } } - if (count == list?.size && count > 0) { - updatePlaylistDownloadState(id, DownloadState.STATE_DOWNLOADED) - } else { - updatePlaylistDownloadState(id, DownloadState.STATE_NOT_DOWNLOADED) - } - getListTrack(list) - withContext(Dispatchers.Main) { - _uiState.value = PlaylistUIState.Success - } } - else if (isRadio != null && playlistBrowse != null) { - _liked.value = false - playlistDownloadState.value = DownloadState.STATE_NOT_DOWNLOADED - _playlistEntity.value = null - when (isRadio) { - true -> { - insertRadioPlaylist(playlistBrowse.toPlaylistEntity()) - } - false -> { - insertPlaylist(playlistBrowse.toPlaylistEntity()) - } + if (count == list?.size && count > 0) { + updatePlaylistDownloadState(id, DownloadState.STATE_DOWNLOADED) + } else { + updatePlaylistDownloadState(id, DownloadState.STATE_NOT_DOWNLOADED) + } + getListTrack(list) + withContext(Dispatchers.Main) { + _uiState.value = PlaylistUIState.Success + } + } else if (isRadio != null && playlistBrowse != null) { + _liked.value = false + playlistDownloadState.value = DownloadState.STATE_NOT_DOWNLOADED + _playlistEntity.value = null + when (isRadio) { + true -> { + insertRadioPlaylist(playlistBrowse.toPlaylistEntity()) } - withContext(Dispatchers.Main) { - _uiState.value = PlaylistUIState.Success + false -> { + insertPlaylist(playlistBrowse.toPlaylistEntity()) } } - else { - withContext(Dispatchers.Main) { - _uiState.value = PlaylistUIState.Error(message) - } + withContext(Dispatchers.Main) { + _uiState.value = PlaylistUIState.Success + } + } else { + withContext(Dispatchers.Main) { + _uiState.value = PlaylistUIState.Error(message) } } } } + } - private var _listTrack: MutableStateFlow> = MutableStateFlow(emptyList()) - var listTrack: StateFlow> = _listTrack + private var _listTrack: MutableStateFlow> = MutableStateFlow(emptyList()) + var listTrack: StateFlow> = _listTrack - fun getListTrack(tracks: List?) { - viewModelScope.launch { - val listFlow = mutableListOf>>() - tracks?.chunked(500)?.forEachIndexed { index, videoIds -> - listFlow.add(mainRepository.getSongsByListVideoId(videoIds).stateIn(viewModelScope)) - } - combine( - listFlow - ) { list -> - list.map { it }.flatten() - }.collectLatest { values -> - val sortedList = values.sortedBy { + fun getListTrack(tracks: List?) { + viewModelScope.launch { + val listFlow = mutableListOf>>() + tracks?.chunked(500)?.forEachIndexed { index, videoIds -> + listFlow.add(mainRepository.getSongsByListVideoId(videoIds).stateIn(viewModelScope)) + } + combine( + listFlow, + ) { list -> + list.map { it }.flatten() + }.collectLatest { values -> + val sortedList = + values.sortedBy { tracks?.indexOf(it.videoId) } - _listTrack.value = sortedList - collectDownloadedJob?.cancel() - if (values.isNotEmpty()) { - collectDownloadedJob = launch { + _listTrack.value = sortedList + collectDownloadedJob?.cancel() + if (values.isNotEmpty()) { + collectDownloadedJob = + launch { mainRepository.getDownloadedVideoIdListFromListVideoIdAsFlow(values.toVideoIdList()).collectLatest { _downloadedList.value = it } } - } - else { - _downloadedList.value = emptyList() - } + } else { + _downloadedList.value = emptyList() } } } + } - fun checkAllSongDownloaded(list: ArrayList) { - viewModelScope.launch { - var count = 0 - list.forEach { track -> - mainRepository.getSongById(track.videoId).collect { song -> - if (song != null) { - if (song.downloadState == DownloadState.STATE_DOWNLOADED) { - count++ - } + fun checkAllSongDownloaded(list: ArrayList) { + viewModelScope.launch { + var count = 0 + list.forEach { track -> + mainRepository.getSongById(track.videoId).collect { song -> + if (song != null) { + if (song.downloadState == DownloadState.STATE_DOWNLOADED) { + count++ } } } - if (count == list.size && count > 0) { - id.value?.let { updatePlaylistDownloadState(it, DownloadState.STATE_DOWNLOADED) } - } - id.value?.let { - mainRepository.getPlaylist(it).collect { album -> - if (album != null) { - if (album.downloadState == DownloadState.STATE_DOWNLOADED && count < list.size) { - updatePlaylistDownloadState(it, DownloadState.STATE_NOT_DOWNLOADED) - _playlistEntity.value = album - } - else if (playlistEntity.value?.downloadState != album.downloadState) { - _playlistEntity.value = album - } + } + if (count == list.size && count > 0) { + id.value?.let { updatePlaylistDownloadState(it, DownloadState.STATE_DOWNLOADED) } + } + id.value?.let { + mainRepository.getPlaylist(it).collect { album -> + if (album != null) { + if (album.downloadState == DownloadState.STATE_DOWNLOADED && count < list.size) { + updatePlaylistDownloadState(it, DownloadState.STATE_NOT_DOWNLOADED) + _playlistEntity.value = album + } else if (playlistEntity.value?.downloadState != album.downloadState) { + _playlistEntity.value = album } } } } } + } - fun updatePlaylistLiked( - liked: Boolean, - id: String, - ) { - viewModelScope.launch { - val tempLiked = if (liked) 1 else 0 - mainRepository.updatePlaylistLiked(id, tempLiked) - mainRepository.getPlaylist(id).collect { values -> - _playlistEntity.value = values - if (values != null) { - _liked.value = values.liked - } + fun updatePlaylistLiked( + liked: Boolean, + id: String, + ) { + viewModelScope.launch { + val tempLiked = if (liked) 1 else 0 + mainRepository.updatePlaylistLiked(id, tempLiked) + mainRepository.getPlaylist(id).collect { values -> + _playlistEntity.value = values + if (values != null) { + _liked.value = values.liked } } } + } - val listJob: MutableStateFlow> = MutableStateFlow(arrayListOf()) - val playlistDownloadState: MutableStateFlow = MutableStateFlow(DownloadState.STATE_NOT_DOWNLOADED) - - fun updatePlaylistDownloadState( - id: String, - state: Int, - ) { - viewModelScope.launch { - mainRepository.getPlaylist(id).collect { playlist -> - _playlistEntity.value = playlist - mainRepository.updatePlaylistDownloadState(id, state) - playlistDownloadState.value = state - } + val listJob: MutableStateFlow> = MutableStateFlow(arrayListOf()) + val playlistDownloadState: MutableStateFlow = MutableStateFlow(DownloadState.STATE_NOT_DOWNLOADED) + + fun updatePlaylistDownloadState( + id: String, + state: Int, + ) { + viewModelScope.launch { + mainRepository.getPlaylist(id).collect { playlist -> + _playlistEntity.value = playlist + mainRepository.updatePlaylistDownloadState(id, state) + playlistDownloadState.value = state } } + } // fun downloading() { // _prevPlaylistDownloading.value = true // } @@ -408,226 +406,226 @@ class PlaylistViewModel // } // } - @UnstableApi - fun getDownloadStateFromService(videoId: String) { - viewModelScope.launch { - } + @UnstableApi + fun getDownloadStateFromService(videoId: String) { + viewModelScope.launch { } + } - fun clearPlaylistBrowse() { - _playlistBrowse.value = null - _gradientDrawable.value = null - _radioContinuation.value = null - } + fun clearPlaylistBrowse() { + _playlistBrowse.value = null + _gradientDrawable.value = null + _radioContinuation.value = null + } - fun clearPlaylistEntity() { - _listTrack.value = emptyList() - _playlistEntity.value = null - } + fun clearPlaylistEntity() { + _listTrack.value = emptyList() + _playlistEntity.value = null + } - fun getLocation() { - regionCode = runBlocking { dataStoreManager.location.first() } - language = runBlocking { dataStoreManager.getString(SELECTED_LANGUAGE).first() } - } + fun getLocation() { + regionCode = runBlocking { dataStoreManager.location.first() } + language = runBlocking { dataStoreManager.getString(SELECTED_LANGUAGE).first() } + } - fun getSongEntity(song: SongEntity) { - viewModelScope.launch { - mainRepository.insertSong(song).first().let { - println("Insert song $it") - } - mainRepository.getSongById(song.videoId).collect { values -> - _songEntity.value = values - } + fun getSongEntity(song: SongEntity) { + viewModelScope.launch { + mainRepository.insertSong(song).first().let { + println("Insert song $it") + } + mainRepository.getSongById(song.videoId).collect { values -> + _songEntity.value = values } } + } - fun updateLikeStatus( - videoId: String, - likeStatus: Int, - ) { - viewModelScope.launch { - mainRepository.updateLikeStatus(likeStatus = likeStatus, videoId = videoId) - } + fun updateLikeStatus( + videoId: String, + likeStatus: Int, + ) { + viewModelScope.launch { + mainRepository.updateLikeStatus(likeStatus = likeStatus, videoId = videoId) } + } - fun getLocalPlaylist() { - viewModelScope.launch { - mainRepository.getAllLocalPlaylists().collect { values -> - _listLocalPlaylist.postValue(values) - } + fun getLocalPlaylist() { + viewModelScope.launch { + mainRepository.getAllLocalPlaylists().collect { values -> + _listLocalPlaylist.postValue(values) } } + } - fun updateLocalPlaylistTracks( - list: List, - id: Long, - ) { - viewModelScope.launch { - mainRepository.getSongsByListVideoId(list).collect { values -> - var count = 0 - values.forEach { song -> - if (song.downloadState == DownloadState.STATE_DOWNLOADED) { - count++ - } - } - mainRepository.updateLocalPlaylistTracks(list, id) - Toast.makeText(getApplication(), application.getString(R.string.added_to_playlist), Toast.LENGTH_SHORT).show() - if (count == values.size && count > 0) { - mainRepository.updateLocalPlaylistDownloadState(DownloadState.STATE_DOWNLOADED, id) - } else { - mainRepository.updateLocalPlaylistDownloadState(DownloadState.STATE_NOT_DOWNLOADED, id) + fun updateLocalPlaylistTracks( + list: List, + id: Long, + ) { + viewModelScope.launch { + mainRepository.getSongsByListVideoId(list).collect { values -> + var count = 0 + values.forEach { song -> + if (song.downloadState == DownloadState.STATE_DOWNLOADED) { + count++ } } + mainRepository.updateLocalPlaylistTracks(list, id) + Toast.makeText(getApplication(), application.getString(R.string.added_to_playlist), Toast.LENGTH_SHORT).show() + if (count == values.size && count > 0) { + mainRepository.updateLocalPlaylistDownloadState(DownloadState.STATE_DOWNLOADED, id) + } else { + mainRepository.updateLocalPlaylistDownloadState(DownloadState.STATE_NOT_DOWNLOADED, id) + } } } + } - fun updateDownloadState( - videoId: String, - state: Int, - ) { - viewModelScope.launch { - mainRepository.updateDownloadState(videoId, state) - } + fun updateDownloadState( + videoId: String, + state: Int, + ) { + viewModelScope.launch { + mainRepository.updateDownloadState(videoId, state) } + } - fun insertSong(songEntity: SongEntity) { - viewModelScope.launch { - mainRepository.insertSong(songEntity).collect { - println("Insert Song $it") - } + fun insertSong(songEntity: SongEntity) { + viewModelScope.launch { + mainRepository.insertSong(songEntity).collect { + println("Insert Song $it") } } + } - @UnstableApi - fun downloadFullPlaylistState(id: String) { - viewModelScope.launch { - downloadUtils.downloads.collect { download -> - playlistDownloadState.value = - if (listJob.value.all { download[it.videoId]?.state == Download.STATE_COMPLETED }) { - mainRepository.updatePlaylistDownloadState( - id, - DownloadState.STATE_DOWNLOADED, - ) - DownloadState.STATE_DOWNLOADED - } else if (listJob.value.all { - download[it.videoId]?.state == Download.STATE_QUEUED || - download[it.videoId]?.state == Download.STATE_DOWNLOADING || - download[it.videoId]?.state == Download.STATE_COMPLETED - } - ) { - mainRepository.updatePlaylistDownloadState( - id, - DownloadState.STATE_DOWNLOADING, - ) - DownloadState.STATE_DOWNLOADING - } else { - mainRepository.updatePlaylistDownloadState(id, DownloadState.STATE_NOT_DOWNLOADED) - DownloadState.STATE_NOT_DOWNLOADED + @UnstableApi + fun downloadFullPlaylistState(id: String) { + viewModelScope.launch { + downloadUtils.downloads.collect { download -> + playlistDownloadState.value = + if (listJob.value.all { download[it.videoId]?.state == Download.STATE_COMPLETED }) { + mainRepository.updatePlaylistDownloadState( + id, + DownloadState.STATE_DOWNLOADED, + ) + DownloadState.STATE_DOWNLOADED + } else if (listJob.value.all { + download[it.videoId]?.state == Download.STATE_QUEUED || + download[it.videoId]?.state == Download.STATE_DOWNLOADING || + download[it.videoId]?.state == Download.STATE_COMPLETED } - } + ) { + mainRepository.updatePlaylistDownloadState( + id, + DownloadState.STATE_DOWNLOADING, + ) + DownloadState.STATE_DOWNLOADING + } else { + mainRepository.updatePlaylistDownloadState(id, DownloadState.STATE_NOT_DOWNLOADED) + DownloadState.STATE_NOT_DOWNLOADED + } } } + } - fun insertLocalPlaylist( - localPlaylistEntity: LocalPlaylistEntity, - listTrack: List, - ) { - viewModelScope.launch { - mainRepository.insertLocalPlaylist(localPlaylistEntity) - delay(500) - mainRepository.getLocalPlaylistByYoutubePlaylistId(localPlaylistEntity.youtubePlaylistId!!).singleOrNull()?.let { playlist -> - if (playlist.youtubePlaylistId == localPlaylistEntity.youtubePlaylistId) { - for (i in listTrack.indices) { - mainRepository.insertSong(listTrack[i].toSongEntity()).first().let { - println("Insert song $it") - } - mainRepository.insertPairSongLocalPlaylist( - PairSongLocalPlaylist( - playlistId = playlist.id, - songId = listTrack[i].videoId, - position = i, - inPlaylist = LocalDateTime.now(), - ), - ) - if (i == 100) { - delay(100) - } + fun insertLocalPlaylist( + localPlaylistEntity: LocalPlaylistEntity, + listTrack: List, + ) { + viewModelScope.launch { + mainRepository.insertLocalPlaylist(localPlaylistEntity) + delay(500) + mainRepository.getLocalPlaylistByYoutubePlaylistId(localPlaylistEntity.youtubePlaylistId!!).singleOrNull()?.let { playlist -> + if (playlist.youtubePlaylistId == localPlaylistEntity.youtubePlaylistId) { + for (i in listTrack.indices) { + mainRepository.insertSong(listTrack[i].toSongEntity()).first().let { + println("Insert song $it") + } + mainRepository.insertPairSongLocalPlaylist( + PairSongLocalPlaylist( + playlistId = playlist.id, + songId = listTrack[i].videoId, + position = i, + inPlaylist = LocalDateTime.now(), + ), + ) + if (i == 100) { + delay(100) } } } - Toast.makeText(application, application.getString(R.string.added_local_playlist), Toast.LENGTH_SHORT).show() } + Toast.makeText(application, application.getString(R.string.added_local_playlist), Toast.LENGTH_SHORT).show() } + } - private var _localPlaylistIfYouTubePlaylist: MutableStateFlow = MutableStateFlow(null) - var localPlaylistIfYouTubePlaylist: MutableStateFlow = _localPlaylistIfYouTubePlaylist + private var _localPlaylistIfYouTubePlaylist: MutableStateFlow = MutableStateFlow(null) + var localPlaylistIfYouTubePlaylist: MutableStateFlow = _localPlaylistIfYouTubePlaylist - fun checkSyncedPlaylist(value: String?) { - viewModelScope.launch { - if (value != null) { - mainRepository.getLocalPlaylistByYoutubePlaylistId(value).collect { - _localPlaylistIfYouTubePlaylist.value = it - } + fun checkSyncedPlaylist(value: String?) { + viewModelScope.launch { + if (value != null) { + mainRepository.getLocalPlaylistByYoutubePlaylistId(value).collect { + _localPlaylistIfYouTubePlaylist.value = it } } } + } - fun addToYouTubePlaylist( - localPlaylistId: Long, - youtubePlaylistId: String, - videoId: String, - ) { - viewModelScope.launch { - mainRepository.updateLocalPlaylistYouTubePlaylistSyncState(localPlaylistId, LocalPlaylistEntity.YouTubeSyncState.Syncing) - mainRepository.addYouTubePlaylistItem(youtubePlaylistId, videoId).collect { response -> - if (response == "STATUS_SUCCEEDED") { - mainRepository.updateLocalPlaylistYouTubePlaylistSyncState(localPlaylistId, LocalPlaylistEntity.YouTubeSyncState.Synced) - Toast.makeText(application, application.getString(R.string.added_to_youtube_playlist), Toast.LENGTH_SHORT).show() - } else { - mainRepository.updateLocalPlaylistYouTubePlaylistSyncState(localPlaylistId, LocalPlaylistEntity.YouTubeSyncState.NotSynced) - Toast.makeText(application, application.getString(R.string.error), Toast.LENGTH_SHORT).show() - } + fun addToYouTubePlaylist( + localPlaylistId: Long, + youtubePlaylistId: String, + videoId: String, + ) { + viewModelScope.launch { + mainRepository.updateLocalPlaylistYouTubePlaylistSyncState(localPlaylistId, LocalPlaylistEntity.YouTubeSyncState.Syncing) + mainRepository.addYouTubePlaylistItem(youtubePlaylistId, videoId).collect { response -> + if (response == "STATUS_SUCCEEDED") { + mainRepository.updateLocalPlaylistYouTubePlaylistSyncState(localPlaylistId, LocalPlaylistEntity.YouTubeSyncState.Synced) + Toast.makeText(application, application.getString(R.string.added_to_youtube_playlist), Toast.LENGTH_SHORT).show() + } else { + mainRepository.updateLocalPlaylistYouTubePlaylistSyncState(localPlaylistId, LocalPlaylistEntity.YouTubeSyncState.NotSynced) + Toast.makeText(application, application.getString(R.string.error), Toast.LENGTH_SHORT).show() } } } + } - fun insertRadioPlaylist(playlistEntity: PlaylistEntity) { - viewModelScope.launch { - mainRepository.insertRadioPlaylist(playlistEntity) - mainRepository.getPlaylist(playlistEntity.id).collect { values -> - _playlistEntity.value = values - if (values != null) { - _liked.value = values.liked - } + fun insertRadioPlaylist(playlistEntity: PlaylistEntity) { + viewModelScope.launch { + mainRepository.insertRadioPlaylist(playlistEntity) + mainRepository.getPlaylist(playlistEntity.id).collect { values -> + _playlistEntity.value = values + if (values != null) { + _liked.value = values.liked } } } + } - fun updateInLibrary(videoId: String) { - viewModelScope.launch { - mainRepository.updateSongInLibrary(LocalDateTime.now(), videoId) - } + fun updateInLibrary(videoId: String) { + viewModelScope.launch { + mainRepository.updateSongInLibrary(LocalDateTime.now(), videoId) } + } - fun insertPairSongLocalPlaylist(pairSongLocalPlaylist: PairSongLocalPlaylist) { - viewModelScope.launch { - mainRepository.insertPairSongLocalPlaylist(pairSongLocalPlaylist) - } + fun insertPairSongLocalPlaylist(pairSongLocalPlaylist: PairSongLocalPlaylist) { + viewModelScope.launch { + mainRepository.insertPairSongLocalPlaylist(pairSongLocalPlaylist) } + } - fun insertPlaylistSongEntity(tracks: List) { - viewModelScope.launch { - tracks.forEach { track -> - mainRepository.insertSong(track.toSongEntity()).first().let { - println("Insert Song: $it") - } - Log.w("PlaylistFragment", "Insert Song: ${track.toSongEntity()}") + fun insertPlaylistSongEntity(tracks: List) { + viewModelScope.launch { + tracks.forEach { track -> + mainRepository.insertSong(track.toSongEntity()).first().let { + println("Insert Song: $it") } + Log.w("PlaylistFragment", "Insert Song: ${track.toSongEntity()}") } } + } fun checkSuccess() { - if (_playlistBrowse.value != null) { + if (_playlistBrowse.value != null) { _uiState.value = PlaylistUIState.Success } } @@ -644,6 +642,10 @@ class PlaylistViewModel sealed class PlaylistUIState { data object Loading : PlaylistUIState() + data object Success : PlaylistUIState() - data class Error(val message: String? = null) : PlaylistUIState() + + data class Error( + val message: String? = null, + ) : PlaylistUIState() } \ No newline at end of file diff --git a/app/src/main/java/com/maxrave/simpmusic/viewModel/PodcastViewModel.kt b/app/src/main/java/com/maxrave/simpmusic/viewModel/PodcastViewModel.kt index 31b0cd99..76698345 100644 --- a/app/src/main/java/com/maxrave/simpmusic/viewModel/PodcastViewModel.kt +++ b/app/src/main/java/com/maxrave/simpmusic/viewModel/PodcastViewModel.kt @@ -2,24 +2,23 @@ package com.maxrave.simpmusic.viewModel import android.app.Application import android.graphics.drawable.GradientDrawable -import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope -import com.maxrave.simpmusic.data.dataStore.DataStoreManager import com.maxrave.simpmusic.data.model.podcast.PodcastBrowse -import com.maxrave.simpmusic.data.repository.MainRepository import com.maxrave.simpmusic.utils.Resource -import dagger.hilt.android.lifecycle.HiltViewModel +import com.maxrave.simpmusic.viewModel.base.BaseViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import javax.inject.Inject +import org.koin.android.annotation.KoinViewModel + +@KoinViewModel +class PodcastViewModel( + application: Application, +) : BaseViewModel(application) { + override val tag: String + get() = "PodcastViewModel" -@HiltViewModel -class PodcastViewModel @Inject constructor( - private val mainRepository: MainRepository, - private val application: Application -) : AndroidViewModel(application) { var gradientDrawable: MutableLiveData = MutableLiveData() var loading = MutableLiveData() var id = MutableLiveData() diff --git a/app/src/main/java/com/maxrave/simpmusic/viewModel/RecentlySongsViewModel.kt b/app/src/main/java/com/maxrave/simpmusic/viewModel/RecentlySongsViewModel.kt index 69ff55fb..7a52990b 100644 --- a/app/src/main/java/com/maxrave/simpmusic/viewModel/RecentlySongsViewModel.kt +++ b/app/src/main/java/com/maxrave/simpmusic/viewModel/RecentlySongsViewModel.kt @@ -1,23 +1,26 @@ package com.maxrave.simpmusic.viewModel import android.app.Application -import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope import androidx.paging.Pager import androidx.paging.PagingConfig import androidx.paging.cachedIn -import com.maxrave.simpmusic.data.repository.MainRepository import com.maxrave.simpmusic.pagination.RecentPagingSource -import dagger.hilt.android.lifecycle.HiltViewModel -import javax.inject.Inject +import com.maxrave.simpmusic.viewModel.base.BaseViewModel +import org.koin.android.annotation.KoinViewModel -@HiltViewModel -class RecentlySongsViewModel @Inject constructor(application: Application, private val mainRepository: MainRepository): AndroidViewModel(application) { - val recentlySongs = Pager( +@KoinViewModel +class RecentlySongsViewModel( + application: Application, +) : BaseViewModel(application) { + override val tag: String = "RecentlySongsViewModel" + + val recentlySongs = + Pager( PagingConfig( pageSize = 20, enablePlaceholders = false, - initialLoadSize = 20 + initialLoadSize = 20, ), ) { RecentPagingSource(mainRepository) diff --git a/app/src/main/java/com/maxrave/simpmusic/viewModel/SearchViewModel.kt b/app/src/main/java/com/maxrave/simpmusic/viewModel/SearchViewModel.kt index 47481260..b8c979c4 100644 --- a/app/src/main/java/com/maxrave/simpmusic/viewModel/SearchViewModel.kt +++ b/app/src/main/java/com/maxrave/simpmusic/viewModel/SearchViewModel.kt @@ -3,7 +3,6 @@ package com.maxrave.simpmusic.viewModel import android.app.Application import android.util.Log import android.widget.Toast -import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope @@ -13,7 +12,6 @@ import com.maxrave.kotlinytmusicscraper.models.SearchSuggestions import com.maxrave.simpmusic.R import com.maxrave.simpmusic.common.DownloadState import com.maxrave.simpmusic.common.SELECTED_LANGUAGE -import com.maxrave.simpmusic.data.dataStore.DataStoreManager import com.maxrave.simpmusic.data.db.entities.LocalPlaylistEntity import com.maxrave.simpmusic.data.db.entities.PairSongLocalPlaylist import com.maxrave.simpmusic.data.db.entities.SearchHistory @@ -24,12 +22,11 @@ import com.maxrave.simpmusic.data.model.searchResult.artists.ArtistsResult import com.maxrave.simpmusic.data.model.searchResult.playlists.PlaylistsResult import com.maxrave.simpmusic.data.model.searchResult.songs.SongsResult import com.maxrave.simpmusic.data.model.searchResult.videos.VideosResult -import com.maxrave.simpmusic.data.repository.MainRepository import com.maxrave.simpmusic.extension.toQueryList import com.maxrave.simpmusic.extension.toSongEntity import com.maxrave.simpmusic.service.test.download.DownloadUtils import com.maxrave.simpmusic.utils.Resource -import dagger.hilt.android.lifecycle.HiltViewModel +import com.maxrave.simpmusic.viewModel.base.BaseViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -39,14 +36,19 @@ import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext +import org.koin.android.annotation.KoinViewModel +import org.koin.core.component.inject import java.time.LocalDateTime -import javax.inject.Inject @UnstableApi -@HiltViewModel -class SearchViewModel @Inject constructor(private val mainRepository: MainRepository, private val application: Application, private var dataStoreManager: DataStoreManager) : AndroidViewModel(application) { - @Inject - lateinit var downloadUtils: DownloadUtils +@KoinViewModel +class SearchViewModel( + private val application: Application, +) : BaseViewModel(application) { + override val tag: String + get() = "SearchViewModel" + + private val downloadUtils: DownloadUtils by inject() var searchType: MutableLiveData = MutableLiveData("all") var searchAllResult: MutableLiveData> = MutableLiveData() @@ -54,15 +56,15 @@ class SearchViewModel @Inject constructor(private val mainRepository: MainReposi var searchHistory: MutableLiveData> = MutableLiveData() var searchResult: MutableLiveData> = MutableLiveData() - private var _songSearchResult: MutableLiveData>> = MutableLiveData() - val songsSearchResult: LiveData>> = _songSearchResult + private var _songsSearchResult: MutableLiveData>> = MutableLiveData() + val songsSearchResult: LiveData>> = _songsSearchResult - private var _artistSearchResult: MutableLiveData>> = MutableLiveData() - val artistsSearchResult: LiveData>> = _artistSearchResult + private var _artistsSearchResult: MutableLiveData>> = MutableLiveData() + val artistsSearchResult: LiveData>> = _artistsSearchResult - private var _albumSearchResult: MutableLiveData>> = + private var _albumsSearchResult: MutableLiveData>> = MutableLiveData() - val albumsSearchResult: LiveData>> = _albumSearchResult + val albumsSearchResult: LiveData>> = _albumsSearchResult private var _playlistSearchResult: MutableLiveData>> = MutableLiveData() @@ -99,7 +101,7 @@ class SearchViewModel @Inject constructor(private val mainRepository: MainReposi fun getSearchHistory() { viewModelScope.launch { - mainRepository.getSearchHistory().collect{ values -> + mainRepository.getSearchHistory().collect { values -> if (values.isNotEmpty()) { values.toQueryList().let { list -> searchHistory.value = list @@ -122,7 +124,7 @@ class SearchViewModel @Inject constructor(private val mainRepository: MainReposi } fun searchSongs(query: String) { - if (loading.value == false){ + if (loading.value == false) { loading.value = true viewModelScope.launch { // mainRepository.searchSongs(query, "songs", regionCode!!, SUPPORTED_LANGUAGE.serverCodes[SUPPORTED_LANGUAGE.codes.indexOf(language!!)]).collect { values -> @@ -133,8 +135,7 @@ class SearchViewModel @Inject constructor(private val mainRepository: MainReposi // } // } mainRepository.getSearchDataSong(query).collect { - _songSearchResult.value = it - Log.d("SearchViewModel", "searchSongs: ${_songSearchResult.value}") + _songsSearchResult.value = it withContext(Dispatchers.Main) { loading.value = false } @@ -142,52 +143,57 @@ class SearchViewModel @Inject constructor(private val mainRepository: MainReposi } } } + fun searchAll(query: String) { searchAllResult.value?.clear() loading.value = true viewModelScope.launch { - val job1 = launch { - mainRepository.getSearchDataSong(query).collect {values -> - _songSearchResult.value = values - Log.d("SearchViewModel", "searchSongs: ${_songSearchResult.value}") + val job1 = + launch { + mainRepository.getSearchDataSong(query).collect { values -> + _songsSearchResult.value = values + } } - } - val job2 = launch { - mainRepository.getSearchDataArtist(query).collect {values -> - _artistSearchResult.value = values - Log.d("SearchViewModel", "searchArtists: ${_artistSearchResult.value}") + val job2 = + launch { + mainRepository.getSearchDataArtist(query).collect { values -> + _artistsSearchResult.value = values + } } - } - val job3 = launch { - mainRepository.getSearchDataAlbum(query).collect {values -> - _albumSearchResult.value = values - Log.d("SearchViewModel", "searchAlbums: ${_albumSearchResult.value}") + val job3 = + launch { + mainRepository.getSearchDataAlbum(query).collect { values -> + _albumsSearchResult.value = values + } } - } - val job4 = launch { - mainRepository.getSearchDataPlaylist(query).collect {values -> - _playlistSearchResult.value = values - Log.d("SearchViewModel", "searchPlaylists: ${_playlistSearchResult.value}") + val job4 = + launch { + mainRepository.getSearchDataPlaylist(query).collect { values -> + _playlistSearchResult.value = values + Log.d("SearchViewModel", "searchPlaylists: ${_playlistSearchResult.value}") + } } - } - val job5 = launch { - mainRepository.getSearchDataVideo(query).collect { values -> - _videoSearchResult.value = values - Log.d("SearchViewModel", "searchVideos: ${_videoSearchResult.value}") + val job5 = + launch { + mainRepository.getSearchDataVideo(query).collect { values -> + _videoSearchResult.value = values + Log.d("SearchViewModel", "searchVideos: ${_videoSearchResult.value}") + } } - } - val job6 = launch { - mainRepository.getSearchDataFeaturedPlaylist(query).collect { values -> - Log.d("SearchViewModel", "featured: $values") - _featuredPlaylistSearchResult.value = values + val job6 = + launch { + mainRepository.getSearchDataFeaturedPlaylist(query).collect { values -> + Log.d("SearchViewModel", "featured: $values") + _featuredPlaylistSearchResult.value = values + } } - } - val job7 = launch { - mainRepository.getSearchDataPodcast(query).collect { values -> - Log.d("SearchViewModel", "podcast: ${values.data.toString()}") - _podcastSearchResult.value = values + val job7 = + launch { + mainRepository.getSearchDataPodcast(query).collect { values -> + Log.d("SearchViewModel", "podcast: ${values.data}") + _podcastSearchResult.value = values + } } - } job1.join() job2.join() job3.join() @@ -200,23 +206,22 @@ class SearchViewModel @Inject constructor(private val mainRepository: MainReposi } } } - fun suggestQuery(query: String){ - viewModelScope.launch { - mainRepository.getSuggestQuery(query).collect{ values -> - Log.d("SearchViewModel", "suggestQuery: $values") - _suggestQuery.value = values - } + + fun suggestQuery(query: String) { + viewModelScope.launch { + mainRepository.getSuggestQuery(query).collect { values -> + Log.d("SearchViewModel", "suggestQuery: $values") + _suggestQuery.value = values + } } } - fun searchAlbums(query: String) { if (loading.value == false) { loading.value = true viewModelScope.launch { mainRepository.getSearchDataAlbum(query).collect { values -> - _albumSearchResult.value = values - Log.d("SearchViewModel", "searchAlbums: ${_albumSearchResult.value}") + _albumsSearchResult.value = values withContext(Dispatchers.Main) { loading.value = false } @@ -233,7 +238,7 @@ class SearchViewModel @Inject constructor(private val mainRepository: MainReposi _featuredPlaylistSearchResult.value = values Log.d( "SearchViewModel", - "searchFeaturedPlaylist: ${_featuredPlaylistSearchResult.value}" + "searchFeaturedPlaylist: ${_featuredPlaylistSearchResult.value}", ) withContext(Dispatchers.Main) { loading.value = false @@ -263,27 +268,30 @@ class SearchViewModel @Inject constructor(private val mainRepository: MainReposi loading.value = true viewModelScope.launch { mainRepository.getSearchDataArtist(query).collect { values -> - _artistSearchResult.value = values - Log.d("SearchViewModel", "searchArtists: ${_artistSearchResult.value}") + _artistsSearchResult.value = values withContext(Dispatchers.Main) { loading.value = false + } } - } } + } } } fun searchPlaylists(query: String) { - if (loading.value == false){ + if (loading.value == false) { loading.value = true - viewModelScope.launch { mainRepository.getSearchDataPlaylist(query).collect {values -> - _playlistSearchResult.value = values - Log.d("SearchViewModel", "searchPlaylists: ${_playlistSearchResult.value}") - withContext(Dispatchers.Main) { - loading.value = false + viewModelScope.launch { + mainRepository.getSearchDataPlaylist(query).collect { values -> + _playlistSearchResult.value = values + Log.d("SearchViewModel", "searchPlaylists: ${_playlistSearchResult.value}") + withContext(Dispatchers.Main) { + loading.value = false + } } - } } + } } } + fun searchVideos(query: String) { if (loading.value == false) { loading.value = true @@ -299,12 +307,14 @@ class SearchViewModel @Inject constructor(private val mainRepository: MainReposi } } - fun updateLikeStatus(videoId: String, b: Boolean) { + fun updateLikeStatus( + videoId: String, + b: Boolean, + ) { viewModelScope.launch { - if (b){ + if (b) { mainRepository.updateLikeStatus(videoId, 1) - } - else { + } else { mainRepository.updateLikeStatus(videoId, 0) } } @@ -323,6 +333,7 @@ class SearchViewModel @Inject constructor(private val mainRepository: MainReposi private var _listLocalPlaylist: MutableLiveData> = MutableLiveData() val localPlaylist: LiveData> = _listLocalPlaylist + fun getAllLocalPlaylist() { viewModelScope.launch { mainRepository.getAllLocalPlaylists().collect { values -> @@ -331,7 +342,10 @@ class SearchViewModel @Inject constructor(private val mainRepository: MainReposi } } - fun updateDownloadState(videoId: String, state: Int) { + fun updateDownloadState( + videoId: String, + state: Int, + ) { viewModelScope.launch { mainRepository.getSongById(videoId).collect { songEntity -> _songEntity.value = songEntity @@ -351,7 +365,7 @@ class SearchViewModel @Inject constructor(private val mainRepository: MainReposi if (down != null) { when (down.state) { Download.STATE_COMPLETED -> { - mainRepository.getSongById(videoId).collect{ song -> + mainRepository.getSongById(videoId).collect { song -> if (song?.downloadState != DownloadState.STATE_DOWNLOADED) { mainRepository.updateDownloadState(videoId, DownloadState.STATE_DOWNLOADED) } @@ -359,7 +373,7 @@ class SearchViewModel @Inject constructor(private val mainRepository: MainReposi Log.d("Check Downloaded", "Downloaded") } Download.STATE_FAILED -> { - mainRepository.getSongById(videoId).collect{ song -> + mainRepository.getSongById(videoId).collect { song -> if (song?.downloadState != DownloadState.STATE_NOT_DOWNLOADED) { mainRepository.updateDownloadState(videoId, DownloadState.STATE_NOT_DOWNLOADED) } @@ -367,7 +381,7 @@ class SearchViewModel @Inject constructor(private val mainRepository: MainReposi Log.d("Check Downloaded", "Failed") } Download.STATE_DOWNLOADING -> { - mainRepository.getSongById(videoId).collect{ song -> + mainRepository.getSongById(videoId).collect { song -> if (song?.downloadState != DownloadState.STATE_DOWNLOADING) { mainRepository.updateDownloadState(videoId, DownloadState.STATE_DOWNLOADING) } @@ -380,36 +394,41 @@ class SearchViewModel @Inject constructor(private val mainRepository: MainReposi } } - fun updateLocalPlaylistTracks(list: List, id: Long) { + fun updateLocalPlaylistTracks( + list: List, + id: Long, + ) { viewModelScope.launch { mainRepository.getSongsByListVideoId(list).collect { values -> var count = 0 values.forEach { song -> - if (song.downloadState == DownloadState.STATE_DOWNLOADED){ + if (song.downloadState == DownloadState.STATE_DOWNLOADED) { count++ } } mainRepository.updateLocalPlaylistTracks(list, id) - Toast.makeText(getApplication(), application.getString (R.string.added_to_playlist), Toast.LENGTH_SHORT).show() + Toast.makeText(getApplication(), application.getString(R.string.added_to_playlist), Toast.LENGTH_SHORT).show() if (count == values.size) { mainRepository.updateLocalPlaylistDownloadState(DownloadState.STATE_DOWNLOADED, id) - } - else { + } else { mainRepository.updateLocalPlaylistDownloadState(DownloadState.STATE_NOT_DOWNLOADED, id) } } } } - fun addToYouTubePlaylist(localPlaylistId: Long, youtubePlaylistId: String, videoId: String) { + fun addToYouTubePlaylist( + localPlaylistId: Long, + youtubePlaylistId: String, + videoId: String, + ) { viewModelScope.launch { mainRepository.updateLocalPlaylistYouTubePlaylistSyncState(localPlaylistId, LocalPlaylistEntity.YouTubeSyncState.Syncing) mainRepository.addYouTubePlaylistItem(youtubePlaylistId, videoId).collect { response -> if (response == "STATUS_SUCCEEDED") { mainRepository.updateLocalPlaylistYouTubePlaylistSyncState(localPlaylistId, LocalPlaylistEntity.YouTubeSyncState.Synced) Toast.makeText(getApplication(), application.getString(R.string.added_to_youtube_playlist), Toast.LENGTH_SHORT).show() - } - else { + } else { mainRepository.updateLocalPlaylistYouTubePlaylistSyncState(localPlaylistId, LocalPlaylistEntity.YouTubeSyncState.NotSynced) Toast.makeText(getApplication(), application.getString(R.string.error), Toast.LENGTH_SHORT).show() } diff --git a/app/src/main/java/com/maxrave/simpmusic/viewModel/SettingsViewModel.kt b/app/src/main/java/com/maxrave/simpmusic/viewModel/SettingsViewModel.kt index 855f8b17..88257a13 100644 --- a/app/src/main/java/com/maxrave/simpmusic/viewModel/SettingsViewModel.kt +++ b/app/src/main/java/com/maxrave/simpmusic/viewModel/SettingsViewModel.kt @@ -6,7 +6,6 @@ import android.content.Intent import android.net.Uri import android.util.Log import android.widget.Toast -import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope import androidx.media3.common.util.UnstableApi import androidx.media3.datasource.cache.SimpleCache @@ -16,6 +15,7 @@ import com.maxrave.kotlinytmusicscraper.YouTube import com.maxrave.kotlinytmusicscraper.models.YouTubeLocale import com.maxrave.kotlinytmusicscraper.models.simpmusic.GithubResponse import com.maxrave.simpmusic.R +import com.maxrave.simpmusic.common.Config import com.maxrave.simpmusic.common.DB_NAME import com.maxrave.simpmusic.common.DownloadState import com.maxrave.simpmusic.common.QUALITY @@ -26,15 +26,12 @@ import com.maxrave.simpmusic.data.dataStore.DataStoreManager import com.maxrave.simpmusic.data.db.DatabaseDao import com.maxrave.simpmusic.data.db.MusicDatabase import com.maxrave.simpmusic.data.db.entities.GoogleAccountEntity -import com.maxrave.simpmusic.data.repository.MainRepository -import com.maxrave.simpmusic.di.DownloadCache -import com.maxrave.simpmusic.di.PlayerCache import com.maxrave.simpmusic.extension.div import com.maxrave.simpmusic.extension.zipInputStream import com.maxrave.simpmusic.extension.zipOutputStream import com.maxrave.simpmusic.service.SimpleMediaService import com.maxrave.simpmusic.ui.MainActivity -import dagger.hilt.android.lifecycle.HiltViewModel +import com.maxrave.simpmusic.viewModel.base.BaseViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow @@ -42,25 +39,25 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking +import org.koin.android.annotation.KoinViewModel +import org.koin.core.component.inject +import org.koin.core.qualifier.named import java.io.FileInputStream import java.io.FileOutputStream import java.util.zip.ZipEntry -import javax.inject.Inject import kotlin.system.exitProcess @UnstableApi -@HiltViewModel -class SettingsViewModel @Inject constructor( +@KoinViewModel +class SettingsViewModel( private val application: Application, - private var mainRepository: MainRepository, - private var database: MusicDatabase, - private var databaseDao: DatabaseDao, - @PlayerCache val playerCache: SimpleCache, - @DownloadCache val downloadCache: SimpleCache, -) : AndroidViewModel(application) { +) : BaseViewModel(application) { + override val tag: String = "SettingsViewModel" - @Inject - lateinit var dataStoreManager: DataStoreManager + private val database: MusicDatabase by inject() + private val databaseDao: DatabaseDao by inject() + val playerCache: SimpleCache by inject(named(Config.PLAYER_CACHE)) + val downloadCache: SimpleCache by inject(named(Config.DOWNLOAD_CACHE)) private var _location: MutableStateFlow = MutableStateFlow(null) val location: StateFlow = _location @@ -128,10 +125,8 @@ class SettingsViewModel @Inject constructor( @OptIn(ExperimentalCoilApi::class) fun getThumbCacheSize() { viewModelScope.launch { - val diskCache = application.imageLoader.diskCache _thumbCacheSize.emit(diskCache?.size) - } } @@ -148,64 +143,70 @@ class SettingsViewModel @Inject constructor( fun getTranslationLanguage() { viewModelScope.launch { - dataStoreManager.translationLanguage.collect { translationLanguage -> - _translationLanguage.emit(translationLanguage) + dataStoreManager.translationLanguage.collect { translationLanguage -> + _translationLanguage.emit(translationLanguage) } } } fun setTranslationLanguage(language: String) { viewModelScope.launch { - dataStoreManager.setTranslationLanguage(language) - getTranslationLanguage() + dataStoreManager.setTranslationLanguage(language) + getTranslationLanguage() } } + fun getUseTranslation() { viewModelScope.launch { - dataStoreManager.enableTranslateLyric.collect { useTranslation -> - _useTranslation.emit(useTranslation) - } + dataStoreManager.enableTranslateLyric.collect { useTranslation -> + _useTranslation.emit(useTranslation) + } } } + fun setUseTranslation(useTranslation: Boolean) { viewModelScope.launch { - dataStoreManager.setEnableTranslateLyric(useTranslation) - getUseTranslation() + dataStoreManager.setEnableTranslateLyric(useTranslation) + getUseTranslation() } } + fun getMusixmatchLoggedIn() { viewModelScope.launch { - dataStoreManager.musixmatchLoggedIn.collect { musixmatchLoggedIn -> - _musixmatchLoggedIn.emit(musixmatchLoggedIn) - } + dataStoreManager.musixmatchLoggedIn.collect { musixmatchLoggedIn -> + _musixmatchLoggedIn.emit(musixmatchLoggedIn) + } } } + fun setMusixmatchLoggedIn(loggedIn: Boolean) { viewModelScope.launch { - dataStoreManager.setMusixmatchLoggedIn(loggedIn) - getMusixmatchLoggedIn() + dataStoreManager.setMusixmatchLoggedIn(loggedIn) + getMusixmatchLoggedIn() } } + fun getLyricsProvider() { viewModelScope.launch { - dataStoreManager.lyricsProvider.collect { mainLyricsProvider -> - _mainLyricsProvider.emit(mainLyricsProvider) - } + dataStoreManager.lyricsProvider.collect { mainLyricsProvider -> + _mainLyricsProvider.emit(mainLyricsProvider) } + } } + fun setLyricsProvider(provider: String) { viewModelScope.launch { - dataStoreManager.setLyricsProvider(provider) - getLyricsProvider() + dataStoreManager.setLyricsProvider(provider) + getLyricsProvider() } } fun checkForUpdate() { viewModelScope.launch { - mainRepository.checkForUpdate().collect {response -> + mainRepository.checkForUpdate().collect { response -> dataStoreManager.putString( "CheckForUpdateAt", - System.currentTimeMillis().toString() + System.currentTimeMillis().toString(), ) _githubResponse.emit(response) } @@ -214,47 +215,50 @@ class SettingsViewModel @Inject constructor( fun getLocation() { viewModelScope.launch { - dataStoreManager.location.collect { location -> - _location.emit(location) - } + dataStoreManager.location.collect { location -> + _location.emit(location) + } } } + fun getLoggedIn() { viewModelScope.launch { - dataStoreManager.loggedIn.collect { loggedIn -> - _loggedIn.emit(loggedIn) - } + dataStoreManager.loggedIn.collect { loggedIn -> + _loggedIn.emit(loggedIn) + } } } fun changeLocation(location: String) { viewModelScope.launch { - dataStoreManager.setLocation(location) - YouTube.locale = YouTubeLocale(location, language.value!!) - getLocation() + dataStoreManager.setLocation(location) + YouTube.locale = YouTubeLocale(location, language.value!!) + getLocation() } } + fun getSaveRecentSongAndQueue() { viewModelScope.launch { - dataStoreManager.saveRecentSongAndQueue.collect { saved -> - _saveRecentSongAndQueue.emit(saved) - } + dataStoreManager.saveRecentSongAndQueue.collect { saved -> + _saveRecentSongAndQueue.emit(saved) + } } } + fun getLastCheckForUpdate() { viewModelScope.launch { - dataStoreManager.getString("CheckForUpdateAt").first().let { lastCheckForUpdate -> - _lastCheckForUpdate.emit(lastCheckForUpdate) - } + dataStoreManager.getString("CheckForUpdateAt").first().let { lastCheckForUpdate -> + _lastCheckForUpdate.emit(lastCheckForUpdate) + } } } fun getSponsorBlockEnabled() { viewModelScope.launch { - dataStoreManager.sponsorBlockEnabled.first().let { enabled -> - _sponsorBlockEnabled.emit(enabled) - } + dataStoreManager.sponsorBlockEnabled.first().let { enabled -> + _sponsorBlockEnabled.emit(enabled) } + } } fun setSponsorBlockEnabled(enabled: Boolean) { @@ -266,24 +270,24 @@ class SettingsViewModel @Inject constructor( fun getPlayVideoInsteadOfAudio() { viewModelScope.launch { - dataStoreManager.watchVideoInsteadOfPlayingAudio.collect { playVideoInsteadOfAudio -> - _playVideoInsteadOfAudio.emit(playVideoInsteadOfAudio) - } + dataStoreManager.watchVideoInsteadOfPlayingAudio.collect { playVideoInsteadOfAudio -> + _playVideoInsteadOfAudio.emit(playVideoInsteadOfAudio) + } } } fun setPlayVideoInsteadOfAudio(playVideoInsteadOfAudio: Boolean) { viewModelScope.launch { - dataStoreManager.setWatchVideoInsteadOfPlayingAudio(playVideoInsteadOfAudio) - getPlayVideoInsteadOfAudio() + dataStoreManager.setWatchVideoInsteadOfPlayingAudio(playVideoInsteadOfAudio) + getPlayVideoInsteadOfAudio() } } fun getSponsorBlockCategories() { viewModelScope.launch { - dataStoreManager.getSponsorBlockCategories().let { - _sponsorBlockCategories.emit(it) - } + dataStoreManager.getSponsorBlockCategories().let { + _sponsorBlockCategories.emit(it) + } } } @@ -310,11 +314,11 @@ class SettingsViewModel @Inject constructor( fun changeVideoQuality(checkedIndex: Int) { viewModelScope.launch { - when (checkedIndex) { - 0 -> dataStoreManager.setVideoQuality(VIDEO_QUALITY.items[0].toString()) - 1 -> dataStoreManager.setVideoQuality(VIDEO_QUALITY.items[1].toString()) - } - getVideoQuality() + when (checkedIndex) { + 0 -> dataStoreManager.setVideoQuality(VIDEO_QUALITY.items[0].toString()) + 1 -> dataStoreManager.setVideoQuality(VIDEO_QUALITY.items[1].toString()) + } + getVideoQuality() } } @@ -361,7 +365,7 @@ class SettingsViewModel @Inject constructor( downloadCache.keys.forEach { key -> downloadCache.removeResource(key) } - mainRepository.getDownloadedSongs().collect {songs -> + mainRepository.getDownloadedSongs().collect { songs -> songs?.forEach { song -> mainRepository.updateDownloadState(song.videoId, DownloadState.STATE_NOT_DOWNLOADED) } @@ -371,11 +375,14 @@ class SettingsViewModel @Inject constructor( } } - fun backup(context: Context, uri: Uri) { + fun backup( + context: Context, + uri: Uri, + ) { runCatching { context.applicationContext.contentResolver.openOutputStream(uri)?.use { it.buffered().zipOutputStream().use { outputStream -> - (context.filesDir/"datastore"/"$SETTINGS_FILENAME.preferences_pb").inputStream().buffered().use { inputStream -> + (context.filesDir / "datastore" / "$SETTINGS_FILENAME.preferences_pb").inputStream().buffered().use { inputStream -> outputStream.putNextEntry(ZipEntry("$SETTINGS_FILENAME.preferences_pb")) inputStream.copyTo(outputStream) } @@ -389,17 +396,28 @@ class SettingsViewModel @Inject constructor( } } }.onSuccess { - Toast.makeText(context, - context.getString(R.string.backup_create_success), Toast.LENGTH_SHORT).show() + Toast + .makeText( + context, + context.getString(R.string.backup_create_success), + Toast.LENGTH_SHORT, + ).show() }.onFailure { it.printStackTrace() - Toast.makeText(context, - context.getString(R.string.backup_create_failed), Toast.LENGTH_SHORT).show() + Toast + .makeText( + context, + context.getString(R.string.backup_create_failed), + Toast.LENGTH_SHORT, + ).show() } } @UnstableApi - fun restore(context: Context, uri: Uri) { + fun restore( + context: Context, + uri: Uri, + ) { runCatching { context.applicationContext.contentResolver.openInputStream(uri)?.use { it.zipInputStream().use { inputStream -> @@ -408,7 +426,7 @@ class SettingsViewModel @Inject constructor( while (entry != null && count < 2) { when (entry.name) { "$SETTINGS_FILENAME.preferences_pb" -> { - (context.filesDir/"datastore"/"$SETTINGS_FILENAME.preferences_pb").outputStream().use { outputStream -> + (context.filesDir / "datastore" / "$SETTINGS_FILENAME.preferences_pb").outputStream().use { outputStream -> inputStream.copyTo(outputStream) } } @@ -428,11 +446,12 @@ class SettingsViewModel @Inject constructor( } } } - Toast.makeText( - context, - context.getString(R.string.restore_success), - Toast.LENGTH_SHORT - ).show() + Toast + .makeText( + context, + context.getString(R.string.restore_success), + Toast.LENGTH_SHORT, + ).show() context.stopService(Intent(context, SimpleMediaService::class.java)) context.startActivity(Intent(context, MainActivity::class.java)) exitProcess(0) @@ -444,63 +463,65 @@ class SettingsViewModel @Inject constructor( fun getLanguage() { viewModelScope.launch { - dataStoreManager.getString(SELECTED_LANGUAGE).collect { language -> - _language.emit(language) - } + dataStoreManager.getString(SELECTED_LANGUAGE).collect { language -> + _language.emit(language) + } } } @UnstableApi fun changeLanguage(code: String) { viewModelScope.launch { - dataStoreManager.putString(SELECTED_LANGUAGE, code) - Log.w("SettingsViewModel", "changeLanguage: $code") - YouTube.locale = YouTubeLocale(location.value!!, code.substring(0..1)) - getLanguage() + dataStoreManager.putString(SELECTED_LANGUAGE, code) + Log.w("SettingsViewModel", "changeLanguage: $code") + YouTube.locale = YouTubeLocale(location.value!!, code.substring(0..1)) + getLanguage() } } fun clearCookie() { viewModelScope.launch { - dataStoreManager.setCookie("") - YouTube.cookie = null - dataStoreManager.setLoggedIn(false) + dataStoreManager.setCookie("") + YouTube.cookie = null + dataStoreManager.setLoggedIn(false) } } fun getNormalizeVolume() { viewModelScope.launch { - dataStoreManager.normalizeVolume.collect { normalizeVolume -> - _normalizeVolume.emit(normalizeVolume) + dataStoreManager.normalizeVolume.collect { normalizeVolume -> + _normalizeVolume.emit(normalizeVolume) } } } + @UnstableApi fun setNormalizeVolume(normalizeVolume: Boolean) { viewModelScope.launch { - dataStoreManager.setNormalizeVolume(normalizeVolume) - getNormalizeVolume() + dataStoreManager.setNormalizeVolume(normalizeVolume) + getNormalizeVolume() } } + fun getSendBackToGoogle() { viewModelScope.launch { - - dataStoreManager.sendBackToGoogle.collect { sendBackToGoogle -> - _sendBackToGoogle.emit(sendBackToGoogle) + dataStoreManager.sendBackToGoogle.collect { sendBackToGoogle -> + _sendBackToGoogle.emit(sendBackToGoogle) } } } + fun setSendBackToGoogle(sendBackToGoogle: Boolean) { viewModelScope.launch { - dataStoreManager.setSendBackToGoogle(sendBackToGoogle) - getSendBackToGoogle() + dataStoreManager.setSendBackToGoogle(sendBackToGoogle) + getSendBackToGoogle() } } + fun getSkipSilent() { viewModelScope.launch { - dataStoreManager.skipSilent.collect { skipSilent -> - _skipSilent.emit(skipSilent) - + dataStoreManager.skipSilent.collect { skipSilent -> + _skipSilent.emit(skipSilent) } } } @@ -508,36 +529,38 @@ class SettingsViewModel @Inject constructor( @UnstableApi fun setSkipSilent(skip: Boolean) { viewModelScope.launch { - dataStoreManager.setSkipSilent(skip) - getSkipSilent() + dataStoreManager.setSkipSilent(skip) + getSkipSilent() } } + fun getSavedPlaybackState() { viewModelScope.launch { - dataStoreManager.saveStateOfPlayback.collect { savedPlaybackState -> - _savedPlaybackState.emit(savedPlaybackState) + dataStoreManager.saveStateOfPlayback.collect { savedPlaybackState -> + _savedPlaybackState.emit(savedPlaybackState) } } } + fun setSavedPlaybackState(savedPlaybackState: Boolean) { viewModelScope.launch { - dataStoreManager.setSaveStateOfPlayback(savedPlaybackState) - getSavedPlaybackState() + dataStoreManager.setSaveStateOfPlayback(savedPlaybackState) + getSavedPlaybackState() } } fun setSaveLastPlayed(b: Boolean) { viewModelScope.launch { - dataStoreManager.setSaveRecentSongAndQueue(b) - getSaveRecentSongAndQueue() + dataStoreManager.setSaveRecentSongAndQueue(b) + getSaveRecentSongAndQueue() } } fun clearMusixmatchCookie() { viewModelScope.launch { - dataStoreManager.setMusixmatchCookie("") - YouTube.musixMatchCookie = null - dataStoreManager.setMusixmatchLoggedIn(false) + dataStoreManager.setMusixmatchCookie("") + YouTube.musixMatchCookie = null + dataStoreManager.setMusixmatchLoggedIn(false) } } @@ -580,7 +603,7 @@ class SettingsViewModel @Inject constructor( dataStoreManager.putString("AccountName", it.name) dataStoreManager.putString( "AccountThumbUrl", - it.thumbnails.lastOrNull()?.url ?: "" + it.thumbnails.lastOrNull()?.url ?: "", ) mainRepository.insertGoogleAccount( GoogleAccountEntity( @@ -588,8 +611,8 @@ class SettingsViewModel @Inject constructor( name = it.name, thumbnailUrl = it.thumbnails.lastOrNull()?.url ?: "", cache = YouTube.cookie, - isUsed = true - ) + isUsed = true, + ), ) delay(500) getAllGoogleAccount() @@ -617,7 +640,7 @@ class SettingsViewModel @Inject constructor( dataStoreManager.putString("AccountName", accountInfo.name) dataStoreManager.putString( "AccountThumbUrl", - accountInfo.thumbnails.lastOrNull()?.url ?: "" + accountInfo.thumbnails.lastOrNull()?.url ?: "", ) mainRepository.insertGoogleAccount( GoogleAccountEntity( @@ -625,8 +648,8 @@ class SettingsViewModel @Inject constructor( name = accountInfo.name, thumbnailUrl = accountInfo.thumbnails.lastOrNull()?.url ?: "", cache = YouTube.cookie, - isUsed = true - ) + isUsed = true, + ), ) dataStoreManager.setLoggedIn(true) dataStoreManager.setCookie(YouTube.cookie ?: "") @@ -636,7 +659,6 @@ class SettingsViewModel @Inject constructor( } } } - } fun setUsedAccount(acc: GoogleAccountEntity?) { @@ -690,11 +712,12 @@ class SettingsViewModel @Inject constructor( fun clearThumbnailCache() { viewModelScope.launch { application.imageLoader.diskCache?.clear() - Toast.makeText( - getApplication(), - application.getString(R.string.clear_thumbnail_cache), - Toast.LENGTH_SHORT - ).show() + Toast + .makeText( + getApplication(), + application.getString(R.string.clear_thumbnail_cache), + Toast.LENGTH_SHORT, + ).show() getThumbCacheSize() } } @@ -712,7 +735,6 @@ class SettingsViewModel @Inject constructor( } } } - } fun setSpotifyLogIn(loggedIn: Boolean) { diff --git a/app/src/main/java/com/maxrave/simpmusic/viewModel/SharedViewModel.kt b/app/src/main/java/com/maxrave/simpmusic/viewModel/SharedViewModel.kt index d5140180..9c1f5df7 100644 --- a/app/src/main/java/com/maxrave/simpmusic/viewModel/SharedViewModel.kt +++ b/app/src/main/java/com/maxrave/simpmusic/viewModel/SharedViewModel.kt @@ -5,7 +5,6 @@ import android.content.Intent import android.graphics.drawable.GradientDrawable import android.util.Log import android.widget.Toast -import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope @@ -23,6 +22,7 @@ import com.maxrave.kotlinytmusicscraper.models.response.spotify.CanvasResponse import com.maxrave.kotlinytmusicscraper.models.simpmusic.GithubResponse import com.maxrave.simpmusic.R import com.maxrave.simpmusic.common.Config.ALBUM_CLICK +import com.maxrave.simpmusic.common.Config.DOWNLOAD_CACHE import com.maxrave.simpmusic.common.Config.PLAYLIST_CLICK import com.maxrave.simpmusic.common.Config.RECOVER_TRACK_QUEUE import com.maxrave.simpmusic.common.Config.SHARE @@ -44,8 +44,6 @@ import com.maxrave.simpmusic.data.db.entities.SongInfoEntity import com.maxrave.simpmusic.data.model.browse.album.Track import com.maxrave.simpmusic.data.model.metadata.Line import com.maxrave.simpmusic.data.model.metadata.Lyrics -import com.maxrave.simpmusic.data.repository.MainRepository -import com.maxrave.simpmusic.di.DownloadCache import com.maxrave.simpmusic.extension.isSong import com.maxrave.simpmusic.extension.isVideo import com.maxrave.simpmusic.extension.toArrayListTrack @@ -67,7 +65,7 @@ import com.maxrave.simpmusic.service.SleepTimerState import com.maxrave.simpmusic.service.test.download.DownloadUtils import com.maxrave.simpmusic.service.test.notification.NotifyWork import com.maxrave.simpmusic.utils.Resource -import dagger.hilt.android.lifecycle.HiltViewModel +import com.maxrave.simpmusic.viewModel.base.BaseViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.delay @@ -89,29 +87,28 @@ import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext +import org.koin.android.annotation.KoinViewModel +import org.koin.core.component.inject +import org.koin.core.qualifier.named import java.time.LocalDateTime import java.util.concurrent.TimeUnit -import javax.inject.Inject -@HiltViewModel +@KoinViewModel @UnstableApi -class SharedViewModel -@Inject -constructor( - private var dataStoreManager: DataStoreManager, - @DownloadCache private val downloadedCache: SimpleCache, - private val mainRepository: MainRepository, +class SharedViewModel( private val application: Application, -) : AndroidViewModel(application) { +) : BaseViewModel(application) { + var isFirstLiked: Boolean = false var isFirstMiniplayer: Boolean = false var isFirstSuggestions: Boolean = false var showOrHideMiniplayer: MutableSharedFlow = MutableSharedFlow() - private val TAG = "SharedViewModel" + override val tag = "SharedViewModel" + + private val downloadedCache: SimpleCache by inject(qualifier = named(DOWNLOAD_CACHE)) - @Inject - lateinit var downloadUtils: DownloadUtils + private val downloadUtils: DownloadUtils by inject() var simpleMediaServiceHandler: SimpleMediaServiceHandler? = null @@ -211,12 +208,12 @@ constructor( val timeline = it.first if (timeline.total > 0 && nowPlaying?.songEntity != null) { if (nowPlaying.mediaItem.isSong()) { - Log.w(TAG, "Duration is ${timeline.total}") - Log.w(TAG, "MediaId is ${nowPlaying.mediaItem.mediaId}") + Log.w(tag, "Duration is ${timeline.total}") + Log.w(tag, "MediaId is ${nowPlaying.mediaItem.mediaId}") getCanvas(nowPlaying.mediaItem.mediaId, (timeline.total / 1000).toInt()) } nowPlaying.songEntity.let { song -> - Log.w(TAG, "Get lyrics from format") + Log.w(tag, "Get lyrics from format") getLyricsFromFormat(song, (timeline.total / 1000).toInt()) } } @@ -234,7 +231,7 @@ constructor( val checkGetVideoJob = launch { dataStoreManager.watchVideoInsteadOfPlayingAudio.collectLatest { - Log.w(TAG, "GetVideo is $it") + Log.w(tag, "GetVideo is $it") _getVideo.value = it == TRUE } } @@ -261,7 +258,7 @@ constructor( handler.nowPlayingState.distinctUntilChangedBy { it.songEntity?.videoId }.collectLatest { state -> - Log.w(TAG, "NowPlayingState is $state") + Log.w(tag, "NowPlayingState is $state") _nowPlayingState.value = state _nowPlayingScreenData.value = NowPlayingScreenData( nowPlayingTitle = state.track?.title ?: "", @@ -482,9 +479,9 @@ constructor( // } // } val controllerJob = launch { - Log.w(TAG, "ControllerJob is running") + Log.w(tag, "ControllerJob is running") handler.controlState.collectLatest { - Log.w(TAG, "ControlState is $it") + Log.w(tag, "ControlState is $it") _controllerState.value = it } } @@ -508,7 +505,7 @@ constructor( // val media = it.second // if (media is SimpleMediaState.Ready && nowPlaying?.mediaItem != null) { // if (nowPlaying.mediaItem.isSong()) { -// Log.w(TAG, "Duration is ${media.duration}") +// Log.w(tag, "Duration is ${media.duration}") // getCanvas(nowPlaying.mediaItem.mediaId, media.duration.toInt()) // } // getLyricsFromFormat(nowPlaying.mediaItem.mediaId, media.duration.toInt()) @@ -580,7 +577,7 @@ constructor( if (dataStoreManager.spotifyCanvas.first() == TRUE){ mainRepository.getCanvas(videoId, duration).cancellable().collect { response -> _canvas.value = response - Log.w(TAG, "Canvas is $response") + Log.w(tag, "Canvas is $response") if (nowPlayingState.value?.mediaItem?.mediaId == videoId) { _nowPlayingScreenData.update { it.copy( @@ -745,7 +742,7 @@ constructor( mainRepository.getSavedLyrics(track.videoId).cancellable().collect { lyrics -> if (lyrics != null) { val lyricsData = lyrics.toLyrics() - Log.d(TAG, "Saved Lyrics $lyricsData") + Log.d(tag, "Saved Lyrics $lyricsData") updateLyrics( track.videoId, lyricsData, @@ -932,20 +929,19 @@ constructor( UIEvent.Repeat -> simpleMediaServiceHandler?.onPlayerEvent(PlayerEvent.Repeat) UIEvent.Shuffle -> simpleMediaServiceHandler?.onPlayerEvent(PlayerEvent.Shuffle) UIEvent.ToggleLike -> { - Log.w(TAG, "ToggleLike") + Log.w(tag, "ToggleLike") simpleMediaServiceHandler?.onPlayerEvent(PlayerEvent.ToggleLike) } } } - private var _listLocalPlaylist: MutableStateFlow> = - MutableStateFlow(listOf()) - val localPlaylist: StateFlow> = _listLocalPlaylist + private var _localPlaylist: MutableStateFlow> = MutableStateFlow(listOf()) + val localPlaylist: StateFlow> = _localPlaylist fun getAllLocalPlaylist() { viewModelScope.launch { mainRepository.getAllLocalPlaylists().collect { values -> - _listLocalPlaylist.emit(values) + _localPlaylist.emit(values) } } } @@ -1123,7 +1119,7 @@ constructor( getFormatFlowJob = viewModelScope.launch { if (mediaId != null) { mainRepository.getFormatFlow(mediaId).cancellable().collectLatest { f -> - Log.w(TAG, "Get format for "+ mediaId.toString() + ": " +f.toString()) + Log.w(tag, "Get format for "+ mediaId.toString() + ": " +f.toString()) if (f != null) { _format.emit(f) } else { @@ -1256,7 +1252,7 @@ constructor( ) { viewModelScope.launch { val videoId = song.videoId - Log.w(TAG, "Get Lyrics From Format for $videoId") + Log.w(tag, "Get Lyrics From Format for $videoId") if (dataStoreManager.lyricsProvider.first() == DataStoreManager.MUSIXMATCH) { val artist = if (song.artistName?.firstOrNull() != null && song.artistName.firstOrNull() @@ -1272,12 +1268,12 @@ constructor( song.title, duration, ).cancellable().collect { response -> - Log.w(TAG, response.second.data.toString()) + Log.w(tag, response.second.data.toString()) when (response.second) { is Resource.Success -> { if (response.second.data != null) { - Log.d(TAG, "Get Lyrics Data Success") + Log.d(tag, "Get Lyrics Data Success") updateLyrics( videoId, response.second.data, @@ -1295,7 +1291,7 @@ constructor( ).cancellable() .collect { translate -> if (translate != null) { - Log.d(TAG, "Get Translate Lyrics Success") + Log.d(tag, "Get Translate Lyrics Success") updateLyrics( videoId, translate.toLyrics( @@ -1318,7 +1314,7 @@ constructor( } is Resource.Error -> { - Log.w(TAG, "Get Lyrics Data Error") + Log.w(tag, "Get Lyrics Data Error") if (_lyrics.value?.message != "reset") { if (dataStoreManager.spotifyLyrics.first() == TRUE) { getSpotifyLyrics( @@ -1567,6 +1563,8 @@ constructor( } } + fun getTranslucentBottomBar() = dataStoreManager.translucentBottomBar + private var _homeRefresh: MutableStateFlow = MutableStateFlow(false) val homeRefresh: StateFlow = _homeRefresh.asStateFlow() diff --git a/app/src/main/java/com/maxrave/simpmusic/viewModel/base/BaseViewModel.kt b/app/src/main/java/com/maxrave/simpmusic/viewModel/base/BaseViewModel.kt new file mode 100644 index 00000000..18b60134 --- /dev/null +++ b/app/src/main/java/com/maxrave/simpmusic/viewModel/base/BaseViewModel.kt @@ -0,0 +1,31 @@ +package com.maxrave.simpmusic.viewModel.base + +import android.app.Application +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.viewModelScope +import com.maxrave.simpmusic.data.dataStore.DataStoreManager +import com.maxrave.simpmusic.data.repository.MainRepository +import kotlinx.coroutines.cancel +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject + +abstract class BaseViewModel( + application: Application, +) : AndroidViewModel(application), + KoinComponent { + protected val dataStoreManager: DataStoreManager by inject() + protected val mainRepository: MainRepository by inject() + + /** + * Tag for logging + */ + abstract val tag: String + + /** + * Cancel all jobs + */ + override fun onCleared() { + super.onCleared() + viewModelScope.cancel() + } +} \ No newline at end of file diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 5bab15ab..425c4a4c 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -187,7 +187,7 @@ استعراض موسيقى خارج الموضوع تسليط الضوء على النقاط الهامة - فلتر + فلتر SponsorBlock تمكين SponsorBlock حدد سلوك تخطي الجزء diff --git a/app/src/main/res/values-b+zh+Hant+TW/strings.xml b/app/src/main/res/values-b+zh+Hant+TW/strings.xml index ece7d5c1..d699fb40 100644 --- a/app/src/main/res/values-b+zh+Hant+TW/strings.xml +++ b/app/src/main/res/values-b+zh+Hant+TW/strings.xml @@ -187,7 +187,7 @@ 預覽 相關音樂 POI熱門 - 篩選器 + 篩選器 SponsorBlock 啟用 SponsorBlock 選擇跳過段行為 diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index bc4422f3..7c67c5dc 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -187,7 +187,7 @@ Vorschau Off-Topic Musik POI Highlight - Filter + Filter SponsorBlock SponsorBlock aktivieren Auswählen, welche Segmente übersprungen werden sollen diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 3a1effd9..557501f8 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -187,7 +187,7 @@ Vista previa Musica Off-topic Resaltar POI - Filtro + Filtro Bloqueo de patrocinadores Activar SponsorBlock Seleccionar segmentos a saltar diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index 0c0623c5..b6cb3603 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -187,7 +187,7 @@ Näyte Musiikki off-topic POI-nosto - Filter + Filter SponsorBlock Ota SponsorBlock käyttöön Segmenttien ohitus diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 90c34642..a577386f 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -187,7 +187,7 @@ Aperçu Musique hors sujet Surbrillance du POI - Filtre + Filtre SponsorBlock Activer SponsorBlock Sélectionnez le comportement du segment ignoré diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 6c2f34b1..9d86a7df 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -187,7 +187,7 @@ Pratinjau Musik di luar topik Sorotan POI - Saringan + Saringan Blokir Sponsor Aktifkan Blokir Sponsor Pilih segmen yg lewati diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 8a1bf618..35dea24b 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -187,7 +187,7 @@ Anteprima Musica fuori tema Evidenziare - Filtro + Filtro SponsorBlock Abilitare SponsorBlock Seleziona il comportamento di salto del segmento diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index 30852563..25bdb707 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -187,7 +187,7 @@ תצוגה מקדימה מוזיקה מחוץ לנושא הדגשה של POI - מיין + מיין חוסם החסויות הפעל חסימת חסות בחר התנהגות של דילוג על קטע diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index a111d5b1..56f0f9eb 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -187,7 +187,7 @@ プレビュー 音楽 オフトピック POIハイライト - フィルター + フィルター SponsorBlock SponsorBlockを有効にする スキップセグメントの動作を選択 diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 1985c060..64e33ae8 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -187,7 +187,7 @@ Podgląd Muzyka poza tematem Podświetlenie POI - Filtr + Filtr Blok sponsora Włącz SponsorBlock\'a Wybierz zachowanie segmentu pomijanego diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index d4229571..527c417b 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -187,7 +187,7 @@ Pré-visualização Música Off-topic Destaque de POI - Filtro + Filtro SponsorBlock Ativar o Sponsorblock Selecione o comportamento \"pula-segmentos\" diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index c659c01b..f5095deb 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -187,7 +187,7 @@ Предварительный просмотр Музыкальные оффтопики Яркие моменты POI - Фильтр + Фильтр SponsorBlock Включить SponsorBlock Выберите поведение пропуска-сегмента diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index f2c9505f..4efc8be8 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -187,7 +187,7 @@ Ön İzleme Müzik Konu Dışı İlgi Çekici Vurgu - Filtre + Filtre SponsorBlock SponsorBlock\'u etkinleştir Kısım atlama davranışını seçin diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index c0029b38..24b6990a 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -187,7 +187,7 @@ Попередній перегляд Музика не по темі Виділення POI - Фільтр + Фільтр SponsorBlock Увімкнути SponsorBlock Виберіть поведінку пропуску сегменту diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index a3f5ac23..ed405105 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -187,7 +187,7 @@ Xem trước Không phải âm nhạc Tóm tắt - Bộ lọc + Bộ lọc SponsorBlock Bật SponsorBlock Lựa chọn hành vi bỏ qua phân đoạn diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 64d722df..28bee07e 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -187,7 +187,7 @@ 预览 音乐离题 POI 高亮 - 筛选器 + 筛选器 SponsorBlock 启用 SponsorBlock 选择跳过区段行为 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4c099102..25922bbf 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -187,7 +187,7 @@ Preview Music Off-topic POI Highlight - Filter + Filter SponsorBlock Enable SponsorBlock Select skip-segment behavior diff --git a/build.gradle.kts b/build.gradle.kts index b91553f4..894a2e57 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,7 +7,6 @@ plugins { alias(libs.plugins.kotlin.android) apply false alias(libs.plugins.compose.compiler) apply false alias(libs.plugins.navigation.safeargs) apply false - alias(libs.plugins.hilt.android) apply false alias(libs.plugins.kotlin.serialization) apply false alias(libs.plugins.ksp) apply false alias(libs.plugins.aboutlibraries) apply false diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 25f8e162..f2fa68fa 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,7 +3,7 @@ android = "8.6.0" kotlin = "2.0.0" serialization = "2.0.0" ksp = "2.0.0-1.0.22" -compose-bom = "2024.06.00" +compose-bom = "2024.08.00" constraintlayout-compose = "1.0.1" activity-compose = "1.9.1" lifecycle-viewmodel-compose = "2.8.4" @@ -16,7 +16,7 @@ startup-runtime = "1.1.1" lifecycle-livedata-ktx = "2.8.4" lifecycle-viewmodel-ktx = "2.8.4" ui-tooling = "1.6.8" -media3 = "1.4.0" +media3 = "1.4.1" palette-ktx = "1.0.0" expandable-text = "2.0.0" constraintlayout = "2.1.4" @@ -33,8 +33,6 @@ coil = "2.6.0" easypermissions = "3.0.0" preference-ktx = "1.2.1" fragment-ktx = "1.8.2" -hilt = "2.50" -hilt-work = "1.2.0" datastore-preferences = "1.1.1" swiperefreshlayout = "1.2.0-alpha01" insetter = "0.6.1" @@ -52,7 +50,9 @@ insetsx = "0.1.0-alpha10" ktor = "2.3.12" brotli = "0.1.2" ksoup = "0.3.1" -desugaring = "2.0.4" +desugaring = "2.1.1" +koin-bom = "4.0.0-RC1" +koin-annotations = "1.4.0-RC4" [libraries] desugaring = { group = "com.android.tools", name = "desugar_jdk_libs", version.ref = "desugaring"} @@ -105,10 +105,6 @@ coil-compose = { group = "io.coil-kt", name = "coil-compose", version.ref = "coi easypermissions = { group = "pub.devrel", name = "easypermissions", version.ref = "easypermissions" } preference-ktx = { group = "androidx.preference", name = "preference-ktx", version.ref = "preference-ktx" } fragment-ktx = { group = "androidx.fragment", name = "fragment-ktx", version.ref = "fragment-ktx" } -hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" } -hilt-work = { group = "androidx.hilt", name = "hilt-work", version.ref = "hilt-work" } -hilt-compiler = { group = "androidx.hilt", name = "hilt-compiler", version.ref = "hilt-work" } -dagger-hilt-compiler = { group = "com.google.dagger", name = "hilt-compiler", version.ref = "hilt" } kotlinx-metadata-jvm = { group = "org.jetbrains.kotlinx", name = "kotlinx-metadata-jvm", version = "0.8.0" } datastore-preferences = { group = "androidx.datastore", name = "datastore-preferences", version.ref = "datastore-preferences" } swiperefreshlayout = { group = "androidx.swiperefreshlayout", name = "swiperefreshlayout", version.ref = "swiperefreshlayout" } @@ -143,6 +139,14 @@ brotli-dec = { group = "org.brotli", name = "dec", version.ref = "brotli" } kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect" } ksoup-html = { group = "com.mohamedrejeb.ksoup", name = "ksoup-html", version.ref = "ksoup" } ksoup-entities = { group = "com.mohamedrejeb.ksoup", name = "ksoup-entities", version.ref = "ksoup" } +#Koin +koin-bom = { module = "io.insert-koin:koin-bom", version.ref = "koin-bom" } +koin-core = { module = "io.insert-koin:koin-core" } +koin-androidx-compose = { module = "io.insert-koin:koin-androidx-compose" } +koin-android = { module = "io.insert-koin:koin-android"} +koin-workmanager = { module = "io.insert-koin:koin-androidx-workmanager" } +koin-annotations = { module = "io.insert-koin:koin-annotations", version.ref = "koin-annotations" } +koin-ksp = { module = "io.insert-koin:koin-ksp-compiler", version.ref = "koin-annotations"} [plugins] android-application = { id = "com.android.application", version.ref = "android" } @@ -150,7 +154,6 @@ android-library = { id = "com.android.library", version.ref = "android" } kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } navigation-safeargs = { id = "androidx.navigation.safeargs.kotlin", version.ref = "navigation" } -hilt-android = { id = "com.google.dagger.hilt.android", version.ref = "hilt" } kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "serialization" } ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } aboutlibraries = { id = "com.mikepenz.aboutlibraries.plugin", version.ref = "aboutlibraries" } \ No newline at end of file