From dd0fd06036f63dd13200ac7bb94b2e7fa0347ca4 Mon Sep 17 00:00:00 2001 From: shabinder Date: Sat, 2 Jan 2021 16:25:49 +0530 Subject: [PATCH] Progress bars Added --- .../com/shabinder/spotiflyer/MainActivity.kt | 56 +++++++++++++++ .../shabinder/spotiflyer/SharedViewModel.kt | 67 +++++++++++++++-- .../spotiflyer/ui/tracklist/TrackList.kt | 60 +++++++++------- .../com/shabinder/spotiflyer/utils/Utils.kt | 4 ++ .../spotiflyer/worker/ForegroundService.kt | 71 +++++++++---------- 5 files changed, 191 insertions(+), 67 deletions(-) diff --git a/app/src/main/java/com/shabinder/spotiflyer/MainActivity.kt b/app/src/main/java/com/shabinder/spotiflyer/MainActivity.kt index 886af2ef..f4c782ee 100644 --- a/app/src/main/java/com/shabinder/spotiflyer/MainActivity.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/MainActivity.kt @@ -1,8 +1,10 @@ package com.shabinder.spotiflyer import android.annotation.SuppressLint +import android.content.BroadcastReceiver import android.content.Context import android.content.Intent +import android.content.IntentFilter import android.net.Uri import android.os.Build import android.os.Bundle @@ -27,6 +29,7 @@ import androidx.lifecycle.ViewModelProvider import androidx.navigation.NavHostController import androidx.navigation.compose.rememberNavController import com.example.jetcaster.util.verticalGradientScrim +import com.shabinder.spotiflyer.models.DownloadStatus import com.shabinder.spotiflyer.navigation.ComposeNavigation import com.shabinder.spotiflyer.navigation.navigateToTrackList import com.shabinder.spotiflyer.networking.SpotifyServiceTokenRequest @@ -35,6 +38,7 @@ import com.shabinder.spotiflyer.ui.appNameStyle import com.shabinder.spotiflyer.ui.colorOffWhite import com.shabinder.spotiflyer.utils.* import com.squareup.moshi.Moshi +import com.tonyodev.fetch2.Status import dagger.hilt.android.AndroidEntryPoint import dev.chrisbanes.accompanist.insets.ProvideWindowInsets import dev.chrisbanes.accompanist.insets.statusBarsHeight @@ -47,6 +51,8 @@ import javax.inject.Inject class MainActivity : AppCompatActivity() { private lateinit var navController: NavHostController + private lateinit var updateUIReceiver: BroadcastReceiver + private lateinit var queryReceiver: BroadcastReceiver @Inject lateinit var moshi: Moshi @Inject lateinit var spotifyServiceTokenRequest: SpotifyServiceTokenRequest @@ -102,6 +108,56 @@ class MainActivity : AppCompatActivity() { handleIntentFromExternalActivity(intent) } + private fun initializeBroadcast(){ + val intentFilter = IntentFilter().apply { + addAction(Status.QUEUED.name) + addAction(Status.FAILED.name) + addAction(Status.DOWNLOADING.name) + addAction("Progress") + addAction("Converting") + addAction("track_download_completed") + } + updateUIReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent?) { + //UI update here + if (intent != null) { + sharedViewModel.updateTrackStatus(intent) + } + } + } + val queryFilter = IntentFilter().apply { addAction("query_result") } + queryReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent?) { + //UI update here + if (intent != null){ + @Suppress("UNCHECKED_CAST") + val trackList = intent.getSerializableExtra("tracks") as HashMap? + trackList?.let { list -> + log("Service Response", "${list.size} Tracks Active") + for (it in list) { + val position: Int = sharedViewModel.trackList.map { it.title }.indexOf(it.key) + log("BroadCast Received","$position, ${it.value} , ${it.key}") + sharedViewModel.updateTrackStatus(position,it.value) + } + } + } + } + } + registerReceiver(updateUIReceiver, intentFilter) + registerReceiver(queryReceiver, queryFilter) + } + + override fun onResume() { + super.onResume() + initializeBroadcast() + } + + override fun onPause() { + super.onPause() + unregisterReceiver(updateUIReceiver) + unregisterReceiver(queryReceiver) + } + @SuppressLint("BatteryLife") fun disableDozeMode() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { diff --git a/app/src/main/java/com/shabinder/spotiflyer/SharedViewModel.kt b/app/src/main/java/com/shabinder/spotiflyer/SharedViewModel.kt index e9ef7ec5..be5428a9 100644 --- a/app/src/main/java/com/shabinder/spotiflyer/SharedViewModel.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/SharedViewModel.kt @@ -17,21 +17,23 @@ package com.shabinder.spotiflyer -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.setValue +import android.content.Intent +import androidx.compose.runtime.* import androidx.compose.ui.graphics.Color import androidx.hilt.lifecycle.ViewModelInject import androidx.lifecycle.ViewModel +import androidx.compose.runtime.getValue +import androidx.compose.runtime.setValue import com.github.kiulian.downloader.YoutubeDownloader import com.shabinder.spotiflyer.database.DatabaseDAO +import com.shabinder.spotiflyer.models.DownloadStatus +import com.shabinder.spotiflyer.models.TrackDetails import com.shabinder.spotiflyer.networking.GaanaInterface import com.shabinder.spotiflyer.networking.SpotifyService import com.shabinder.spotiflyer.ui.colorPrimaryDark -import dagger.hilt.android.scopes.ActivityRetainedScoped -import kotlinx.coroutines.flow.MutableStateFlow +import com.shabinder.spotiflyer.utils.log +import com.tonyodev.fetch2.Status -@ActivityRetainedScoped class SharedViewModel @ViewModelInject constructor( val databaseDAO: DatabaseDAO, val spotifyService: SpotifyService, @@ -45,6 +47,59 @@ class SharedViewModel @ViewModelInject constructor( isAuthenticated = s } + + val trackList = mutableStateListOf() + + fun updateTrackList(list:List){ + trackList.clear() + trackList.addAll(list) + } + fun updateTrackStatus(position:Int, status: DownloadStatus){ + if(position != -1){ + val track = trackList[position].apply { downloaded = status } + trackList[position] = track + } + } + + fun updateTrackStatus(intent: Intent){ + val trackDetails = intent.getParcelableExtra("track") + trackDetails?.let { + val position: Int = + trackList.map { trackState -> trackState.title }.indexOf(it.title) +// log("TrackList", trackList.value.joinToString("\n") { trackState -> trackState.title }) + log("BroadCast Received", "$position, ${intent.action} , ${it.title}") + if (position != -1) { + trackList.getOrNull(position)?.let{ track -> + when (intent.action) { + Status.QUEUED.name -> { + track.downloaded = DownloadStatus.Queued + } + Status.FAILED.name -> { + track.downloaded = DownloadStatus.Failed + } + Status.DOWNLOADING.name -> { + track.downloaded = DownloadStatus.Downloading + } + "Progress" -> { + //Progress Update + track.progress = intent.getIntExtra("progress", 0) + track.downloaded = DownloadStatus.Downloading + } + "Converting" -> { + //Progress Update + track.downloaded = DownloadStatus.Converting + } + "track_download_completed" -> { + track.downloaded = DownloadStatus.Downloaded + } + } + trackList[position] = track + log("SharedVM","TrackListUpdated") + } + } + } + } + var gradientColor by mutableStateOf(colorPrimaryDark) private set diff --git a/app/src/main/java/com/shabinder/spotiflyer/ui/tracklist/TrackList.kt b/app/src/main/java/com/shabinder/spotiflyer/ui/tracklist/TrackList.kt index 35cd38e3..021a920f 100644 --- a/app/src/main/java/com/shabinder/spotiflyer/ui/tracklist/TrackList.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/ui/tracklist/TrackList.kt @@ -1,19 +1,14 @@ package com.shabinder.spotiflyer.ui.tracklist import androidx.compose.foundation.Image -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.material.ExtendedFloatingActionButton -import androidx.compose.material.Icon -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.setValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material.* +import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -29,7 +24,6 @@ import com.shabinder.spotiflyer.R import com.shabinder.spotiflyer.models.DownloadStatus import com.shabinder.spotiflyer.models.PlatformQueryResult import com.shabinder.spotiflyer.models.TrackDetails -import com.shabinder.spotiflyer.models.spotify.Source import com.shabinder.spotiflyer.ui.SpotiFlyerTypography import com.shabinder.spotiflyer.ui.colorAccent import com.shabinder.spotiflyer.providers.queryGaana @@ -37,7 +31,6 @@ import com.shabinder.spotiflyer.providers.querySpotify import com.shabinder.spotiflyer.providers.queryYoutube import com.shabinder.spotiflyer.ui.utils.calculateDominantColor import com.shabinder.spotiflyer.utils.downloadTracks -import com.shabinder.spotiflyer.utils.loadAllImages import com.shabinder.spotiflyer.utils.sharedViewModel import com.shabinder.spotiflyer.utils.showDialog import dev.chrisbanes.accompanist.coil.CoilImage @@ -79,6 +72,7 @@ fun TrackList( if(result == null) navController.popBackStack() } + sharedViewModel.updateTrackList(result?.trackList ?: listOf()) result?.let{ Box(modifier = modifier.fillMaxSize()){ @@ -88,13 +82,13 @@ fun TrackList( item { CoverImage(it.title,it.coverUrl,coroutineScope) } - items(it.trackList) { item -> + itemsIndexed(sharedViewModel.trackList) { index, item -> TrackCard( track = item, onDownload = { - showDialog("Downloading ${it.title}") - downloadTracks(arrayListOf(it)) - } + downloadTracks(arrayListOf(item)) + sharedViewModel.updateTrackStatus(index,DownloadStatus.Queued) + }, ) } }, @@ -102,17 +96,12 @@ fun TrackList( ) DownloadAllButton( onClick = { - loadAllImages( - it.trackList.map { it.albumArtURL }, - Source.Spotify - ) - val finalList = it.trackList.filter{it.downloaded == DownloadStatus.NotDownloaded} + val finalList = sharedViewModel.trackList.filter{it.downloaded == DownloadStatus.NotDownloaded} if (finalList.isNullOrEmpty()) showDialog("Not Downloading Any Song") else downloadTracks(finalList as ArrayList) - for (track in it.trackList) { + for (track in sharedViewModel.trackList) { if (track.downloaded == DownloadStatus.NotDownloaded) { track.downloaded = DownloadStatus.Queued - //adapter.notifyItemChanged(viewModel.trackList.value!!.indexOf(track)) } } showDialog("Downloading All Tracks") @@ -168,7 +157,7 @@ fun DownloadAllButton(onClick: () -> Unit, modifier: Modifier = Modifier) { @Composable fun TrackCard( track:TrackDetails, - onDownload:(TrackDetails)->Unit + onDownload:(TrackDetails)->Unit, ) { Row(verticalAlignment = Alignment.CenterVertically,modifier = Modifier.fillMaxWidth().padding(horizontal = 8.dp)) { val imgUri = track.albumArtURL.toUri().buildUpon().scheme("https").build() @@ -192,7 +181,28 @@ fun TrackCard( Text("${track.durationSec/60} minutes, ${track.durationSec%60} sec",fontSize = 13.sp) } } - Image(vectorResource(id = R.drawable.ic_arrow), Modifier.clickable(onClick = { onDownload(track) })) + when(track.downloaded){ + DownloadStatus.Downloaded -> { + Image(vectorResource(id = R.drawable.ic_tick)) + } + DownloadStatus.Queued -> { + CircularProgressIndicator() + } + DownloadStatus.Failed -> { + Image(vectorResource(id = R.drawable.ic_error)) + } + DownloadStatus.Downloading -> { + CircularProgressIndicator(progress = track.progress.toFloat()/100f) + } + DownloadStatus.Converting -> { + CircularProgressIndicator(progress = 100f,color = colorAccent) + } + DownloadStatus.NotDownloaded -> { + Image(vectorResource(id = R.drawable.ic_arrow), Modifier.clickable(onClick = { + onDownload(track) + })) + } + } } } diff --git a/app/src/main/java/com/shabinder/spotiflyer/utils/Utils.kt b/app/src/main/java/com/shabinder/spotiflyer/utils/Utils.kt index ed79cda5..3271685e 100644 --- a/app/src/main/java/com/shabinder/spotiflyer/utils/Utils.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/utils/Utils.kt @@ -56,6 +56,10 @@ fun downloadTracks( context: Context? = mainActivity ) { if(!trackList.isNullOrEmpty()){ + loadAllImages( + trackList.map { it.albumArtURL }, + trackList.first().source + ) val serviceIntent = Intent(context, ForegroundService::class.java) serviceIntent.putParcelableArrayListExtra("object",trackList) context?.let { ContextCompat.startForegroundService(it, serviceIntent) } diff --git a/app/src/main/java/com/shabinder/spotiflyer/worker/ForegroundService.kt b/app/src/main/java/com/shabinder/spotiflyer/worker/ForegroundService.kt index 60212514..2a98e580 100644 --- a/app/src/main/java/com/shabinder/spotiflyer/worker/ForegroundService.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/worker/ForegroundService.kt @@ -190,7 +190,6 @@ class ForegroundService : Service(){ trackDurationSec = it.durationSec ).keys.firstOrNull() log("Service VideoID", videoId ?: "Not Found") - //println(response.body().toString()) if (videoId.isNullOrBlank()) { sendTrackBroadcast(Status.FAILED.name, it) failed++ @@ -536,45 +535,45 @@ class ForegroundService : Service(){ * Last Element of this List defines Its Source * */ val source = urlList.last() + log("Image","Fetching All ") + for (url in urlList.subList(0, urlList.size - 1)) { + log("Image","Fetching") + val imgUri = url.toUri().buildUpon().scheme("https").build() - for (url in urlList.subList(0, urlList.size - 2)) { - withContext(Dispatchers.IO) { - val imgUri = url.toUri().buildUpon().scheme("https").build() + val r = ImageRequest.Builder(this@ForegroundService) + .data(imgUri) + .build() - val r = ImageRequest.Builder(this@ForegroundService) - .data(imgUri) - .build() - - val bitmap = Coil.execute(r).drawable?.toBitmap() - val file = when (source) { - Source.Spotify.name -> { - File(imageDir, url.substringAfterLast('/') + ".jpeg") - } - Source.YouTube.name -> { - File( - imageDir, - url.substringBeforeLast('/', url) - .substringAfterLast( - '/', - url - ) + ".jpeg" - ) - } - Source.Gaana.name -> { - File( - imageDir, - (url.substringBeforeLast('/').substringAfterLast( - '/' - )) + ".jpeg" - ) - } - else -> File(imageDir, url.substringAfterLast('/') + ".jpeg") + val bitmap = Coil.execute(r).drawable?.toBitmap() + val file = when (source) { + Source.Spotify.name -> { + File(imageDir, url.substringAfterLast('/') + ".jpeg") } - if (bitmap != null) { - file.writeBitmap(bitmap) - func?.let { it(file) } - } else log("Foreground Service", "Album Art Could Not be Fetched") + Source.YouTube.name -> { + File( + imageDir, + url.substringBeforeLast('/', url) + .substringAfterLast( + '/', + url + ) + ".jpeg" + ) + } + Source.Gaana.name -> { + File( + imageDir, + (url.substringBeforeLast('/').substringAfterLast( + '/' + )) + ".jpeg" + ) + } + else -> File(imageDir, url.substringAfterLast('/') + ".jpeg") } + if (bitmap != null) { + file.writeBitmap(bitmap) + func?.let { it(file) } + log("Image","Saved") + } else log("Foreground Service", "Album Art Could Not be Fetched") } }