Multiplatform Settings & Desktop App Fixes. Release Soon!

This commit is contained in:
shabinder 2021-05-08 02:16:50 +05:30
parent 5118aa10c4
commit b8c2ee5b47
18 changed files with 218 additions and 66 deletions

View File

@ -39,6 +39,7 @@ import com.shabinder.common.database.R
import com.shabinder.common.di.Picture
import com.shabinder.common.di.dispatcherIO
import com.shabinder.common.models.methods
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.withContext
@Composable
@ -138,8 +139,7 @@ actual fun RazorPay() = painterResource(R.drawable.ic_indian_rupee)
@Composable
actual fun Toast(
text: String,
visibility: MutableState<Boolean>,
flow: MutableStateFlow<String>,
duration: ToastDuration
) {
// We Have Android's Implementation of Toast so its just Empty

View File

@ -18,6 +18,8 @@
package com.shabinder.common.uikit
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.core.MutableTransitionState
import androidx.compose.animation.core.Spring.StiffnessLow
import androidx.compose.animation.core.animateDp
@ -26,6 +28,7 @@ import androidx.compose.animation.core.spring
import androidx.compose.animation.core.tween
import androidx.compose.animation.core.updateTransition
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@ -37,8 +40,12 @@ import androidx.compose.foundation.layout.size
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material.icons.rounded.ArrowBackIosNew
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@ -48,6 +55,7 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.arkivanov.decompose.extensions.compose.jetbrains.Children
import com.arkivanov.decompose.extensions.compose.jetbrains.animation.child.crossfadeScale
import com.arkivanov.decompose.extensions.compose.jetbrains.asState
import com.shabinder.common.root.SpotiFlyerRoot
import com.shabinder.common.root.SpotiFlyerRoot.Child
import com.shabinder.common.uikit.splash.Splash
@ -93,6 +101,10 @@ fun SpotiFlyerRootContent(component: SpotiFlyerRoot, modifier: Modifier = Modifi
contentTopPadding,
component
)
Toast(
flow = component.toastState,
duration = ToastDuration.Long
)
}
return component
}
@ -112,9 +124,13 @@ fun MainScreen(modifier: Modifier = Modifier, alpha: Float,topPadding: Dp = 0.dp
).then(modifier)
) {
val activeComponent = component.routerState.asState()
val callBacks = component.callBacks
AppBar(
backgroundColor = appBarColor,
setDownloadDirectory = component.callBacks::setDownloadDirectory,
onBackPressed = callBacks::popBackToHomeScreen ,
setDownloadDirectory = callBacks::setDownloadDirectory,
isBackButtonVisible = activeComponent.value.activeChild.instance is Child.List,
modifier = Modifier.fillMaxWidth()
)
Spacer(Modifier.padding(top = topPadding))
@ -130,16 +146,28 @@ fun MainScreen(modifier: Modifier = Modifier, alpha: Float,topPadding: Dp = 0.dp
}
}
@OptIn(ExperimentalAnimationApi::class)
@Composable
fun AppBar(
backgroundColor: Color,
onBackPressed:()->Unit,
setDownloadDirectory:()->Unit,
isBackButtonVisible: Boolean,
modifier: Modifier = Modifier
) {
TopAppBar(
backgroundColor = backgroundColor,
title = {
Row(verticalAlignment = Alignment.CenterVertically) {
AnimatedVisibility(isBackButtonVisible) {
Icon(
Icons.Rounded.ArrowBackIosNew,
contentDescription = "Back Button",
modifier = Modifier.clickable { onBackPressed() },
tint = Color.LightGray
)
Spacer(Modifier.padding(horizontal = 4.dp))
}
Image(
SpotiFlyerLogo(),
"SpotiFlyer Logo",

View File

@ -19,14 +19,14 @@ package com.shabinder.common.uikit
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import kotlinx.coroutines.flow.MutableStateFlow
enum class ToastDuration(val value: Int) {
Short(1000), Long(3000)
Short(1000), Long(2500)
}
@Composable
expect fun Toast(
text: String,
visibility: MutableState<Boolean> = mutableStateOf(false),
duration: ToastDuration = ToastDuration.Long
flow: MutableStateFlow<String>,
duration: ToastDuration
)

View File

@ -16,67 +16,76 @@
package com.shabinder.common.uikit
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutHorizontally
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.sizeIn
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
private val message: MutableState<String> = mutableStateOf("")
private val state: MutableState<Boolean> = mutableStateOf(false)
fun showToast(text: String) {
message.value = text
state.value = true
}
private var isShown: Boolean = false
@OptIn(ExperimentalAnimationApi::class)
@Composable
actual fun Toast(
text: String,
visibility: MutableState<Boolean>,
flow: MutableStateFlow<String>,
duration: ToastDuration
) {
if (isShown) {
return
}
if (visibility.value) {
isShown = true
val state = flow.collectAsState("")
val message = state.value
AnimatedVisibility (
visible = message != "",
enter = fadeIn() + slideInVertically(initialOffsetY = { it / 4 }),
exit = slideOutHorizontally(targetOffsetX = { it / 4 }) + fadeOut()
) {
Box(
modifier = Modifier.fillMaxSize().padding(bottom = 20.dp),
contentAlignment = Alignment.BottomCenter
modifier = Modifier.fillMaxSize().padding(bottom = 16.dp).padding(end = 16.dp),
contentAlignment = Alignment.BottomEnd
) {
Surface(
modifier = Modifier.size(300.dp, 70.dp),
modifier = Modifier.sizeIn(maxWidth = 250.dp,maxHeight = 80.dp),
color = Color(23, 23, 23),
shape = RoundedCornerShape(4.dp)
shape = RoundedCornerShape(8.dp),
border = BorderStroke(1.dp, colorOffWhite)
) {
Box(contentAlignment = Alignment.Center) {
Box(contentAlignment = Alignment.Center,modifier = Modifier.fillMaxSize()){
Text(
text = text,
color = Color(210, 210, 210)
text = message,
color = Color(210, 210, 210),
textAlign = TextAlign.Center,
overflow = TextOverflow.Ellipsis,
style = SpotiFlyerTypography.body2,
modifier = Modifier.padding(8.dp)
)
}
DisposableEffect(Unit) {
GlobalScope.launch {
delay(duration.value.toLong())
isShown = false
visibility.value = false
flow.value = ""
}
onDispose { }
}

View File

@ -32,6 +32,7 @@ kotlin {
implementation("org.jetbrains.kotlinx:atomicfu:0.16.1")
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.2.0")
implementation("com.shabinder.fuzzywuzzy:fuzzywuzzy:1.0")
implementation("com.russhwolf:multiplatform-settings-no-arg:0.7.6")
implementation(Extras.youtubeDownloader)
implementation(MVIKotlin.rx)
}
@ -49,7 +50,6 @@ kotlin {
dependencies {
implementation(compose.materialIconsExtended)
implementation(Extras.mp3agic)
//implementation(Extras.jaudioTagger)
}
}
jsMain {

View File

@ -16,13 +16,13 @@
package com.shabinder.common.di
import android.content.SharedPreferences
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.os.Environment
import androidx.compose.ui.graphics.asImageBitmap
import co.touchlab.kermit.Kermit
import com.mpatric.mp3agic.Mp3File
import com.russhwolf.settings.Settings
import com.shabinder.common.database.SpotiFlyerDatabase
import com.shabinder.common.models.TrackDetails
import com.shabinder.common.models.methods
@ -40,18 +40,14 @@ import java.net.URL
actual class Dir actual constructor(
private val logger: Kermit,
private val settings: Settings,
private val spotiFlyerDatabase: SpotiFlyerDatabase,
) {
companion object {
const val DirKey = "downloadDir"
}
// This Wont throw `NPE` as We will never pass null
private val sharedPreferences:SharedPreferences by lazy { methods.value.platformActions.sharedPreferences!! }
fun setDownloadDirectory(newBasePath:String){
sharedPreferences.edit().putString(DirKey,newBasePath).apply()
}
actual fun setDownloadDirectory(newBasePath:String) = settings.putString(DirKey,newBasePath)
@Suppress("DEPRECATION")
private val defaultBaseDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC).toString()
@ -61,8 +57,8 @@ actual class Dir actual constructor(
actual fun imageCacheDir(): String = methods.value.platformActions.imageCacheDir
// fun call in order to always access Updated Value
actual fun defaultDir(): String = sharedPreferences.getString(DirKey,defaultBaseDir)!! + File.separator +
"SpotiFlyer" + File.separator
actual fun defaultDir(): String = (settings.getStringOrNull(DirKey) ?: defaultBaseDir) +
File.separator + "SpotiFlyer" + File.separator
actual fun isPresent(path: String): Boolean = File(path).exists()

View File

@ -18,6 +18,7 @@ package com.shabinder.common.di
import co.touchlab.kermit.Kermit
import co.touchlab.stately.ensureNeverFrozen
import com.russhwolf.settings.Settings
import com.shabinder.common.database.databaseModule
import com.shabinder.common.database.getLogger
import com.shabinder.common.di.providers.GaanaProvider
@ -51,7 +52,8 @@ fun initKoin() = initKoin(enableNetworkLogs = false) { }
fun commonModule(enableNetworkLogs: Boolean) = module {
single { createHttpClient(enableNetworkLogs = enableNetworkLogs) }
single { Dir(get(), get()) }
single { Dir(get(), get(), get()) }
single { Settings() }
single { Kermit(getLogger()) }
single { TokenStore(get(), get()) }
single { YoutubeMusic(get(), get()) }

View File

@ -17,6 +17,8 @@
package com.shabinder.common.di
import co.touchlab.kermit.Kermit
import com.russhwolf.settings.Settings
import com.russhwolf.settings.SettingsListener
import com.shabinder.common.database.SpotiFlyerDatabase
import com.shabinder.common.di.utils.removeIllegalChars
import com.shabinder.common.models.DownloadResult
@ -32,6 +34,7 @@ import kotlin.math.roundToInt
expect class Dir (
logger: Kermit,
settings: Settings,
spotiFlyerDatabase: SpotiFlyerDatabase,
) {
val db: Database?
@ -40,6 +43,7 @@ expect class Dir (
fun defaultDir(): String
fun imageCacheDir(): String
fun createDirectory(dirPath: String)
fun setDownloadDirectory(newBasePath:String)
suspend fun cacheImage(image: Any, path: String) // in Android = ImageBitmap, Desktop = BufferedImage
suspend fun loadImage(url: String): Picture
suspend fun clearCache()

View File

@ -74,10 +74,15 @@ suspend fun downloadTrack(
youtubeMp3: YoutubeMp3
) {
try {
var link = youtubeMp3.getMp3DownloadLink(videoID)
val link = youtubeMp3.getMp3DownloadLink(videoID) ?: ytDownloader.getVideo(videoID).getData()?.url
if (link == null) {
link = ytDownloader.getVideo(videoID).getData()?.url ?: return
DownloadProgressFlow.emit(
DownloadProgressFlow.replayCache.getOrElse(
0
) { hashMapOf() }.apply { set(trackDetails.title, DownloadStatus.Failed) }
)
return
}
downloadFile(link).collect {
when (it) {

View File

@ -20,6 +20,7 @@ import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.asImageBitmap
import co.touchlab.kermit.Kermit
import com.mpatric.mp3agic.Mp3File
import com.russhwolf.settings.Settings
import com.shabinder.common.database.SpotiFlyerDatabase
import com.shabinder.common.models.TrackDetails
import com.shabinder.database.Database
@ -39,8 +40,12 @@ import javax.imageio.ImageIO
actual class Dir actual constructor(
private val logger: Kermit,
private val settings: Settings,
private val spotiFlyerDatabase: SpotiFlyerDatabase,
) {
companion object {
const val DirKey = "downloadDir"
}
init {
createDirectories()
@ -51,9 +56,13 @@ actual class Dir actual constructor(
actual fun imageCacheDir(): String = System.getProperty("user.home") +
fileSeparator() + "SpotiFlyer/.images" + fileSeparator()
actual fun defaultDir(): String = System.getProperty("user.home") + fileSeparator() +
private val defaultBaseDir = System.getProperty("user.home")!!
actual fun defaultDir(): String = (settings.getStringOrNull(DirKey) ?: defaultBaseDir) + fileSeparator() +
"SpotiFlyer" + fileSeparator()
actual fun setDownloadDirectory(newBasePath:String) = settings.putString(DirKey,newBasePath)
actual fun isPresent(path: String): Boolean = File(path).exists()
actual fun createDirectory(dirPath: String) {
@ -88,13 +97,68 @@ actual class Dir actual constructor(
trackDetails: TrackDetails,
postProcess:(track: TrackDetails)->Unit
) {
val file = File(trackDetails.outputFilePath)
file.writeBytes(mp3ByteArray)
val songFile = File(trackDetails.outputFilePath)
try {
/*
* Check , if Fetch was Used, File is saved Already, else write byteArray we Received
* */
if(!songFile.exists()) {
/*Make intermediate Dirs if they don't exist yet*/
songFile.parentFile.mkdirs()
}
Mp3File(file)
.removeAllTags()
.setId3v1Tags(trackDetails)
.setId3v2TagsAndSaveFile(trackDetails)
if(mp3ByteArray.isNotEmpty()) songFile.writeBytes(mp3ByteArray)
when (trackDetails.outputFilePath.substringAfterLast('.')) {
".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))
.removeAllTags()
.setId3v1Tags(trackDetails)
.setId3v2TagsAndSaveFile(trackDetails)
addToLibrary(songFile.absolutePath)
} catch (e: Exception) { e.printStackTrace() }
}
}
}catch (e:Exception){
withContext(Dispatchers.Main){
//Toast.makeText(appContext,"Could Not Create File:\n${songFile.absolutePath}",Toast.LENGTH_SHORT).show()
}
if(songFile.exists()) songFile.delete()
logger.e { "${songFile.absolutePath} could not be created" }
}
}
actual fun addToLibrary(path: String) {}

View File

@ -1,6 +1,7 @@
package com.shabinder.common.di
import co.touchlab.kermit.Kermit
import com.russhwolf.settings.Settings
import com.shabinder.common.database.SpotiFlyerDatabase
import com.shabinder.common.models.TrackDetails
import com.shabinder.database.Database
@ -20,18 +21,28 @@ import platform.Foundation.sendSynchronousRequest
import platform.Foundation.writeToFile
import platform.UIKit.UIImage
import platform.UIKit.UIImageJPEGRepresentation
import java.lang.System
actual class Dir actual constructor(
val logger: Kermit,
private val settings: Settings,
private val spotiFlyerDatabase: SpotiFlyerDatabase,
) {
companion object {
const val DirKey = "downloadDir"
}
actual fun isPresent(path: String): Boolean = NSFileManager.defaultManager.fileExistsAtPath(path)
actual fun fileSeparator(): String = "/"
private val defaultBaseDir = NSFileManager.defaultManager.URLForDirectory(NSMusicDirectory, NSUserDomainMask,null,true,null)!!.path!!
// TODO Error Handling
actual fun defaultDir(): String = defaultDirURL.path!! + fileSeparator()
actual fun defaultDir(): String = (settings.getStringOrNull(DirKey) ?: defaultBaseDir) +
fileSeparator() + "SpotiFlyer" + fileSeparator()
actual fun setDownloadDirectory(newBasePath:String) = settings.putString(DirKey,newBasePath)
private val defaultDirURL: NSURL by lazy {
val musicDir = NSFileManager.defaultManager.URLForDirectory(NSMusicDirectory, NSUserDomainMask,null,true,null)!!

View File

@ -17,6 +17,7 @@
package com.shabinder.common.di
import co.touchlab.kermit.Kermit
import com.russhwolf.settings.Settings
import com.shabinder.common.database.SpotiFlyerDatabase
import com.shabinder.common.di.gaana.corsApi
import com.shabinder.common.di.utils.removeIllegalChars
@ -33,8 +34,12 @@ import org.w3c.dom.ImageBitmap
actual class Dir actual constructor(
private val logger: Kermit,
private val settings: Settings,
private val spotiFlyerDatabase: SpotiFlyerDatabase,
) {
companion object {
const val DirKey = "downloadDir"
}
/*init {
createDirectories()

View File

@ -94,21 +94,22 @@ internal class SpotiFlyerListStoreProvider(
}
is Intent.StartDownloadAll -> {
val finalList =
intent.trackList.filter { it.downloaded == DownloadStatus.NotDownloaded }
if (finalList.isNullOrEmpty()) methods.value.showPopUpMessage("All Songs are Processed")
else downloadTracks(finalList, fetchQuery, dir)
val list = intent.trackList.map {
if (it.downloaded == DownloadStatus.NotDownloaded)
return@map it.copy(downloaded = DownloadStatus.Queued)
it
}
dispatch(Result.UpdateTrackList(list.updateTracksStatuses(downloadProgressFlow.replayCache.getOrElse(0) { hashMapOf() })))
val finalList =
intent.trackList.filter { it.downloaded == DownloadStatus.NotDownloaded }
if (finalList.isNullOrEmpty()) methods.value.showPopUpMessage("All Songs are Processed")
else downloadTracks(finalList, fetchQuery, dir)
}
is Intent.StartDownload -> {
downloadTracks(listOf(intent.track), fetchQuery, dir)
dispatch(Result.UpdateTrackItem(intent.track.copy(downloaded = DownloadStatus.Queued)))
downloadTracks(listOf(intent.track), fetchQuery, dir)
}
is Intent.RefreshTracksStatuses -> methods.value.queryActiveTracks()
}

View File

@ -30,12 +30,16 @@ import com.shabinder.common.root.SpotiFlyerRoot.Dependencies
import com.shabinder.common.root.callbacks.SpotiFlyerRootCallBacks
import com.shabinder.common.root.integration.SpotiFlyerRootImpl
import com.shabinder.database.Database
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
interface SpotiFlyerRoot {
val routerState: Value<RouterState<*, Child>>
val toastState: MutableStateFlow<String>
val callBacks: SpotiFlyerRootCallBacks
sealed class Child {

View File

@ -18,6 +18,7 @@ package com.shabinder.common.root.callbacks
interface SpotiFlyerRootCallBacks {
fun searchLink(link: String)
fun showToast(text:String)
fun popBackToHomeScreen()
fun setDownloadDirectory()
}

View File

@ -42,6 +42,7 @@ import com.shabinder.common.root.SpotiFlyerRoot.Dependencies
import com.shabinder.common.root.callbacks.SpotiFlyerRootCallBacks
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
internal class SpotiFlyerRootImpl(
@ -91,6 +92,8 @@ internal class SpotiFlyerRootImpl(
override val routerState: Value<RouterState<*, Child>> = router.state
override val toastState = MutableStateFlow("")
override val callBacks = object : SpotiFlyerRootCallBacks {
override fun searchLink(link: String) = onMainOutput(SpotiFlyerMain.Output.Search(link))
override fun popBackToHomeScreen() {
@ -98,6 +101,7 @@ internal class SpotiFlyerRootImpl(
it !is Configuration.Main
}
}
override fun showToast(text:String) { toastState.value = text }
override fun setDownloadDirectory() { actions.setDownloadDirectoryAction() }
}

View File

@ -40,10 +40,16 @@ kotlin {
implementation(project(":common:compose"))
implementation(project(":common:data-models"))
implementation(project(":common:root"))
// Decompose
implementation(Decompose.decompose)
implementation(Decompose.extensionsCompose)
// MVI
implementation(MVIKotlin.mvikotlin)
implementation(MVIKotlin.mvikotlinMain)
// Koin
implementation(Koin.core)
}
}
val jvmTest by getting
@ -55,6 +61,7 @@ compose.desktop {
mainClass = "MainKt"
description = "Music Downloader for Spotify, Gaana, Youtube Music."
nativeDistributions {
modules("java.sql", "java.security.jgss")
targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
packageName = "SpotiFlyer"
}

View File

@ -40,12 +40,12 @@ import com.shabinder.common.uikit.SpotiFlyerRootContent
import com.shabinder.common.uikit.SpotiFlyerShapes
import com.shabinder.common.uikit.SpotiFlyerTypography
import com.shabinder.common.uikit.colorOffWhite
import com.shabinder.common.uikit.showToast
import com.shabinder.database.Database
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
private val koin = initKoin(enableNetworkLogs = true).koin
private lateinit var showToast: (String)->Unit
fun main() {
@ -63,7 +63,8 @@ fun main() {
typography = SpotiFlyerTypography,
shapes = SpotiFlyerShapes
) {
SpotiFlyerRootContent(rememberRootComponent(factory = ::spotiFlyerRoot))
val root = SpotiFlyerRootContent(rememberRootComponent(factory = ::spotiFlyerRoot))
showToast = root.callBacks::showToast
}
}
}
@ -81,17 +82,27 @@ private fun spotiFlyerRoot(componentContext: ComponentContext): SpotiFlyerRoot =
override val actions = object: Actions {
override val platformActions = object : PlatformActions {}
override fun showPopUpMessage(string: String, long: Boolean) = showToast(string)
override fun showPopUpMessage(string: String, long: Boolean) {
if(::showToast.isInitialized){
showToast(string)
}
}
override fun setDownloadDirectoryAction() {}
override fun setDownloadDirectoryAction() {
showToast("TODO: Still needs to be Implemented")
}
override fun queryActiveTracks() {}
override fun giveDonation() {}
override fun giveDonation() {
}
override fun shareApp() {}
override fun openPlatform(packageID: String, platformLink: String) {}
override fun openPlatform(packageID: String, platformLink: String) {
showToast("TODO: Still needs to be Implemented")
}
override fun writeMp3Tags(trackDetails: TrackDetails) {/*IMPLEMENTED*/}