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
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<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")
fun disableDozeMode() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {

View File

@ -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<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)
private set

View File

@ -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<TrackDetails>)
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)
}))
}
}
}
}

View File

@ -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) }

View File

@ -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")
}
}