mirror of
https://github.com/Shabinder/SpotiFlyer.git
synced 2024-12-22 20:57:54 +01:00
Service Integrated
This commit is contained in:
parent
5c0ec20e1b
commit
fd2a1cf46b
@ -56,6 +56,6 @@
|
||||
android:value="rzp_live_3ZQeoFYOxjmXye"
|
||||
/>
|
||||
|
||||
<service android:name=".worker.ForegroundService"/>
|
||||
</application>
|
||||
|
||||
</manifest>
|
@ -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
|
||||
|
@ -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<TrackDetails>)
|
||||
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) }))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -45,7 +45,7 @@ val mainActivity
|
||||
val sharedViewModel
|
||||
get() = MainActivity.getSharedViewModel()
|
||||
|
||||
fun loadAllImages(context: Context? = mainActivity, images:List<String>? = null,source: Source) {
|
||||
fun loadAllImages( images:List<String>? = null,source: Source, context: Context? = mainActivity) {
|
||||
val serviceIntent = Intent(context, ForegroundService::class.java)
|
||||
images?.let { serviceIntent.putStringArrayListExtra("imagesList",(it + source.name) as ArrayList<String>) }
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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<String>, func: ((resource:File) -> Unit)? = null) {
|
||||
suspend fun downloadAllImages(urlList: ArrayList<String>, 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<File> {
|
||||
override fun onLoadFailed(
|
||||
e: GlideException?,
|
||||
model: Any?,
|
||||
target: Target<File>?,
|
||||
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<File>?,
|
||||
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])
|
||||
|
@ -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}")}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user