Service Integrated

This commit is contained in:
shabinder 2021-01-02 01:32:26 +05:30
parent 5c0ec20e1b
commit fd2a1cf46b
7 changed files with 100 additions and 100 deletions

View File

@ -56,6 +56,6 @@
android:value="rzp_live_3ZQeoFYOxjmXye" android:value="rzp_live_3ZQeoFYOxjmXye"
/> />
<service android:name=".worker.ForegroundService"/>
</application> </application>
</manifest> </manifest>

View File

@ -24,14 +24,11 @@ import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.core.view.WindowCompat import androidx.core.view.WindowCompat
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
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.spotify.Token
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.SpotifyService
import com.shabinder.spotiflyer.networking.SpotifyServiceTokenRequest import com.shabinder.spotiflyer.networking.SpotifyServiceTokenRequest
import com.shabinder.spotiflyer.ui.ComposeLearnTheme import com.shabinder.spotiflyer.ui.ComposeLearnTheme
import com.shabinder.spotiflyer.ui.appNameStyle import com.shabinder.spotiflyer.ui.appNameStyle
@ -41,17 +38,7 @@ import com.squareup.moshi.Moshi
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
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 javax.inject.Inject
import com.shabinder.spotiflyer.utils.showDialog as showDialog1
/* /*
* This is App's God Activity * This is App's God Activity

View File

@ -1,6 +1,7 @@
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.layout.*
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.ExtendedFloatingActionButton import androidx.compose.material.ExtendedFloatingActionButton
@ -25,14 +26,18 @@ import androidx.compose.ui.unit.sp
import androidx.core.net.toUri import androidx.core.net.toUri
import androidx.navigation.NavController import androidx.navigation.NavController
import com.shabinder.spotiflyer.R import com.shabinder.spotiflyer.R
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
import com.shabinder.spotiflyer.providers.querySpotify 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.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
@ -83,14 +88,35 @@ fun TrackList(
item { item {
CoverImage(it.title,it.coverUrl,coroutineScope) CoverImage(it.title,it.coverUrl,coroutineScope)
} }
items(it.trackList) { items(it.trackList) { item ->
TrackCard(track = it) TrackCard(
track = item,
onDownload = {
showDialog("Downloading ${it.title}")
downloadTracks(arrayListOf(it))
}
)
} }
}, },
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
) )
DownloadAllButton( 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) modifier = Modifier.padding(bottom = 24.dp).align(Alignment.BottomCenter)
) )
} }
@ -140,7 +166,10 @@ fun DownloadAllButton(onClick: () -> Unit, modifier: Modifier = Modifier) {
} }
@Composable @Composable
fun TrackCard(track:TrackDetails) { fun TrackCard(
track:TrackDetails,
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()
CoilImage( CoilImage(
@ -163,7 +192,7 @@ fun TrackCard(track:TrackDetails) {
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)) Image(vectorResource(id = R.drawable.ic_arrow), Modifier.clickable(onClick = { onDownload(track) }))
} }
} }

View File

@ -3,6 +3,7 @@ package com.shabinder.spotiflyer.utils
import android.Manifest import android.Manifest
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.util.Log 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.github.kiulian.downloader.model.quality.AudioQuality
import com.shabinder.spotiflyer.BuildConfig import com.shabinder.spotiflyer.BuildConfig
import com.shabinder.spotiflyer.MainActivity 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?{ fun YoutubeVideo.getData(): Format?{
return try { return try {
findAudioWithQuality(AudioQuality.medium)?.get(0) as Format findAudioWithQuality(AudioQuality.medium)?.get(0) as Format

View File

@ -45,7 +45,7 @@ val mainActivity
val sharedViewModel val sharedViewModel
get() = MainActivity.getSharedViewModel() 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) val serviceIntent = Intent(context, ForegroundService::class.java)
images?.let { serviceIntent.putStringArrayListExtra("imagesList",(it + source.name) as ArrayList<String>) } images?.let { serviceIntent.putStringArrayListExtra("imagesList",(it + source.name) as ArrayList<String>) }
context?.let { ContextCompat.startForegroundService(it, serviceIntent) } context?.let { ContextCompat.startForegroundService(it, serviceIntent) }
@ -105,7 +105,9 @@ fun isOnline(): Boolean {
fun showDialog(title:String? = null, message: String? = null,response: String = "Ok"){ fun showDialog(title:String? = null, message: String? = null,response: String = "Ok"){
//TODO //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()
}
} }
/** /**

View File

@ -20,6 +20,7 @@ package com.shabinder.spotiflyer.worker
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.* import android.app.*
import android.app.DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED import android.app.DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED
import android.app.PendingIntent.FLAG_CANCEL_CURRENT
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
@ -28,21 +29,15 @@ import android.media.MediaScannerConnection
import android.net.Uri import android.net.Uri
import android.os.* import android.os.*
import androidx.annotation.RequiresApi 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.app.NotificationCompat
import androidx.core.graphics.drawable.toBitmap
import androidx.core.net.toUri import androidx.core.net.toUri
import coil.Coil
import coil.request.ImageRequest
import com.arthenica.mobileffmpeg.Config import com.arthenica.mobileffmpeg.Config
import com.arthenica.mobileffmpeg.Config.RETURN_CODE_CANCEL import com.arthenica.mobileffmpeg.Config.RETURN_CODE_CANCEL
import com.arthenica.mobileffmpeg.Config.RETURN_CODE_SUCCESS import com.arthenica.mobileffmpeg.Config.RETURN_CODE_SUCCESS
import com.arthenica.mobileffmpeg.FFmpeg 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.github.kiulian.downloader.YoutubeDownloader
import com.mpatric.mp3agic.Mp3File import com.mpatric.mp3agic.Mp3File
import com.shabinder.spotiflyer.R import com.shabinder.spotiflyer.R
@ -64,7 +59,6 @@ import retrofit2.Call
import retrofit2.Callback import retrofit2.Callback
import retrofit2.Response import retrofit2.Response
import java.io.File import java.io.File
import java.io.IOException
import java.util.* import java.util.*
import javax.inject.Inject import javax.inject.Inject
@ -97,6 +91,7 @@ class ForegroundService : Service(){
override fun onBind(intent: Intent): IBinder? = null override fun onBind(intent: Intent): IBinder? = null
@SuppressLint("UnspecifiedImmutableFlag")
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
@ -106,7 +101,7 @@ class ForegroundService : Service(){
this, this,
ForegroundService::class.java ForegroundService::class.java
).apply{action = "kill"} ).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 downloadManager = getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
initialiseFetch() initialiseFetch()
} }
@ -535,75 +530,50 @@ class ForegroundService : Service(){
/** /**
* Function to fetch all Images for use in mp3 tags. * 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 * Last Element of this List defines Its Source
* */ * */
val source = urlList.last() val source = urlList.last()
for (url in urlList.subList(0, urlList.size - 2)) { for (url in urlList.subList(0, urlList.size - 2)) {
val imgUri = url.toUri().buildUpon().scheme("https").build() withContext(Dispatchers.IO) {
Glide val imgUri = url.toUri().buildUpon().scheme("https").build()
.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
}
override fun onResourceReady( val r = ImageRequest.Builder(this@ForegroundService)
resource: File?, .data(imgUri)
model: Any?, .build()
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"
)
}
else -> File( val bitmap = Coil.execute(r).drawable?.toBitmap()
imageDir, val file = when (source) {
url.substringAfterLast('/') + ".jpeg" Source.Spotify.name -> {
) File(imageDir, url.substringAfterLast('/') + ".jpeg")
}
resource?.copyTo(file)
func?.let { it(file) }
}
} catch (e: IOException) {
e.printStackTrace()
}
return false
} }
}).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 { private fun getNotification():Notification = NotificationCompat.Builder(this, channelId).run {
setSmallIcon(R.drawable.ic_download_arrow) setSmallIcon(R.drawable.ic_download_arrow)
setContentTitle("Total: $total Completed:$converted Failed:$failed") setContentTitle("Total: $total Completed:$converted Failed:$failed")
setNotificationSilent() setSilent(true)
setStyle( setStyle(
NotificationCompat.InboxStyle().run { NotificationCompat.InboxStyle().run {
addLine(messageList[messageList.size - 1]) addLine(messageList[messageList.size - 1])

View File

@ -22,6 +22,8 @@ import com.mpatric.mp3agic.ID3v24Tag
import com.mpatric.mp3agic.Mp3File import com.mpatric.mp3agic.Mp3File
import com.shabinder.spotiflyer.models.TrackDetails import com.shabinder.spotiflyer.models.TrackDetails
import com.shabinder.spotiflyer.utils.log import com.shabinder.spotiflyer.utils.log
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import java.io.FileInputStream import java.io.FileInputStream
/** /**
@ -59,12 +61,14 @@ fun setId3v2Tags(mp3file: Mp3File, track: TrackDetails,service: ForegroundServic
try { try {
//Image Still Not Downloaded! //Image Still Not Downloaded!
//Lets Download Now and Write it into Album Art //Lets Download Now and Write it into Album Art
service.downloadAllImages(arrayListOf(track.albumArtURL, track.source.name)){ GlobalScope.launch {
val bytesArray = ByteArray(it.length().toInt()) service.downloadAllImages(arrayListOf(track.albumArtURL, track.source.name)) {
val fis = FileInputStream(it) val bytesArray = ByteArray(it.length().toInt())
fis.read(bytesArray) //read file into bytes[] val fis = FileInputStream(it)
fis.close() fis.read(bytesArray) //read file into bytes[]
id3v2Tag.setAlbumImage(bytesArray, "image/jpeg") fis.close()
id3v2Tag.setAlbumImage(bytesArray, "image/jpeg")
}
} }
}catch (e: Exception){log("Error", "Couldn't Write Mp3 Album Art, error: ${e.stackTrace}")} }catch (e: Exception){log("Error", "Couldn't Write Mp3 Album Art, error: ${e.stackTrace}")}
} }