Image Loading Sent to Background for smooth UI

This commit is contained in:
shabinder 2021-04-15 00:06:51 +05:30
parent c25837845e
commit c9696fa4aa
4 changed files with 84 additions and 90 deletions

View File

@ -98,9 +98,6 @@ class MainActivity : ComponentActivity(), PaymentResultListener {
permissionGranted = remember { mutableStateOf(true) } permissionGranted = remember { mutableStateOf(true) }
val view = LocalView.current val view = LocalView.current
LaunchedEffect(view) {
permissionGranted.value = checkPermissions()
}
Box { Box {
root = SpotiFlyerRootContent( root = SpotiFlyerRootContent(
rememberRootComponent(::spotiFlyerRoot), rememberRootComponent(::spotiFlyerRoot),
@ -114,6 +111,9 @@ class MainActivity : ComponentActivity(), PaymentResultListener {
) )
} }
LaunchedEffect(view) {
permissionGranted.value = checkPermissions()
}
NetworkDialog() NetworkDialog()
PermissionDialog() PermissionDialog()
} }

View File

@ -27,10 +27,7 @@ import com.mpatric.mp3agic.Mp3File
import com.shabinder.common.database.appContext import com.shabinder.common.database.appContext
import com.shabinder.common.models.TrackDetails import com.shabinder.common.models.TrackDetails
import com.shabinder.database.Database import com.shabinder.database.Database
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import java.io.File import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
import java.io.IOException import java.io.IOException
@ -76,12 +73,14 @@ actual class Dir actual constructor(
} }
actual suspend fun cacheImage(image: Any, path: String) { actual suspend fun cacheImage(image: Any, path: String) {
try { withContext(Dispatchers.IO){
FileOutputStream(path).use { out -> try {
(image as? Bitmap)?.compress(Bitmap.CompressFormat.JPEG, 100, out) FileOutputStream(path).use { out ->
(image as? Bitmap)?.compress(Bitmap.CompressFormat.JPEG, 100, out)
}
} catch (e: IOException) {
e.printStackTrace()
} }
} catch (e: IOException) {
e.printStackTrace()
} }
} }
@ -90,53 +89,55 @@ actual class Dir actual constructor(
mp3ByteArray: ByteArray, mp3ByteArray: ByteArray,
trackDetails: TrackDetails trackDetails: TrackDetails
) { ) {
val songFile = File(trackDetails.outputFilePath) withContext(Dispatchers.IO){
/* val songFile = File(trackDetails.outputFilePath)
* Check , if Fetch was Used, File is saved Already, else write byteArray we Received /*
* */ * Check , if Fetch was Used, File is saved Already, else write byteArray we Received
// if(!m4aFile.exists()) m4aFile.writeBytes(mp3ByteArray) * */
// if(!m4aFile.exists()) m4aFile.writeBytes(mp3ByteArray)
when (trackDetails.outputFilePath.substringAfterLast('.')) { when (trackDetails.outputFilePath.substringAfterLast('.')) {
".mp3" -> { ".mp3" -> {
Mp3File(File(songFile.absolutePath))
.removeAllTags()
.setId3v1Tags(trackDetails)
.setId3v2TagsAndSaveFile(trackDetails)
addToLibrary(songFile.absolutePath)
}
".m4a" -> {
/*FFmpeg.executeAsync(
"-i ${m4aFile.absolutePath} -y -b:a 160k -acodec libmp3lame -vn ${m4aFile.absolutePath.substringBeforeLast('.') + ".mp3"}"
){ _, returnCode ->
when (returnCode) {
Config.RETURN_CODE_SUCCESS -> {
//FFMPEG task Completed
logger.d{ "Async command execution completed successfully." }
scope.launch {
Mp3File(File(m4aFile.absolutePath.substringBeforeLast('.') + ".mp3"))
.removeAllTags()
.setId3v1Tags(trackDetails)
.setId3v2TagsAndSaveFile(trackDetails)
addToLibrary(m4aFile.absolutePath.substringBeforeLast('.') + ".mp3")
}
}
Config.RETURN_CODE_CANCEL -> {
logger.d{"Async command execution cancelled by user."}
}
else -> {
logger.d { "Async command execution failed with rc=$returnCode" }
}
}
}*/
}
else -> {
try {
Mp3File(File(songFile.absolutePath)) Mp3File(File(songFile.absolutePath))
.removeAllTags() .removeAllTags()
.setId3v1Tags(trackDetails) .setId3v1Tags(trackDetails)
.setId3v2TagsAndSaveFile(trackDetails) .setId3v2TagsAndSaveFile(trackDetails)
addToLibrary(songFile.absolutePath) addToLibrary(songFile.absolutePath)
} catch (e: Exception) { e.printStackTrace() } }
".m4a" -> {
/*FFmpeg.executeAsync(
"-i ${m4aFile.absolutePath} -y -b:a 160k -acodec libmp3lame -vn ${m4aFile.absolutePath.substringBeforeLast('.') + ".mp3"}"
){ _, returnCode ->
when (returnCode) {
Config.RETURN_CODE_SUCCESS -> {
//FFMPEG task Completed
logger.d{ "Async command execution completed successfully." }
scope.launch {
Mp3File(File(m4aFile.absolutePath.substringBeforeLast('.') + ".mp3"))
.removeAllTags()
.setId3v1Tags(trackDetails)
.setId3v2TagsAndSaveFile(trackDetails)
addToLibrary(m4aFile.absolutePath.substringBeforeLast('.') + ".mp3")
}
}
Config.RETURN_CODE_CANCEL -> {
logger.d{"Async command execution cancelled by user."}
}
else -> {
logger.d { "Async command execution failed with rc=$returnCode" }
}
}
}*/
}
else -> {
try {
Mp3File(File(songFile.absolutePath))
.removeAllTags()
.setId3v1Tags(trackDetails)
.setId3v2TagsAndSaveFile(trackDetails)
addToLibrary(songFile.absolutePath)
} catch (e: Exception) { e.printStackTrace() }
}
} }
} }
} }
@ -149,9 +150,9 @@ actual class Dir actual constructor(
) )
} }
actual suspend fun loadImage(url: String): Picture { actual suspend fun loadImage(url: String): Picture = withContext(Dispatchers.IO){
val cachePath = imageCacheDir() + getNameURL(url) val cachePath = imageCacheDir() + getNameURL(url)
return Picture(image = (loadCachedImage(cachePath) ?: freshImage(url))?.asImageBitmap()) Picture(image = (loadCachedImage(cachePath) ?: freshImage(url))?.asImageBitmap())
} }
private fun loadCachedImage(cachePath: String): Bitmap? { private fun loadCachedImage(cachePath: String): Bitmap? {
@ -162,8 +163,9 @@ actual class Dir actual constructor(
null null
} }
} }
private suspend fun freshImage(url: String): Bitmap? {
return try { private suspend fun freshImage(url: String): Bitmap? = withContext(Dispatchers.IO) {
try {
val source = URL(url) val source = URL(url)
val connection: HttpURLConnection = source.openConnection() as HttpURLConnection val connection: HttpURLConnection = source.openConnection() as HttpURLConnection
connection.connectTimeout = 5000 connection.connectTimeout = 5000

View File

@ -42,17 +42,15 @@ actual class YoutubeProvider actual constructor(
private val sampleDomain2 = "youtube.com" private val sampleDomain2 = "youtube.com"
private val sampleDomain3 = "youtu.be" private val sampleDomain3 = "youtu.be"
actual suspend fun query(fullLink: String): PlatformQueryResult? { actual suspend fun query(fullLink: String): PlatformQueryResult? = withContext(Dispatchers.IO) {
val link = fullLink.removePrefix("https://").removePrefix("http://") val link = fullLink.removePrefix("https://").removePrefix("http://")
if (link.contains("playlist", true) || link.contains("list", true)) { if (link.contains("playlist", true) || link.contains("list", true)) {
// Given Link is of a Playlist // Given Link is of a Playlist
logger.i { link } logger.i { link }
val playlistId = link.substringAfter("?list=").substringAfter("&list=").substringBefore("&").substringBefore("?") val playlistId = link.substringAfter("?list=").substringAfter("&list=").substringBefore("&").substringBefore("?")
return withContext(Dispatchers.IO) { getYTPlaylist(
getYTPlaylist( playlistId
playlistId )
)
}
} else { // Given Link is of a Video } else { // Given Link is of a Video
var searchId = "error" var searchId = "error"
when { when {
@ -66,12 +64,10 @@ actual class YoutubeProvider actual constructor(
searchId = link.substringAfterLast("/", "error").substringBefore("&") searchId = link.substringAfterLast("/", "error").substringBefore("&")
} }
} }
return if (searchId != "error") { if (searchId != "error") {
withContext(Dispatchers.IO) { getYTTrack(
getYTTrack( searchId
searchId )
)
}
} else { } else {
logger.d { "Your Youtube Link is not of a Video!!" } logger.d { "Your Youtube Link is not of a Video!!" }
null null
@ -81,7 +77,7 @@ actual class YoutubeProvider actual constructor(
private suspend fun getYTPlaylist( private suspend fun getYTPlaylist(
searchId: String searchId: String
): PlatformQueryResult? { ): PlatformQueryResult? = withContext(Dispatchers.IO) {
val result = PlatformQueryResult( val result = PlatformQueryResult(
folderType = "", folderType = "",
subFolder = "", subFolder = "",
@ -133,14 +129,13 @@ actual class YoutubeProvider actual constructor(
logger.d { "An Error Occurred While Processing!" } logger.d { "An Error Occurred While Processing!" }
} }
} }
return if (result.title.isNotBlank()) result if (result.title.isNotBlank()) result else null
else null
} }
@Suppress("DefaultLocale") @Suppress("DefaultLocale")
private suspend fun getYTTrack( private suspend fun getYTTrack(
searchId: String, searchId: String,
): PlatformQueryResult? { ): PlatformQueryResult? = withContext(Dispatchers.IO) {
val result = PlatformQueryResult( val result = PlatformQueryResult(
folderType = "", folderType = "",
subFolder = "", subFolder = "",
@ -188,7 +183,6 @@ actual class YoutubeProvider actual constructor(
logger.e { "An Error Occurred While Processing!,$searchId" } logger.e { "An Error Occurred While Processing!,$searchId" }
} }
} }
return if (result.title.isNotBlank()) result if (result.title.isNotBlank()) result else null
else null
} }
} }

View File

@ -173,7 +173,7 @@ class ForegroundService : Service(), CoroutineScope {
**/ **/
private fun downloadAllTracks(trackList: List<TrackDetails>) { private fun downloadAllTracks(trackList: List<TrackDetails>) {
trackList.forEach { trackList.forEach {
launch { launch(Dispatchers.IO) {
if (!it.videoID.isNullOrBlank()) { // Video ID already known! if (!it.videoID.isNullOrBlank()) { // Video ID already known!
downloadTrack(it.videoID!!, it) downloadTrack(it.videoID!!, it)
} else { } else {
@ -193,20 +193,18 @@ class ForegroundService : Service(), CoroutineScope {
} }
} }
private fun downloadTrack(videoID: String, track: TrackDetails) { private suspend fun downloadTrack(videoID: String, track: TrackDetails) {
launch { try {
try { val url = fetcher.youtubeMp3.getMp3DownloadLink(videoID)
val url = fetcher.youtubeMp3.getMp3DownloadLink(videoID) if (url == null) {
if (url == null) { val audioData: Format = ytDownloader.getVideo(videoID).getData() ?: throw Exception("Java YT Dependency Error")
val audioData: Format = ytDownloader.getVideo(videoID).getData() ?: throw Exception("Java YT Dependency Error") val ytUrl: String = audioData.url()
val ytUrl: String = audioData.url() enqueueDownload(ytUrl, track)
enqueueDownload(ytUrl, track) } else enqueueDownload(url, track)
} else enqueueDownload(url, track) } catch (e: Exception) {
} catch (e: Exception) { logger.d("Service YT Error") { e.message.toString() }
logger.d("Service YT Error") { e.message.toString() } sendTrackBroadcast(Status.FAILED.name, track)
sendTrackBroadcast(Status.FAILED.name, track) allTracksStatus[track.title] = DownloadStatus.Failed
allTracksStatus[track.title] = DownloadStatus.Failed
}
} }
} }