mirror of
https://github.com/Shabinder/SpotiFlyer.git
synced 2024-10-18 12:54:33 +02:00
Progress bars Added
This commit is contained in:
parent
0a2cd5400a
commit
dd0fd06036
@ -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) {
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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) }
|
||||||
|
@ -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{
|
||||||
|
Loading…
Reference in New Issue
Block a user