diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 037487a5..4b129179 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -56,6 +56,6 @@
android:value="rzp_live_3ZQeoFYOxjmXye"
/>
+
-
\ No newline at end of file
diff --git a/app/src/main/java/com/shabinder/spotiflyer/MainActivity.kt b/app/src/main/java/com/shabinder/spotiflyer/MainActivity.kt
index 9c16eb8b..886af2ef 100644
--- a/app/src/main/java/com/shabinder/spotiflyer/MainActivity.kt
+++ b/app/src/main/java/com/shabinder/spotiflyer/MainActivity.kt
@@ -24,14 +24,11 @@ import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.unit.dp
import androidx.core.view.WindowCompat
import androidx.lifecycle.ViewModelProvider
-import androidx.lifecycle.viewModelScope
import androidx.navigation.NavHostController
import androidx.navigation.compose.rememberNavController
import com.example.jetcaster.util.verticalGradientScrim
-import com.shabinder.spotiflyer.models.spotify.Token
import com.shabinder.spotiflyer.navigation.ComposeNavigation
import com.shabinder.spotiflyer.navigation.navigateToTrackList
-import com.shabinder.spotiflyer.networking.SpotifyService
import com.shabinder.spotiflyer.networking.SpotifyServiceTokenRequest
import com.shabinder.spotiflyer.ui.ComposeLearnTheme
import com.shabinder.spotiflyer.ui.appNameStyle
@@ -41,17 +38,7 @@ import com.squareup.moshi.Moshi
import dagger.hilt.android.AndroidEntryPoint
import dev.chrisbanes.accompanist.insets.ProvideWindowInsets
import dev.chrisbanes.accompanist.insets.statusBarsHeight
-import kotlinx.coroutines.flow.collect
-import kotlinx.coroutines.flow.firstOrNull
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.runBlocking
-import okhttp3.Interceptor
-import okhttp3.OkHttpClient
-import okhttp3.Request
-import retrofit2.Retrofit
-import retrofit2.converter.moshi.MoshiConverterFactory
import javax.inject.Inject
-import com.shabinder.spotiflyer.utils.showDialog as showDialog1
/*
* This is App's God Activity
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 74f289a0..35cd38e3 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,6 +1,7 @@
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
@@ -25,14 +26,18 @@ import androidx.compose.ui.unit.sp
import androidx.core.net.toUri
import androidx.navigation.NavController
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
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
@@ -83,14 +88,35 @@ fun TrackList(
item {
CoverImage(it.title,it.coverUrl,coroutineScope)
}
- items(it.trackList) {
- TrackCard(track = it)
+ items(it.trackList) { item ->
+ TrackCard(
+ track = item,
+ onDownload = {
+ showDialog("Downloading ${it.title}")
+ downloadTracks(arrayListOf(it))
+ }
+ )
}
},
modifier = Modifier.fillMaxSize(),
)
DownloadAllButton(
- onClick = {},
+ onClick = {
+ loadAllImages(
+ it.trackList.map { it.albumArtURL },
+ Source.Spotify
+ )
+ val finalList = it.trackList.filter{it.downloaded == DownloadStatus.NotDownloaded}
+ if (finalList.isNullOrEmpty()) showDialog("Not Downloading Any Song")
+ else downloadTracks(finalList as ArrayList)
+ for (track in it.trackList) {
+ if (track.downloaded == DownloadStatus.NotDownloaded) {
+ track.downloaded = DownloadStatus.Queued
+ //adapter.notifyItemChanged(viewModel.trackList.value!!.indexOf(track))
+ }
+ }
+ showDialog("Downloading All Tracks")
+ },
modifier = Modifier.padding(bottom = 24.dp).align(Alignment.BottomCenter)
)
}
@@ -140,7 +166,10 @@ fun DownloadAllButton(onClick: () -> Unit, modifier: Modifier = Modifier) {
}
@Composable
-fun TrackCard(track:TrackDetails) {
+fun TrackCard(
+ track:TrackDetails,
+ onDownload:(TrackDetails)->Unit
+) {
Row(verticalAlignment = Alignment.CenterVertically,modifier = Modifier.fillMaxWidth().padding(horizontal = 8.dp)) {
val imgUri = track.albumArtURL.toUri().buildUpon().scheme("https").build()
CoilImage(
@@ -163,7 +192,7 @@ fun TrackCard(track:TrackDetails) {
Text("${track.durationSec/60} minutes, ${track.durationSec%60} sec",fontSize = 13.sp)
}
}
- Image(vectorResource(id = R.drawable.ic_arrow))
+ Image(vectorResource(id = R.drawable.ic_arrow), Modifier.clickable(onClick = { onDownload(track) }))
}
}
diff --git a/app/src/main/java/com/shabinder/spotiflyer/utils/Extensions.kt b/app/src/main/java/com/shabinder/spotiflyer/utils/Extensions.kt
index d7e2bb33..be8ef6ea 100644
--- a/app/src/main/java/com/shabinder/spotiflyer/utils/Extensions.kt
+++ b/app/src/main/java/com/shabinder/spotiflyer/utils/Extensions.kt
@@ -3,6 +3,7 @@ package com.shabinder.spotiflyer.utils
import android.Manifest
import android.content.Intent
import android.content.pm.PackageManager
+import android.graphics.Bitmap
import android.net.Uri
import android.os.Build
import android.util.Log
@@ -11,6 +12,7 @@ import com.github.kiulian.downloader.model.formats.Format
import com.github.kiulian.downloader.model.quality.AudioQuality
import com.shabinder.spotiflyer.BuildConfig
import com.shabinder.spotiflyer.MainActivity
+import java.io.File
/*
@@ -29,6 +31,12 @@ fun MainActivity.requestStoragePermission() {
)
}
}
+fun File.writeBitmap(bitmap: Bitmap, format: Bitmap.CompressFormat = Bitmap.CompressFormat.JPEG , quality: Int = 90) {
+ outputStream().use { out ->
+ bitmap.compress(format, quality, out)
+ out.flush()
+ }
+}
fun YoutubeVideo.getData(): Format?{
return try {
findAudioWithQuality(AudioQuality.medium)?.get(0) as Format
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 ddcfef8f..ed79cda5 100644
--- a/app/src/main/java/com/shabinder/spotiflyer/utils/Utils.kt
+++ b/app/src/main/java/com/shabinder/spotiflyer/utils/Utils.kt
@@ -45,7 +45,7 @@ val mainActivity
val sharedViewModel
get() = MainActivity.getSharedViewModel()
-fun loadAllImages(context: Context? = mainActivity, images:List? = null,source: Source) {
+fun loadAllImages( images:List? = null,source: Source, context: Context? = mainActivity) {
val serviceIntent = Intent(context, ForegroundService::class.java)
images?.let { serviceIntent.putStringArrayListExtra("imagesList",(it + source.name) as ArrayList) }
context?.let { ContextCompat.startForegroundService(it, serviceIntent) }
@@ -105,7 +105,9 @@ fun isOnline(): Boolean {
fun showDialog(title:String? = null, message: String? = null,response: String = "Ok"){
//TODO
- Toast.makeText(mainActivity,title ?: "No Internet",Toast.LENGTH_SHORT).show()
+ CoroutineScope(Dispatchers.Main).launch {
+ Toast.makeText(mainActivity,title ?: "No Internet",Toast.LENGTH_SHORT).show()
+ }
}
/**
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 06d65101..398028f0 100644
--- a/app/src/main/java/com/shabinder/spotiflyer/worker/ForegroundService.kt
+++ b/app/src/main/java/com/shabinder/spotiflyer/worker/ForegroundService.kt
@@ -20,6 +20,7 @@ package com.shabinder.spotiflyer.worker
import android.annotation.SuppressLint
import android.app.*
import android.app.DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED
+import android.app.PendingIntent.FLAG_CANCEL_CURRENT
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
@@ -28,21 +29,15 @@ import android.media.MediaScannerConnection
import android.net.Uri
import android.os.*
import androidx.annotation.RequiresApi
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.rounded.Cancel
-import androidx.compose.material.icons.rounded.CloudDownload
import androidx.core.app.NotificationCompat
+import androidx.core.graphics.drawable.toBitmap
import androidx.core.net.toUri
+import coil.Coil
+import coil.request.ImageRequest
import com.arthenica.mobileffmpeg.Config
import com.arthenica.mobileffmpeg.Config.RETURN_CODE_CANCEL
import com.arthenica.mobileffmpeg.Config.RETURN_CODE_SUCCESS
import com.arthenica.mobileffmpeg.FFmpeg
-import com.bumptech.glide.Glide
-import com.bumptech.glide.load.DataSource
-import com.bumptech.glide.load.engine.DiskCacheStrategy
-import com.bumptech.glide.load.engine.GlideException
-import com.bumptech.glide.request.RequestListener
-import com.bumptech.glide.request.target.Target
import com.github.kiulian.downloader.YoutubeDownloader
import com.mpatric.mp3agic.Mp3File
import com.shabinder.spotiflyer.R
@@ -64,7 +59,6 @@ import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import java.io.File
-import java.io.IOException
import java.util.*
import javax.inject.Inject
@@ -97,6 +91,7 @@ class ForegroundService : Service(){
override fun onBind(intent: Intent): IBinder? = null
+ @SuppressLint("UnspecifiedImmutableFlag")
override fun onCreate() {
super.onCreate()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
@@ -106,7 +101,7 @@ class ForegroundService : Service(){
this,
ForegroundService::class.java
).apply{action = "kill"}
- cancelIntent = PendingIntent.getService (this, 0 , intent , PendingIntent.FLAG_CANCEL_CURRENT )
+ cancelIntent = PendingIntent.getService (this, 0 , intent , FLAG_CANCEL_CURRENT )
downloadManager = getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
initialiseFetch()
}
@@ -535,75 +530,50 @@ class ForegroundService : Service(){
/**
* Function to fetch all Images for use in mp3 tags.
**/
- fun downloadAllImages(urlList: ArrayList, func: ((resource:File) -> Unit)? = null) {
+ suspend fun downloadAllImages(urlList: ArrayList, func: ((resource:File) -> Unit)? = null) {
/*
* Last Element of this List defines Its Source
* */
val source = urlList.last()
+
for (url in urlList.subList(0, urlList.size - 2)) {
- val imgUri = url.toUri().buildUpon().scheme("https").build()
- Glide
- .with(this@ForegroundService)
- .asFile()
- .diskCacheStrategy(DiskCacheStrategy.NONE)
- .load(imgUri)
- .listener(object : RequestListener {
- override fun onLoadFailed(
- e: GlideException?,
- model: Any?,
- target: Target?,
- isFirstResource: Boolean
- ): Boolean {
- log("Glide", "LoadFailed")
- return false
- }
+ withContext(Dispatchers.IO) {
+ val imgUri = url.toUri().buildUpon().scheme("https").build()
- override fun onResourceReady(
- resource: File?,
- model: Any?,
- target: Target?,
- dataSource: DataSource?,
- isFirstResource: Boolean
- ): Boolean {
- try {
- serviceScope.launch {
- 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"
- )
- }
+ val r = ImageRequest.Builder(this@ForegroundService)
+ .data(imgUri)
+ .build()
- else -> File(
- imageDir,
- url.substringAfterLast('/') + ".jpeg"
- )
- }
- resource?.copyTo(file)
- func?.let { it(file) }
- }
- } catch (e: IOException) {
- e.printStackTrace()
- }
- return false
+ val bitmap = Coil.execute(r).drawable?.toBitmap()
+ val file = when (source) {
+ Source.Spotify.name -> {
+ File(imageDir, url.substringAfterLast('/') + ".jpeg")
}
- }).submit()
+ 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) }
+ } else log("Foreground Service", "Album Art Could Not be Fetched")
+ }
}
}
@@ -660,7 +630,7 @@ class ForegroundService : Service(){
private fun getNotification():Notification = NotificationCompat.Builder(this, channelId).run {
setSmallIcon(R.drawable.ic_download_arrow)
setContentTitle("Total: $total Completed:$converted Failed:$failed")
- setNotificationSilent()
+ setSilent(true)
setStyle(
NotificationCompat.InboxStyle().run {
addLine(messageList[messageList.size - 1])
diff --git a/app/src/main/java/com/shabinder/spotiflyer/worker/ID3Tagging.kt b/app/src/main/java/com/shabinder/spotiflyer/worker/ID3Tagging.kt
index 040dc13f..90f18eb1 100644
--- a/app/src/main/java/com/shabinder/spotiflyer/worker/ID3Tagging.kt
+++ b/app/src/main/java/com/shabinder/spotiflyer/worker/ID3Tagging.kt
@@ -22,6 +22,8 @@ import com.mpatric.mp3agic.ID3v24Tag
import com.mpatric.mp3agic.Mp3File
import com.shabinder.spotiflyer.models.TrackDetails
import com.shabinder.spotiflyer.utils.log
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.launch
import java.io.FileInputStream
/**
@@ -59,12 +61,14 @@ fun setId3v2Tags(mp3file: Mp3File, track: TrackDetails,service: ForegroundServic
try {
//Image Still Not Downloaded!
//Lets Download Now and Write it into Album Art
- service.downloadAllImages(arrayListOf(track.albumArtURL, track.source.name)){
- val bytesArray = ByteArray(it.length().toInt())
- val fis = FileInputStream(it)
- fis.read(bytesArray) //read file into bytes[]
- fis.close()
- id3v2Tag.setAlbumImage(bytesArray, "image/jpeg")
+ GlobalScope.launch {
+ service.downloadAllImages(arrayListOf(track.albumArtURL, track.source.name)) {
+ val bytesArray = ByteArray(it.length().toInt())
+ val fis = FileInputStream(it)
+ fis.read(bytesArray) //read file into bytes[]
+ fis.close()
+ id3v2Tag.setAlbumImage(bytesArray, "image/jpeg")
+ }
}
}catch (e: Exception){log("Error", "Couldn't Write Mp3 Album Art, error: ${e.stackTrace}")}
}