Progress bars Added

This commit is contained in:
shabinder 2021-01-02 16:25:49 +05:30
parent 0a2cd5400a
commit dd0fd06036
5 changed files with 191 additions and 67 deletions

View File

@ -1,8 +1,10 @@
package com.shabinder.spotiflyer package com.shabinder.spotiflyer
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
@ -27,6 +29,7 @@ import androidx.lifecycle.ViewModelProvider
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import com.example.jetcaster.util.verticalGradientScrim import com.example.jetcaster.util.verticalGradientScrim
import com.shabinder.spotiflyer.models.DownloadStatus
import com.shabinder.spotiflyer.navigation.ComposeNavigation import com.shabinder.spotiflyer.navigation.ComposeNavigation
import com.shabinder.spotiflyer.navigation.navigateToTrackList import com.shabinder.spotiflyer.navigation.navigateToTrackList
import com.shabinder.spotiflyer.networking.SpotifyServiceTokenRequest 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.ui.colorOffWhite
import com.shabinder.spotiflyer.utils.* import com.shabinder.spotiflyer.utils.*
import com.squareup.moshi.Moshi import com.squareup.moshi.Moshi
import com.tonyodev.fetch2.Status
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import dev.chrisbanes.accompanist.insets.ProvideWindowInsets import dev.chrisbanes.accompanist.insets.ProvideWindowInsets
import dev.chrisbanes.accompanist.insets.statusBarsHeight import dev.chrisbanes.accompanist.insets.statusBarsHeight
@ -47,6 +51,8 @@ import javax.inject.Inject
class MainActivity : AppCompatActivity() { class MainActivity : AppCompatActivity() {
private lateinit var navController: NavHostController private lateinit var navController: NavHostController
private lateinit var updateUIReceiver: BroadcastReceiver
private lateinit var queryReceiver: BroadcastReceiver
@Inject lateinit var moshi: Moshi @Inject lateinit var moshi: Moshi
@Inject lateinit var spotifyServiceTokenRequest: SpotifyServiceTokenRequest @Inject lateinit var spotifyServiceTokenRequest: SpotifyServiceTokenRequest
@ -102,6 +108,56 @@ class MainActivity : AppCompatActivity() {
handleIntentFromExternalActivity(intent) 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<String, DownloadStatus>?
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") @SuppressLint("BatteryLife")
fun disableDozeMode() { fun disableDozeMode() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {

View File

@ -17,21 +17,23 @@
package com.shabinder.spotiflyer package com.shabinder.spotiflyer
import androidx.compose.runtime.getValue import android.content.Intent
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.*
import androidx.compose.runtime.setValue
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.hilt.lifecycle.ViewModelInject import androidx.hilt.lifecycle.ViewModelInject
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import com.github.kiulian.downloader.YoutubeDownloader import com.github.kiulian.downloader.YoutubeDownloader
import com.shabinder.spotiflyer.database.DatabaseDAO 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.GaanaInterface
import com.shabinder.spotiflyer.networking.SpotifyService import com.shabinder.spotiflyer.networking.SpotifyService
import com.shabinder.spotiflyer.ui.colorPrimaryDark import com.shabinder.spotiflyer.ui.colorPrimaryDark
import dagger.hilt.android.scopes.ActivityRetainedScoped import com.shabinder.spotiflyer.utils.log
import kotlinx.coroutines.flow.MutableStateFlow import com.tonyodev.fetch2.Status
@ActivityRetainedScoped
class SharedViewModel @ViewModelInject constructor( class SharedViewModel @ViewModelInject constructor(
val databaseDAO: DatabaseDAO, val databaseDAO: DatabaseDAO,
val spotifyService: SpotifyService, val spotifyService: SpotifyService,
@ -45,6 +47,59 @@ class SharedViewModel @ViewModelInject constructor(
isAuthenticated = s isAuthenticated = s
} }
val trackList = mutableStateListOf<TrackDetails>()
fun updateTrackList(list:List<TrackDetails>){
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<TrackDetails?>("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) var gradientColor by mutableStateOf(colorPrimaryDark)
private set private set

View File

@ -1,19 +1,14 @@
package com.shabinder.spotiflyer.ui.tracklist package com.shabinder.spotiflyer.ui.tracklist
import androidx.compose.foundation.Image 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.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.foundation.clickable
import androidx.compose.runtime.remember import androidx.compose.foundation.layout.*
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip 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.DownloadStatus
import com.shabinder.spotiflyer.models.PlatformQueryResult import com.shabinder.spotiflyer.models.PlatformQueryResult
import com.shabinder.spotiflyer.models.TrackDetails import com.shabinder.spotiflyer.models.TrackDetails
import com.shabinder.spotiflyer.models.spotify.Source
import com.shabinder.spotiflyer.ui.SpotiFlyerTypography import com.shabinder.spotiflyer.ui.SpotiFlyerTypography
import com.shabinder.spotiflyer.ui.colorAccent import com.shabinder.spotiflyer.ui.colorAccent
import com.shabinder.spotiflyer.providers.queryGaana 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.providers.queryYoutube
import com.shabinder.spotiflyer.ui.utils.calculateDominantColor import com.shabinder.spotiflyer.ui.utils.calculateDominantColor
import com.shabinder.spotiflyer.utils.downloadTracks import com.shabinder.spotiflyer.utils.downloadTracks
import com.shabinder.spotiflyer.utils.loadAllImages
import com.shabinder.spotiflyer.utils.sharedViewModel import com.shabinder.spotiflyer.utils.sharedViewModel
import com.shabinder.spotiflyer.utils.showDialog import com.shabinder.spotiflyer.utils.showDialog
import dev.chrisbanes.accompanist.coil.CoilImage import dev.chrisbanes.accompanist.coil.CoilImage
@ -79,6 +72,7 @@ fun TrackList(
if(result == null) navController.popBackStack() if(result == null) navController.popBackStack()
} }
sharedViewModel.updateTrackList(result?.trackList ?: listOf())
result?.let{ result?.let{
Box(modifier = modifier.fillMaxSize()){ Box(modifier = modifier.fillMaxSize()){
@ -88,13 +82,13 @@ fun TrackList(
item { item {
CoverImage(it.title,it.coverUrl,coroutineScope) CoverImage(it.title,it.coverUrl,coroutineScope)
} }
items(it.trackList) { item -> itemsIndexed(sharedViewModel.trackList) { index, item ->
TrackCard( TrackCard(
track = item, track = item,
onDownload = { onDownload = {
showDialog("Downloading ${it.title}") downloadTracks(arrayListOf(item))
downloadTracks(arrayListOf(it)) sharedViewModel.updateTrackStatus(index,DownloadStatus.Queued)
} },
) )
} }
}, },
@ -102,17 +96,12 @@ fun TrackList(
) )
DownloadAllButton( DownloadAllButton(
onClick = { onClick = {
loadAllImages( val finalList = sharedViewModel.trackList.filter{it.downloaded == DownloadStatus.NotDownloaded}
it.trackList.map { it.albumArtURL },
Source.Spotify
)
val finalList = it.trackList.filter{it.downloaded == DownloadStatus.NotDownloaded}
if (finalList.isNullOrEmpty()) showDialog("Not Downloading Any Song") if (finalList.isNullOrEmpty()) showDialog("Not Downloading Any Song")
else downloadTracks(finalList as ArrayList<TrackDetails>) else downloadTracks(finalList as ArrayList<TrackDetails>)
for (track in it.trackList) { for (track in sharedViewModel.trackList) {
if (track.downloaded == DownloadStatus.NotDownloaded) { if (track.downloaded == DownloadStatus.NotDownloaded) {
track.downloaded = DownloadStatus.Queued track.downloaded = DownloadStatus.Queued
//adapter.notifyItemChanged(viewModel.trackList.value!!.indexOf(track))
} }
} }
showDialog("Downloading All Tracks") showDialog("Downloading All Tracks")
@ -168,7 +157,7 @@ fun DownloadAllButton(onClick: () -> Unit, modifier: Modifier = Modifier) {
@Composable @Composable
fun TrackCard( fun TrackCard(
track:TrackDetails, track:TrackDetails,
onDownload:(TrackDetails)->Unit onDownload:(TrackDetails)->Unit,
) { ) {
Row(verticalAlignment = Alignment.CenterVertically,modifier = Modifier.fillMaxWidth().padding(horizontal = 8.dp)) { Row(verticalAlignment = Alignment.CenterVertically,modifier = Modifier.fillMaxWidth().padding(horizontal = 8.dp)) {
val imgUri = track.albumArtURL.toUri().buildUpon().scheme("https").build() 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) 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)
}))
}
}
} }
} }

View File

@ -56,6 +56,10 @@ fun downloadTracks(
context: Context? = mainActivity context: Context? = mainActivity
) { ) {
if(!trackList.isNullOrEmpty()){ if(!trackList.isNullOrEmpty()){
loadAllImages(
trackList.map { it.albumArtURL },
trackList.first().source
)
val serviceIntent = Intent(context, ForegroundService::class.java) val serviceIntent = Intent(context, ForegroundService::class.java)
serviceIntent.putParcelableArrayListExtra("object",trackList) serviceIntent.putParcelableArrayListExtra("object",trackList)
context?.let { ContextCompat.startForegroundService(it, serviceIntent) } context?.let { ContextCompat.startForegroundService(it, serviceIntent) }

View File

@ -190,7 +190,6 @@ class ForegroundService : Service(){
trackDurationSec = it.durationSec trackDurationSec = it.durationSec
).keys.firstOrNull() ).keys.firstOrNull()
log("Service VideoID", videoId ?: "Not Found") log("Service VideoID", videoId ?: "Not Found")
//println(response.body().toString())
if (videoId.isNullOrBlank()) { if (videoId.isNullOrBlank()) {
sendTrackBroadcast(Status.FAILED.name, it) sendTrackBroadcast(Status.FAILED.name, it)
failed++ failed++
@ -536,9 +535,9 @@ class ForegroundService : Service(){
* Last Element of this List defines Its Source * Last Element of this List defines Its Source
* */ * */
val source = urlList.last() val source = urlList.last()
log("Image","Fetching All ")
for (url in urlList.subList(0, urlList.size - 2)) { for (url in urlList.subList(0, urlList.size - 1)) {
withContext(Dispatchers.IO) { log("Image","Fetching")
val imgUri = url.toUri().buildUpon().scheme("https").build() val imgUri = url.toUri().buildUpon().scheme("https").build()
val r = ImageRequest.Builder(this@ForegroundService) val r = ImageRequest.Builder(this@ForegroundService)
@ -573,10 +572,10 @@ class ForegroundService : Service(){
if (bitmap != null) { if (bitmap != null) {
file.writeBitmap(bitmap) file.writeBitmap(bitmap)
func?.let { it(file) } func?.let { it(file) }
log("Image","Saved")
} else log("Foreground Service", "Album Art Could Not be Fetched") } else log("Foreground Service", "Album Art Could Not be Fetched")
} }
} }
}
private fun killService() { private fun killService() {
serviceScope.launch{ serviceScope.launch{