mirror of
https://github.com/Shabinder/SpotiFlyer.git
synced 2024-12-22 12:47:54 +01:00
Code Changes for Native, IOS app builds, gradle scripts changes
This commit is contained in:
parent
a14181b968
commit
751ba3512e
@ -20,6 +20,7 @@ import org.jetbrains.compose.compose
|
||||
plugins {
|
||||
id("com.android.application")
|
||||
kotlin("android")
|
||||
id("kotlin-parcelize")
|
||||
id("org.jetbrains.compose")
|
||||
id("com.google.gms.google-services")
|
||||
id("com.google.firebase.crashlytics")
|
||||
@ -96,37 +97,42 @@ dependencies {
|
||||
implementation(compose.materialIconsExtended)
|
||||
implementation(Androidx.androidxActivity)
|
||||
|
||||
// Project's SubModules
|
||||
implementation(project(":common:database"))
|
||||
implementation(project(":common:compose"))
|
||||
implementation(project(":common:root"))
|
||||
implementation(project(":common:dependency-injection"))
|
||||
implementation(project(":common:data-models"))
|
||||
|
||||
// Koin
|
||||
implementation(Koin.android)
|
||||
implementation(Koin.compose)
|
||||
|
||||
implementation("com.google.accompanist:accompanist-insets:0.8.1")
|
||||
|
||||
// DECOMPOSE
|
||||
implementation(Decompose.decompose)
|
||||
implementation(Decompose.extensionsCompose)
|
||||
|
||||
// MVI
|
||||
implementation(MVIKotlin.mvikotlin)
|
||||
implementation(MVIKotlin.mvikotlinMain)
|
||||
implementation(MVIKotlin.mvikotlinLogging)
|
||||
implementation(MVIKotlin.mvikotlinTimeTravel)
|
||||
|
||||
// Firebase
|
||||
implementation(platform("com.google.firebase:firebase-bom:27.1.0"))
|
||||
implementation("com.google.firebase:firebase-analytics-ktx")
|
||||
implementation("com.google.firebase:firebase-crashlytics-ktx")
|
||||
implementation("com.google.firebase:firebase-perf-ktx")
|
||||
|
||||
// Extras
|
||||
Extras.Android.apply {
|
||||
implementation(appUpdator)
|
||||
implementation(razorpay)
|
||||
}
|
||||
implementation(MVIKotlin.mvikotlin)
|
||||
implementation(MVIKotlin.mvikotlinMain)
|
||||
implementation(MVIKotlin.mvikotlinLogging)
|
||||
implementation(MVIKotlin.mvikotlinTimeTravel)
|
||||
implementation(Decompose.decompose)
|
||||
implementation(Decompose.extensionsCompose)
|
||||
|
||||
implementation("dev.icerock.moko:parcelize:0.6.1")
|
||||
implementation("com.github.shabinder:storage-chooser:2.0.4.45")
|
||||
implementation("com.google.accompanist:accompanist-insets:0.8.1")
|
||||
|
||||
// Test
|
||||
testImplementation("junit:junit:4.13.2")
|
||||
|
@ -210,14 +210,14 @@ class MainActivity : ComponentActivity(), PaymentResultListener {
|
||||
)
|
||||
|
||||
override fun addToLibrary(path: String) {
|
||||
MediaScannerConnection.scanFile(
|
||||
MediaScannerConnection.scanFile (
|
||||
applicationContext,
|
||||
listOf(path).toTypedArray(), null, null
|
||||
)
|
||||
}
|
||||
|
||||
override fun sendTracksToService(array: ArrayList<TrackDetails>) {
|
||||
for (list in array.chunked(50)){
|
||||
for (list in array.chunked(50)) {
|
||||
val serviceIntent = Intent(this@MainActivity, ForegroundService::class.java)
|
||||
serviceIntent.putParcelableArrayListExtra("object", list as ArrayList)
|
||||
ContextCompat.startForegroundService(this@MainActivity, serviceIntent)
|
||||
@ -378,7 +378,7 @@ class MainActivity : ComponentActivity(), PaymentResultListener {
|
||||
while(!this@MainActivity::root.isInitialized){
|
||||
delay(100)
|
||||
}
|
||||
if(methods.isInternetAvailable)callBacks.searchLink(link)
|
||||
if(methods.value.isInternetAvailable)callBacks.searchLink(link)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,6 @@ plugins {
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
mavenLocal()
|
||||
maven(url = "https://jitpack.io")
|
||||
|
@ -22,10 +22,9 @@ group = "com.shabinder"
|
||||
version = "2.1"
|
||||
|
||||
repositories {
|
||||
jcenter()
|
||||
google()
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
google()
|
||||
maven(url = "https://jitpack.io")
|
||||
maven(url = "https://plugins.gradle.org/m2/")
|
||||
maven(url = "https://dl.bintray.com/kotlin/kotlin-js-wrappers")
|
||||
|
@ -18,6 +18,7 @@ plugins {
|
||||
id("com.android.library")
|
||||
id("kotlin-multiplatform")
|
||||
id("org.jetbrains.compose")
|
||||
id("kotlin-parcelize")
|
||||
id("ktlint-setup")
|
||||
}
|
||||
|
||||
@ -35,10 +36,22 @@ kotlin {
|
||||
sourceSets {
|
||||
named("commonMain") {
|
||||
dependencies {
|
||||
// Decompose
|
||||
implementation(Decompose.decompose)
|
||||
|
||||
// MVI
|
||||
implementation(MVIKotlin.coroutines)
|
||||
implementation(MVIKotlin.mvikotlin)
|
||||
|
||||
implementation(compose.runtime)
|
||||
implementation(compose.foundation)
|
||||
implementation(compose.material)
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3-native-mt")
|
||||
|
||||
implementation(Extras.kermit)
|
||||
implementation("dev.icerock.moko:parcelize:0.6.1")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3-native-mt") {
|
||||
isForce = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@ plugins {
|
||||
id("kotlin-multiplatform")
|
||||
id("org.jetbrains.compose")
|
||||
id("ktlint-setup")
|
||||
id("kotlin-parcelize")
|
||||
}
|
||||
|
||||
kotlin {
|
||||
@ -30,7 +31,7 @@ kotlin {
|
||||
if (isiOSDevice) {
|
||||
iosArm64("ios")
|
||||
} else {
|
||||
iosX64("ios")
|
||||
iosX64("ios") {}
|
||||
}
|
||||
}
|
||||
|
||||
@ -56,7 +57,32 @@ kotlin {
|
||||
|
||||
sourceSets {
|
||||
named("commonMain") {
|
||||
dependencies {}
|
||||
dependencies {
|
||||
// Decompose
|
||||
implementation(Decompose.decompose)
|
||||
|
||||
// MVI
|
||||
implementation(MVIKotlin.coroutines)
|
||||
implementation(MVIKotlin.mvikotlin)
|
||||
|
||||
// Koin
|
||||
implementation(Koin.core)
|
||||
|
||||
implementation(Ktor.auth)
|
||||
implementation(Ktor.clientJson)
|
||||
implementation(Ktor.clientCore)
|
||||
implementation(Ktor.clientLogging)
|
||||
implementation(Ktor.clientSerialization)
|
||||
|
||||
// Extras
|
||||
implementation(Extras.kermit)
|
||||
implementation("co.touchlab:stately-common:1.1.6")
|
||||
implementation("dev.icerock.moko:parcelize:0.6.1")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.2.0")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3-native-mt") {
|
||||
isForce = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
named("androidMain") {
|
||||
@ -67,8 +93,9 @@ kotlin {
|
||||
implementation(compose.material)
|
||||
implementation(compose.foundation)
|
||||
implementation(compose.materialIconsExtended)
|
||||
implementation(Decompose.decompose)
|
||||
implementation(Decompose.extensionsCompose)
|
||||
implementation(Ktor.clientAndroid)
|
||||
implementation(Koin.android)
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,12 +106,14 @@ kotlin {
|
||||
implementation(compose.material)
|
||||
implementation(compose.desktop.common)
|
||||
implementation(compose.materialIconsExtended)
|
||||
implementation(Decompose.decompose)
|
||||
implementation(Decompose.extensionsCompose)
|
||||
implementation(Ktor.clientApache)
|
||||
implementation(Ktor.slf4j)
|
||||
}
|
||||
}
|
||||
named("jsMain") {
|
||||
dependencies {
|
||||
implementation(Ktor.clientJs)
|
||||
implementation("org.jetbrains:kotlin-react:17.0.1-pre.148-kotlin-1.4.30")
|
||||
implementation("org.jetbrains:kotlin-styled:1.0.0-pre.115-kotlin-1.4.10")
|
||||
implementation("org.jetbrains:kotlin-react-dom:17.0.1-pre.148-kotlin-1.4.30")
|
||||
@ -92,7 +121,9 @@ kotlin {
|
||||
}
|
||||
if(HostOS.isMac){
|
||||
named("iosMain"){
|
||||
dependencies { }
|
||||
dependencies {
|
||||
implementation(Ktor.clientIos)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,6 @@ import org.jetbrains.compose.compose
|
||||
plugins {
|
||||
id("multiplatform-compose-setup")
|
||||
id("android-setup")
|
||||
id("kotlin-parcelize")
|
||||
}
|
||||
|
||||
kotlin {
|
||||
@ -33,8 +32,6 @@ kotlin {
|
||||
implementation(project(":common:database"))
|
||||
implementation(project(":common:data-models"))
|
||||
implementation(project(":common:dependency-injection"))
|
||||
// DECOMPOSE
|
||||
implementation(Decompose.decompose)
|
||||
implementation(Decompose.extensionsCompose)
|
||||
}
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ actual fun ImageLoad(
|
||||
var pic by remember(link) { mutableStateOf<ImageBitmap?>(null) }
|
||||
|
||||
LaunchedEffect(link) {
|
||||
withContext(methods.dispatcherIO) {
|
||||
withContext(methods.value.dispatcherIO) {
|
||||
pic = loader(link).image
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,6 @@ import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
@ -46,24 +45,24 @@ import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.arkivanov.decompose.extensions.compose.jetbrains.asState
|
||||
import com.shabinder.common.di.Picture
|
||||
import com.shabinder.common.list.SpotiFlyerList
|
||||
import com.shabinder.common.models.DownloadStatus
|
||||
import com.shabinder.common.models.TrackDetails
|
||||
import com.shabinder.common.models.methods
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
@Composable
|
||||
fun SpotiFlyerListContent(
|
||||
component: SpotiFlyerList,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val model by component.models.collectAsState(SpotiFlyerList.State())
|
||||
val model by component.models.asState()
|
||||
|
||||
LaunchedEffect(model.errorOccurred) {
|
||||
/*Handle if Any Exception Occurred*/
|
||||
model.errorOccurred?.let {
|
||||
methods.showPopUpMessage(it.message ?: "An Error Occurred, Check your Link / Connection")
|
||||
methods.value.showPopUpMessage(it.message ?: "An Error Occurred, Check your Link / Connection")
|
||||
component.onBackPressed()
|
||||
}
|
||||
}
|
||||
|
@ -58,7 +58,6 @@ import androidx.compose.material.icons.rounded.Edit
|
||||
import androidx.compose.material.icons.rounded.Flag
|
||||
import androidx.compose.material.icons.rounded.Share
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
@ -72,6 +71,7 @@ import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.arkivanov.decompose.extensions.compose.jetbrains.asState
|
||||
import com.shabinder.common.di.Picture
|
||||
import com.shabinder.common.main.SpotiFlyerMain
|
||||
import com.shabinder.common.main.SpotiFlyerMain.HomeCategory
|
||||
@ -80,7 +80,7 @@ import com.shabinder.common.models.methods
|
||||
|
||||
@Composable
|
||||
fun SpotiFlyerMainContent(component: SpotiFlyerMain) {
|
||||
val model by component.models.collectAsState(SpotiFlyerMain.State())
|
||||
val model by component.models.asState()
|
||||
|
||||
Column {
|
||||
SearchPanel(
|
||||
@ -193,7 +193,7 @@ fun SearchPanel(
|
||||
OutlinedButton(
|
||||
modifier = Modifier.padding(12.dp).wrapContentWidth(),
|
||||
onClick = {
|
||||
if (link.isBlank()) methods.showPopUpMessage("Enter A Link!")
|
||||
if (link.isBlank()) methods.value.showPopUpMessage("Enter A Link!")
|
||||
else {
|
||||
// TODO if(!isOnline(ctx)) showPopUpMessage("Check Your Internet Connection") else
|
||||
onSearch(link)
|
||||
@ -235,7 +235,7 @@ fun AboutColumn(modifier: Modifier = Modifier) {
|
||||
"Open Spotify",
|
||||
tint = Color.Unspecified,
|
||||
modifier = Modifier.clip(SpotiFlyerShapes.small).clickable(
|
||||
onClick = { methods.openPlatform("com.spotify.music", "http://open.spotify.com") }
|
||||
onClick = { methods.value.openPlatform("com.spotify.music", "http://open.spotify.com") }
|
||||
)
|
||||
)
|
||||
Spacer(modifier = modifier.padding(start = 16.dp))
|
||||
@ -244,7 +244,7 @@ fun AboutColumn(modifier: Modifier = Modifier) {
|
||||
"Open Gaana",
|
||||
tint = Color.Unspecified,
|
||||
modifier = Modifier.clip(SpotiFlyerShapes.small).clickable(
|
||||
onClick = { methods.openPlatform("com.gaana", "http://gaana.com") }
|
||||
onClick = { methods.value.openPlatform("com.gaana", "http://gaana.com") }
|
||||
)
|
||||
)
|
||||
Spacer(modifier = modifier.padding(start = 16.dp))
|
||||
@ -253,7 +253,7 @@ fun AboutColumn(modifier: Modifier = Modifier) {
|
||||
"Open Youtube",
|
||||
tint = Color.Unspecified,
|
||||
modifier = Modifier.clip(SpotiFlyerShapes.small).clickable(
|
||||
onClick = { methods.openPlatform("com.google.android.youtube", "http://m.youtube.com") }
|
||||
onClick = { methods.value.openPlatform("com.google.android.youtube", "http://m.youtube.com") }
|
||||
)
|
||||
)
|
||||
Spacer(modifier = modifier.padding(start = 12.dp))
|
||||
@ -262,7 +262,7 @@ fun AboutColumn(modifier: Modifier = Modifier) {
|
||||
"Open Youtube Music",
|
||||
tint = Color.Unspecified,
|
||||
modifier = Modifier.clip(SpotiFlyerShapes.small).clickable(
|
||||
onClick = { methods.openPlatform("com.google.android.apps.youtube.music", "https://music.youtube.com/") }
|
||||
onClick = { methods.value.openPlatform("com.google.android.apps.youtube.music", "https://music.youtube.com/") }
|
||||
)
|
||||
)
|
||||
}
|
||||
@ -283,7 +283,7 @@ fun AboutColumn(modifier: Modifier = Modifier) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.fillMaxWidth().clickable(
|
||||
onClick = { methods.openPlatform("", "http://github.com/Shabinder/SpotiFlyer") }
|
||||
onClick = { methods.value.openPlatform("", "http://github.com/Shabinder/SpotiFlyer") }
|
||||
)
|
||||
.padding(vertical = 6.dp)
|
||||
) {
|
||||
@ -302,7 +302,7 @@ fun AboutColumn(modifier: Modifier = Modifier) {
|
||||
}
|
||||
Row(
|
||||
modifier = modifier.fillMaxWidth().padding(vertical = 6.dp)
|
||||
.clickable(onClick = { methods.openPlatform("", "http://github.com/Shabinder/SpotiFlyer") }),
|
||||
.clickable(onClick = { methods.value.openPlatform("", "http://github.com/Shabinder/SpotiFlyer") }),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(Icons.Rounded.Flag, "Help Translate", Modifier.size(32.dp))
|
||||
@ -320,7 +320,7 @@ fun AboutColumn(modifier: Modifier = Modifier) {
|
||||
}
|
||||
Row(
|
||||
modifier = modifier.fillMaxWidth().padding(vertical = 6.dp)
|
||||
.clickable(onClick = { methods.giveDonation() }),
|
||||
.clickable(onClick = { methods.value.giveDonation() }),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(Icons.Rounded.CardGiftcard, "Support Developer")
|
||||
@ -340,7 +340,7 @@ fun AboutColumn(modifier: Modifier = Modifier) {
|
||||
modifier = modifier.fillMaxWidth().padding(vertical = 6.dp)
|
||||
.clickable(
|
||||
onClick = {
|
||||
methods.shareApp()
|
||||
methods.value.shareApp()
|
||||
}
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
|
@ -48,7 +48,7 @@ actual fun ImageLoad(
|
||||
) {
|
||||
var pic by remember(link) { mutableStateOf<ImageBitmap?>(null) }
|
||||
LaunchedEffect(link) {
|
||||
withContext(methods.dispatcherIO) {
|
||||
withContext(methods.value.dispatcherIO) {
|
||||
pic = loader(link).image
|
||||
}
|
||||
}
|
||||
|
@ -24,16 +24,7 @@ plugins {
|
||||
kotlin {
|
||||
sourceSets {
|
||||
commonMain {
|
||||
dependencies {
|
||||
api("dev.icerock.moko:parcelize:0.6.1")
|
||||
api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.1.0")
|
||||
api("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3-native-mt")
|
||||
}
|
||||
}
|
||||
if(HostOS.isMac){
|
||||
val iosMain by getting {
|
||||
|
||||
}
|
||||
dependencies {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,3 @@
|
||||
package com.shabinder.common.models
|
||||
|
||||
actual class NativeAtomicReference<T> actual constructor(actual var value: T)
|
@ -1,11 +1,13 @@
|
||||
package com.shabinder.common.models
|
||||
|
||||
import co.touchlab.stately.freeze
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
||||
/*
|
||||
* Holder to call platform actions from anywhere
|
||||
* */
|
||||
lateinit var methods: Actions
|
||||
var methods: NativeAtomicReference<Actions> = NativeAtomicReference(stubActions().freeze())
|
||||
|
||||
/*
|
||||
* Interface Having All Platform Dependent Functions
|
||||
@ -44,4 +46,18 @@ interface Actions {
|
||||
|
||||
// Current Platform Info
|
||||
val currentPlatform: AllPlatforms
|
||||
}
|
||||
|
||||
|
||||
private fun stubActions() = object :Actions{
|
||||
override val platformActions = object: PlatformActions{}
|
||||
override fun showPopUpMessage(string: String, long: Boolean) {}
|
||||
override fun setDownloadDirectoryAction() {}
|
||||
override fun queryActiveTracks() {}
|
||||
override fun giveDonation() {}
|
||||
override fun shareApp() {}
|
||||
override fun openPlatform(packageID: String, platformLink: String) {}
|
||||
override val dispatcherIO: CoroutineDispatcher = Dispatchers.Default
|
||||
override val isInternetAvailable: Boolean = true
|
||||
override val currentPlatform: AllPlatforms = AllPlatforms.Jvm
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
package com.shabinder.common.models
|
||||
|
||||
expect class NativeAtomicReference<T>(value: T) {
|
||||
var value: T
|
||||
// fun compareAndSet(expected: T, new: T): Boolean
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
package com.shabinder.common.models
|
||||
|
||||
actual class NativeAtomicReference<T> actual constructor(actual var value: T)
|
@ -0,0 +1,7 @@
|
||||
package com.shabinder.common.models
|
||||
|
||||
import kotlin.native.concurrent.AtomicReference
|
||||
|
||||
actual interface PlatformActions
|
||||
|
||||
actual typealias NativeAtomicReference<T> = AtomicReference<T>
|
@ -1,4 +0,0 @@
|
||||
package com.shabinder.common.models
|
||||
|
||||
actual interface PlatformActions {
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
package com.shabinder.common.models
|
||||
|
||||
actual class NativeAtomicReference<T> actual constructor(actual var value: T)
|
@ -31,13 +31,13 @@ kotlin {
|
||||
commonMain {
|
||||
dependencies {
|
||||
implementation(project(":common:data-models"))
|
||||
|
||||
// SQL Delight
|
||||
implementation(SqlDelight.runtime)
|
||||
implementation(SqlDelight.coroutineExtensions)
|
||||
api(Extras.kermit)
|
||||
|
||||
// koin
|
||||
api(Koin.core)
|
||||
api(Koin.test)
|
||||
implementation(Koin.test)
|
||||
}
|
||||
}
|
||||
|
||||
@ -53,7 +53,8 @@ kotlin {
|
||||
implementation(SqlDelight.jdbcDriver)
|
||||
}
|
||||
}
|
||||
if(HostOS.isMac){
|
||||
|
||||
if(HostOS.isMac) {
|
||||
val iosMain by getting {
|
||||
dependencies {
|
||||
implementation(SqlDelight.nativeDriver)
|
||||
|
@ -20,75 +20,43 @@ plugins {
|
||||
id("multiplatform-setup")
|
||||
id("android-setup")
|
||||
kotlin("plugin.serialization")
|
||||
kotlin("native.cocoapods")
|
||||
}
|
||||
|
||||
version = "1.0"
|
||||
|
||||
kotlin {
|
||||
|
||||
cocoapods {
|
||||
// Configure fields required by CocoaPods.
|
||||
summary = "SpotiFlyer-DI Native Module"
|
||||
homepage = "https://github.com/Shabinder/SpotiFlyer"
|
||||
authors = "Shabinder Singh"
|
||||
// You can change the name of the produced framework.
|
||||
// By default, it is the name of the Gradle project.
|
||||
frameworkName = "SpotiFlyer-DI"
|
||||
ios.deploymentTarget = "9.0"
|
||||
|
||||
// Dependencies
|
||||
pod("TagLibIOS") {
|
||||
version = "~> 0.3"
|
||||
}
|
||||
//podfile = project.file("spotiflyer-ios/Podfile")
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
commonMain {
|
||||
dependencies {
|
||||
implementation(project(":common:data-models"))
|
||||
implementation(project(":common:database"))
|
||||
implementation("org.jetbrains.kotlinx:atomicfu:0.16.1")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.1.0")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.2.0")
|
||||
implementation("com.shabinder.fuzzywuzzy:fuzzywuzzy:1.0")
|
||||
api(Ktor.clientCore)
|
||||
api(Ktor.clientSerialization)
|
||||
api(Ktor.clientLogging)
|
||||
api(Ktor.clientJson)
|
||||
api(Ktor.auth)
|
||||
api(Extras.youtubeDownloader)
|
||||
api(Extras.kermit)
|
||||
implementation(Extras.youtubeDownloader)
|
||||
implementation(MVIKotlin.rx)
|
||||
}
|
||||
}
|
||||
androidMain {
|
||||
dependencies {
|
||||
implementation(compose.materialIconsExtended)
|
||||
api(Koin.android)
|
||||
api(Ktor.clientAndroid)
|
||||
api(Extras.Android.razorpay)
|
||||
api(Extras.mp3agic)
|
||||
api(Extras.jaudioTagger)
|
||||
api("com.github.shabinder:storage-chooser:2.0.4.45")
|
||||
// api(files("$rootDir/libs/mobile-ffmpeg.aar"))
|
||||
implementation(Extras.Android.razorpay)
|
||||
implementation(Extras.mp3agic)
|
||||
//implementation(Extras.jaudioTagger)
|
||||
implementation("com.github.shabinder:storage-chooser:2.0.4.45")
|
||||
// implementation(files("$rootDir/libs/mobile-ffmpeg.aar"))
|
||||
}
|
||||
}
|
||||
desktopMain {
|
||||
dependencies {
|
||||
implementation(compose.materialIconsExtended)
|
||||
api(Ktor.clientApache)
|
||||
api(Ktor.slf4j)
|
||||
api(Extras.mp3agic)
|
||||
api(Extras.jaudioTagger)
|
||||
implementation(Extras.mp3agic)
|
||||
//implementation(Extras.jaudioTagger)
|
||||
}
|
||||
}
|
||||
jsMain {
|
||||
dependencies {
|
||||
implementation(npm("browser-id3-writer", "4.4.0"))
|
||||
implementation(npm("file-saver", "2.0.4"))
|
||||
implementation(project(":common:data-models"))
|
||||
api(Ktor.clientJs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,46 +0,0 @@
|
||||
Pod::Spec.new do |spec|
|
||||
spec.name = 'dependency_injection'
|
||||
spec.version = '1.0'
|
||||
spec.homepage = 'https://github.com/Shabinder/SpotiFlyer'
|
||||
spec.source = { :git => "Not Published", :tag => "Cocoapods/#{spec.name}/#{spec.version}" }
|
||||
spec.authors = 'Shabinder Singh'
|
||||
spec.license = ''
|
||||
spec.summary = 'SpotiFlyer-DI Native Module'
|
||||
|
||||
spec.static_framework = true
|
||||
spec.vendored_frameworks = "build/cocoapods/framework/SpotiFlyer-DI.framework"
|
||||
spec.libraries = "c++"
|
||||
spec.module_name = "#{spec.name}_umbrella"
|
||||
|
||||
spec.ios.deployment_target = '9.0'
|
||||
|
||||
spec.dependency 'TagLibIOS', '~> 0.3'
|
||||
|
||||
spec.pod_target_xcconfig = {
|
||||
'KOTLIN_TARGET[sdk=iphonesimulator*]' => 'ios_x64',
|
||||
'KOTLIN_TARGET[sdk=iphoneos*]' => 'ios_arm',
|
||||
'KOTLIN_TARGET[sdk=watchsimulator*]' => 'watchos_x64',
|
||||
'KOTLIN_TARGET[sdk=watchos*]' => 'watchos_arm',
|
||||
'KOTLIN_TARGET[sdk=appletvsimulator*]' => 'tvos_x64',
|
||||
'KOTLIN_TARGET[sdk=appletvos*]' => 'tvos_arm64',
|
||||
'KOTLIN_TARGET[sdk=macosx*]' => 'macos_x64'
|
||||
}
|
||||
|
||||
spec.script_phases = [
|
||||
{
|
||||
:name => 'Build dependency_injection',
|
||||
:execution_position => :before_compile,
|
||||
:shell_path => '/bin/sh',
|
||||
:script => <<-SCRIPT
|
||||
set -ev
|
||||
REPO_ROOT="$PODS_TARGET_SRCROOT"
|
||||
"$REPO_ROOT/../../gradlew" -p "$REPO_ROOT" :common:dependency-injection:syncFramework \
|
||||
-Pkotlin.native.cocoapods.target=$KOTLIN_TARGET \
|
||||
-Pkotlin.native.cocoapods.configuration=$CONFIGURATION \
|
||||
-Pkotlin.native.cocoapods.cflags="$OTHER_CFLAGS" \
|
||||
-Pkotlin.native.cocoapods.paths.headers="$HEADER_SEARCH_PATHS" \
|
||||
-Pkotlin.native.cocoapods.paths.frameworks="$FRAMEWORK_SEARCH_PATHS"
|
||||
SCRIPT
|
||||
}
|
||||
]
|
||||
end
|
@ -25,6 +25,6 @@ actual suspend fun downloadTracks(
|
||||
dir: Dir
|
||||
) {
|
||||
if (!list.isNullOrEmpty()) {
|
||||
methods.platformActions.sendTracksToService(ArrayList(list))
|
||||
methods.value.platformActions.sendTracksToService(ArrayList(list))
|
||||
}
|
||||
}
|
@ -46,7 +46,7 @@ actual class Dir actual constructor(
|
||||
const val DirKey = "downloadDir"
|
||||
}
|
||||
|
||||
private val sharedPreferences:SharedPreferences by lazy { methods.platformActions.sharedPreferences }
|
||||
private val sharedPreferences:SharedPreferences by lazy { methods.value.platformActions.sharedPreferences }
|
||||
|
||||
fun setDownloadDirectory(newBasePath:String){
|
||||
sharedPreferences.edit().putString(DirKey,newBasePath).apply()
|
||||
@ -57,7 +57,7 @@ actual class Dir actual constructor(
|
||||
|
||||
actual fun fileSeparator(): String = File.separator
|
||||
|
||||
actual fun imageCacheDir(): String = methods.platformActions.imageCacheDir
|
||||
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 +
|
||||
@ -84,7 +84,8 @@ actual class Dir actual constructor(
|
||||
@Suppress("BlockingMethodInNonBlockingContext")
|
||||
actual suspend fun saveFileWithMetadata(
|
||||
mp3ByteArray: ByteArray,
|
||||
trackDetails: TrackDetails
|
||||
trackDetails: TrackDetails,
|
||||
postProcess:(track: TrackDetails)->Unit
|
||||
) {
|
||||
withContext(Dispatchers.IO){
|
||||
val songFile = File(trackDetails.outputFilePath)
|
||||
@ -152,7 +153,7 @@ actual class Dir actual constructor(
|
||||
}
|
||||
}
|
||||
|
||||
actual fun addToLibrary(path: String) = methods.platformActions.addToLibrary(path)
|
||||
actual fun addToLibrary(path: String) = methods.value.platformActions.addToLibrary(path)
|
||||
|
||||
actual suspend fun loadImage(url: String): Picture = withContext(Dispatchers.IO){
|
||||
val cachePath = imageCacheDir() + getNameURL(url)
|
||||
|
@ -236,7 +236,7 @@ class ForegroundService : Service(), CoroutineScope {
|
||||
is DownloadResult.Success -> {
|
||||
try {
|
||||
// Save File and Embed Metadata
|
||||
val job = launch(Dispatchers.Default) { dir.saveFileWithMetadata(it.byteArray, track) }
|
||||
val job = launch(Dispatchers.Default) { dir.saveFileWithMetadata(it.byteArray, track){} }
|
||||
allTracksStatus[track.title] = DownloadStatus.Converting
|
||||
sendTrackBroadcast("Converting", track)
|
||||
addToNotification("Processing ${track.title}")
|
||||
@ -293,7 +293,7 @@ class ForegroundService : Service(), CoroutineScope {
|
||||
// Checking if the received broadcast is for our enqueued download by matching download id
|
||||
if (downloadID == id) {
|
||||
allTracksStatus[track.title] = DownloadStatus.Converting
|
||||
launch { dir.saveFileWithMetadata(byteArrayOf(), track); converted++ }
|
||||
launch { dir.saveFileWithMetadata(byteArrayOf(), track){}; converted++ }
|
||||
// Unregister this broadcast Receiver
|
||||
this@ForegroundService.unregisterReceiver(this)
|
||||
}
|
||||
|
@ -42,6 +42,9 @@ fun initKoin(enableNetworkLogs: Boolean = false, appDeclaration: KoinAppDeclarat
|
||||
modules(commonModule(enableNetworkLogs = enableNetworkLogs), databaseModule())
|
||||
}
|
||||
|
||||
// Called by IOS
|
||||
fun initKoin() = initKoin(enableNetworkLogs = false) { }
|
||||
|
||||
fun commonModule(enableNetworkLogs: Boolean) = module {
|
||||
single { createHttpClient(enableNetworkLogs = enableNetworkLogs) }
|
||||
single { Dir(get(), get()) }
|
||||
|
@ -43,7 +43,7 @@ expect class Dir (
|
||||
suspend fun cacheImage(image: Any, path: String) // in Android = ImageBitmap, Desktop = BufferedImage
|
||||
suspend fun loadImage(url: String): Picture
|
||||
suspend fun clearCache()
|
||||
suspend fun saveFileWithMetadata(mp3ByteArray: ByteArray, trackDetails: TrackDetails)
|
||||
suspend fun saveFileWithMetadata(mp3ByteArray: ByteArray, trackDetails: TrackDetails,postProcess:(track: TrackDetails)->Unit = {})
|
||||
fun addToLibrary(path: String)
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@ package com.shabinder.common.di
|
||||
import com.shabinder.common.models.TrackDetails
|
||||
import com.shabinder.common.models.methods
|
||||
import io.ktor.client.request.*
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
expect suspend fun downloadTracks(
|
||||
@ -28,7 +29,7 @@ expect suspend fun downloadTracks(
|
||||
)
|
||||
|
||||
suspend fun isInternetAccessible(): Boolean {
|
||||
return withContext(methods.dispatcherIO) {
|
||||
return withContext(methods.value.dispatcherIO) {
|
||||
try {
|
||||
ktorHttpClient.head<String>("http://google.com")
|
||||
true
|
||||
@ -38,3 +39,5 @@ suspend fun isInternetAccessible(): Boolean {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal val dispatcherDefault = Dispatchers.Default
|
||||
|
@ -27,7 +27,7 @@ import com.shabinder.common.models.methods
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.request.get
|
||||
|
||||
val corsApi get() = if (methods.currentPlatform is AllPlatforms.Js) {
|
||||
val corsApi get() = if (methods.value.currentPlatform is AllPlatforms.Js) {
|
||||
corsProxy.url
|
||||
} // "https://spotiflyer-cors.azurewebsites.net/" //"https://spotiflyer-cors.herokuapp.com/"//"https://cors.bridged.cc/"
|
||||
else ""
|
||||
|
@ -38,6 +38,7 @@ import io.ktor.client.request.header
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlin.native.concurrent.ThreadLocal
|
||||
|
||||
class SpotifyProvider(
|
||||
private val tokenStore: TokenStore,
|
||||
@ -48,7 +49,7 @@ class SpotifyProvider(
|
||||
/* init {
|
||||
logger.d { "Creating Spotify Provider" }
|
||||
GlobalScope.launch(Dispatchers.Default) {
|
||||
if (methods.currentPlatform is AllPlatforms.Js) {
|
||||
if (methods.value.currentPlatform is AllPlatforms.Js) {
|
||||
authenticateSpotifyClient(override = true)
|
||||
} else authenticateSpotifyClient()
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ class YoutubeMp3(
|
||||
suspend fun getMp3DownloadLink(videoID: String): String? = try {
|
||||
getLinkFromYt1sMp3(videoID)?.let {
|
||||
logger.i { "Download Link: $it" }
|
||||
if (methods.currentPlatform is AllPlatforms.Js/* && corsProxy !is CorsProxy.PublicProxyWithExtension*/)
|
||||
if (methods.value.currentPlatform is AllPlatforms.Js/* && corsProxy !is CorsProxy.PublicProxyWithExtension*/)
|
||||
"https://kind-grasshopper-73.telebit.io/cors/$it"
|
||||
// "https://spotiflyer.azurewebsites.net/$it" // Data OUT Limit issue
|
||||
else it
|
||||
|
@ -29,7 +29,7 @@ import io.ktor.http.Parameters
|
||||
|
||||
suspend fun authenticateSpotify(): TokenData? {
|
||||
return try {
|
||||
if (methods.isInternetAvailable) spotifyAuthClient.post("https://accounts.spotify.com/api/token") {
|
||||
if (methods.value.isInternetAvailable) spotifyAuthClient.post("https://accounts.spotify.com/api/token") {
|
||||
body = FormDataContent(Parameters.build { append("grant_type", "client_credentials") })
|
||||
} else null
|
||||
}catch (e:Exception) {
|
||||
|
@ -17,7 +17,7 @@
|
||||
package com.shabinder.common.di.utils
|
||||
|
||||
// Dependencies:
|
||||
// implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9")
|
||||
// implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9-native-mt")
|
||||
// implementation("org.jetbrains.kotlinx:atomicfu:0.14.4")
|
||||
// Gist: https://gist.github.com/fluidsonic/ba32de21c156bbe8424c8d5fc20dcd8e
|
||||
|
||||
@ -37,7 +37,7 @@ import kotlinx.coroutines.withContext
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
class ParallelExecutor(
|
||||
parentContext: CoroutineContext = methods.dispatcherIO,
|
||||
parentContext: CoroutineContext = methods.value.dispatcherIO,
|
||||
) : Closeable {
|
||||
|
||||
private val concurrentOperationLimit = atomic(4)
|
||||
|
@ -0,0 +1,23 @@
|
||||
package com.shabinder.common.di.utils
|
||||
|
||||
import com.arkivanov.decompose.value.Value
|
||||
import com.arkivanov.decompose.value.ValueObserver
|
||||
import com.arkivanov.mvikotlin.core.store.Store
|
||||
import com.arkivanov.mvikotlin.rx.Disposable
|
||||
|
||||
fun <T : Any> Store<*, T, *>.asValue(): Value<T> =
|
||||
object : Value<T>() {
|
||||
override val value: T get() = state
|
||||
private var disposables = emptyMap<ValueObserver<T>, Disposable>()
|
||||
|
||||
override fun subscribe(observer: ValueObserver<T>) {
|
||||
val disposable = states(com.arkivanov.mvikotlin.rx.observer(onNext = observer))
|
||||
this.disposables += observer to disposable
|
||||
}
|
||||
|
||||
override fun unsubscribe(observer: ValueObserver<T>) {
|
||||
val disposable = disposables[observer] ?: return
|
||||
this.disposables -= observer
|
||||
disposable.dispose()
|
||||
}
|
||||
}
|
@ -61,7 +61,7 @@ private val ytDownloader = YoutubeDownloader()
|
||||
suspend fun downloadTrack(
|
||||
videoID: String,
|
||||
trackDetails: TrackDetails,
|
||||
saveFileWithMetaData: suspend (mp3ByteArray: ByteArray, trackDetails: TrackDetails) -> Unit
|
||||
saveFileWithMetaData: suspend (mp3ByteArray: ByteArray, trackDetails: TrackDetails,postProcess:(TrackDetails)->Unit) -> Unit
|
||||
) {
|
||||
try {
|
||||
val audioData = ytDownloader.getVideo(videoID).getData()
|
||||
@ -85,7 +85,7 @@ suspend fun downloadTrack(
|
||||
)
|
||||
}
|
||||
is DownloadResult.Success -> { // Todo clear map
|
||||
saveFileWithMetaData(it.byteArray, trackDetails)
|
||||
saveFileWithMetaData(it.byteArray, trackDetails){}
|
||||
DownloadProgressFlow.emit(
|
||||
DownloadProgressFlow.replayCache.getOrElse(
|
||||
0
|
||||
|
@ -85,7 +85,8 @@ actual class Dir actual constructor(
|
||||
@Suppress("BlockingMethodInNonBlockingContext")
|
||||
actual suspend fun saveFileWithMetadata(
|
||||
mp3ByteArray: ByteArray,
|
||||
trackDetails: TrackDetails
|
||||
trackDetails: TrackDetails,
|
||||
postProcess:(track: TrackDetails)->Unit
|
||||
) {
|
||||
val file = File(trackDetails.outputFilePath)
|
||||
file.writeBytes(mp3ByteArray)
|
||||
|
@ -7,6 +7,5 @@ actual suspend fun downloadTracks(
|
||||
fetcher: FetchPlatformQueryResult,
|
||||
dir: Dir
|
||||
) {
|
||||
|
||||
// TODO
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package com.shabinder.common.di
|
||||
|
||||
import com.shabinder.common.models.DownloadStatus
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
|
||||
/*
|
||||
* Dependency Provider for IOS
|
||||
* */
|
||||
object IOSDeps: KoinComponent {
|
||||
val dir: Dir by inject() // = get()
|
||||
val fetchPlatformQueryResult: FetchPlatformQueryResult by inject() // get()
|
||||
val database get() = dir.db
|
||||
val sharedFlow = MutableSharedFlow<HashMap<String, DownloadStatus>>(1)
|
||||
val defaultDispatcher = dispatcherDefault
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
package com.shabinder.common.di
|
||||
|
||||
import co.touchlab.kermit.Kermit
|
||||
import cocoapods.TagLibIOS.TLAudio
|
||||
import com.shabinder.common.database.SpotiFlyerDatabase
|
||||
import com.shabinder.common.models.TrackDetails
|
||||
import com.shabinder.database.Database
|
||||
@ -27,7 +26,7 @@ actual class Dir actual constructor(
|
||||
) {
|
||||
|
||||
init {
|
||||
createDirectories()
|
||||
//createDirectories()
|
||||
}
|
||||
|
||||
actual fun isPresent(path: String): Boolean = NSFileManager.defaultManager.fileExistsAtPath(path)
|
||||
@ -135,16 +134,18 @@ actual class Dir actual constructor(
|
||||
|
||||
actual suspend fun saveFileWithMetadata(
|
||||
mp3ByteArray: ByteArray,
|
||||
trackDetails: TrackDetails
|
||||
trackDetails: TrackDetails,
|
||||
postProcess:(track: TrackDetails)->Unit
|
||||
) {
|
||||
when (trackDetails.outputFilePath.substringAfterLast('.')) {
|
||||
".mp3" -> {
|
||||
val file = TLAudio(trackDetails.outputFilePath)
|
||||
postProcess(trackDetails)
|
||||
/*val file = TLAudio(trackDetails.outputFilePath)
|
||||
file.addTagsAndSave(
|
||||
trackDetails,
|
||||
this::loadCachedImage,
|
||||
this::addToLibrary
|
||||
)
|
||||
)*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
package com.shabinder.common.di
|
||||
/*
|
||||
|
||||
import cocoapods.TagLibIOS.TLAudio
|
||||
import com.shabinder.common.models.TrackDetails
|
||||
@ -34,4 +35,4 @@ suspend fun TLAudio.addTagsAndSave(
|
||||
}
|
||||
} catch (e: Exception){ e.printStackTrace() }
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
@ -35,7 +35,7 @@ actual suspend fun downloadTracks(
|
||||
dir: Dir
|
||||
) {
|
||||
list.forEach {
|
||||
withContext(methods.dispatcherIO) {
|
||||
withContext(methods.value.dispatcherIO) {
|
||||
allTracksStatus[it.title] = DownloadStatus.Queued
|
||||
if (!it.videoID.isNullOrBlank()) { // Video ID already known!
|
||||
downloadTrack(it.videoID!!, it, fetcher, dir)
|
||||
@ -66,7 +66,7 @@ suspend fun downloadTrack(videoID: String, track: TrackDetails, fetcher: FetchPl
|
||||
when (it) {
|
||||
is DownloadResult.Success -> {
|
||||
println("Download Completed")
|
||||
dir.saveFileWithMetadata(it.byteArray, track)
|
||||
dir.saveFileWithMetadata(it.byteArray, track){}
|
||||
}
|
||||
is DownloadResult.Error -> {
|
||||
allTracksStatus[track.title] = DownloadStatus.Failed
|
||||
|
@ -62,7 +62,8 @@ actual class Dir actual constructor(
|
||||
@Suppress("BlockingMethodInNonBlockingContext")
|
||||
actual suspend fun saveFileWithMetadata(
|
||||
mp3ByteArray: ByteArray,
|
||||
trackDetails: TrackDetails
|
||||
trackDetails: TrackDetails,
|
||||
postProcess:(track: TrackDetails)->Unit
|
||||
) {
|
||||
val writer = ID3Writer(mp3ByteArray.toArrayBuffer())
|
||||
val albumArt = downloadFile(corsApi + trackDetails.albumArtURL)
|
||||
|
@ -28,9 +28,6 @@ kotlin {
|
||||
implementation(project(":common:data-models"))
|
||||
implementation(project(":common:database"))
|
||||
implementation(SqlDelight.coroutineExtensions)
|
||||
implementation(MVIKotlin.coroutines)
|
||||
implementation(MVIKotlin.mvikotlin)
|
||||
implementation(Decompose.decompose)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@
|
||||
package com.shabinder.common.list
|
||||
|
||||
import com.arkivanov.decompose.ComponentContext
|
||||
import com.arkivanov.decompose.value.Value
|
||||
import com.arkivanov.mvikotlin.core.store.StoreFactory
|
||||
import com.shabinder.common.di.Dir
|
||||
import com.shabinder.common.di.FetchPlatformQueryResult
|
||||
@ -26,12 +27,11 @@ import com.shabinder.common.models.Consumer
|
||||
import com.shabinder.common.models.DownloadStatus
|
||||
import com.shabinder.common.models.PlatformQueryResult
|
||||
import com.shabinder.common.models.TrackDetails
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
|
||||
interface SpotiFlyerList {
|
||||
|
||||
val models: Flow<State>
|
||||
val models: Value<State>
|
||||
|
||||
/*
|
||||
* Download All Tracks(after filtering already Downloaded)
|
||||
@ -66,9 +66,11 @@ interface SpotiFlyerList {
|
||||
val listOutput: Consumer<Output>
|
||||
val downloadProgressFlow: MutableSharedFlow<HashMap<String, DownloadStatus>>
|
||||
}
|
||||
|
||||
sealed class Output {
|
||||
object Finished : Output()
|
||||
}
|
||||
|
||||
data class State(
|
||||
val queryResult: PlatformQueryResult? = null,
|
||||
val link: String = "",
|
||||
|
@ -17,8 +17,9 @@
|
||||
package com.shabinder.common.list.integration
|
||||
|
||||
import com.arkivanov.decompose.ComponentContext
|
||||
import com.arkivanov.mvikotlin.extensions.coroutines.states
|
||||
import com.arkivanov.decompose.value.Value
|
||||
import com.shabinder.common.di.Picture
|
||||
import com.shabinder.common.di.utils.asValue
|
||||
import com.shabinder.common.list.SpotiFlyerList
|
||||
import com.shabinder.common.list.SpotiFlyerList.Dependencies
|
||||
import com.shabinder.common.list.SpotiFlyerList.State
|
||||
@ -26,7 +27,6 @@ import com.shabinder.common.list.store.SpotiFlyerListStore.Intent
|
||||
import com.shabinder.common.list.store.SpotiFlyerListStoreProvider
|
||||
import com.shabinder.common.list.store.getStore
|
||||
import com.shabinder.common.models.TrackDetails
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
internal class SpotiFlyerListImpl(
|
||||
componentContext: ComponentContext,
|
||||
@ -44,7 +44,7 @@ internal class SpotiFlyerListImpl(
|
||||
).provide()
|
||||
}
|
||||
|
||||
override val models: Flow<State> = store.states
|
||||
override val models: Value<State> = store.asValue()
|
||||
|
||||
override fun onDownloadAllClicked(trackList: List<TrackDetails>) {
|
||||
store.accept(Intent.StartDownloadAll(trackList))
|
||||
|
@ -92,7 +92,7 @@ internal class SpotiFlyerListStoreProvider(
|
||||
is Intent.StartDownloadAll -> {
|
||||
val finalList =
|
||||
intent.trackList.filter { it.downloaded == DownloadStatus.NotDownloaded }
|
||||
if (finalList.isNullOrEmpty()) methods.showPopUpMessage("All Songs are Processed")
|
||||
if (finalList.isNullOrEmpty()) methods.value.showPopUpMessage("All Songs are Processed")
|
||||
else downloadTracks(finalList, fetchQuery, dir)
|
||||
|
||||
val list = intent.trackList.map {
|
||||
@ -106,7 +106,7 @@ internal class SpotiFlyerListStoreProvider(
|
||||
downloadTracks(listOf(intent.track), fetchQuery, dir)
|
||||
dispatch(Result.UpdateTrackItem(intent.track.copy(downloaded = DownloadStatus.Queued)))
|
||||
}
|
||||
is Intent.RefreshTracksStatuses -> methods.queryActiveTracks()
|
||||
is Intent.RefreshTracksStatuses -> methods.value.queryActiveTracks()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,9 +28,6 @@ kotlin {
|
||||
implementation(project(":common:data-models"))
|
||||
implementation(project(":common:database"))
|
||||
implementation(SqlDelight.coroutineExtensions)
|
||||
implementation(MVIKotlin.coroutines)
|
||||
implementation(MVIKotlin.mvikotlin)
|
||||
implementation(Decompose.decompose)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@
|
||||
package com.shabinder.common.main
|
||||
|
||||
import com.arkivanov.decompose.ComponentContext
|
||||
import com.arkivanov.decompose.value.Value
|
||||
import com.arkivanov.mvikotlin.core.store.StoreFactory
|
||||
import com.shabinder.common.di.Dir
|
||||
import com.shabinder.common.di.Picture
|
||||
@ -28,7 +29,7 @@ import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface SpotiFlyerMain {
|
||||
|
||||
val models: Flow<State>
|
||||
val models: Value<State>
|
||||
|
||||
/*
|
||||
* We Intend to Move to List Screen
|
||||
@ -67,6 +68,7 @@ interface SpotiFlyerMain {
|
||||
val link: String = "",
|
||||
val selectedCategory: HomeCategory = HomeCategory.About
|
||||
)
|
||||
|
||||
enum class HomeCategory {
|
||||
About, History
|
||||
}
|
||||
|
@ -17,8 +17,9 @@
|
||||
package com.shabinder.common.main.integration
|
||||
|
||||
import com.arkivanov.decompose.ComponentContext
|
||||
import com.arkivanov.mvikotlin.extensions.coroutines.states
|
||||
import com.arkivanov.decompose.value.Value
|
||||
import com.shabinder.common.di.Picture
|
||||
import com.shabinder.common.di.utils.asValue
|
||||
import com.shabinder.common.main.SpotiFlyerMain
|
||||
import com.shabinder.common.main.SpotiFlyerMain.Dependencies
|
||||
import com.shabinder.common.main.SpotiFlyerMain.HomeCategory
|
||||
@ -28,7 +29,6 @@ import com.shabinder.common.main.store.SpotiFlyerMainStore.Intent
|
||||
import com.shabinder.common.main.store.SpotiFlyerMainStoreProvider
|
||||
import com.shabinder.common.main.store.getStore
|
||||
import com.shabinder.common.models.methods
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
internal class SpotiFlyerMainImpl(
|
||||
componentContext: ComponentContext,
|
||||
@ -43,11 +43,11 @@ internal class SpotiFlyerMainImpl(
|
||||
).provide()
|
||||
}
|
||||
|
||||
override val models: Flow<State> = store.states
|
||||
override val models: Value<State> = store.asValue()
|
||||
|
||||
override fun onLinkSearch(link: String) {
|
||||
if (methods.isInternetAvailable) mainOutput.callback(Output.Search(link = link))
|
||||
else methods.showPopUpMessage("Check Network Connection Please")
|
||||
if (methods.value.isInternetAvailable) mainOutput.callback(Output.Search(link = link))
|
||||
else methods.value.showPopUpMessage("Check Network Connection Please")
|
||||
}
|
||||
|
||||
override fun onInputLinkChanged(link: String) {
|
||||
|
@ -78,9 +78,9 @@ internal class SpotiFlyerMainStoreProvider(
|
||||
|
||||
override suspend fun executeIntent(intent: Intent, getState: () -> State) {
|
||||
when (intent) {
|
||||
is Intent.OpenPlatform -> methods.openPlatform(intent.platformID, intent.platformLink)
|
||||
is Intent.GiveDonation -> methods.giveDonation()
|
||||
is Intent.ShareApp -> methods.shareApp()
|
||||
is Intent.OpenPlatform -> methods.value.openPlatform(intent.platformID, intent.platformLink)
|
||||
is Intent.GiveDonation -> methods.value.giveDonation()
|
||||
is Intent.ShareApp -> methods.value.shareApp()
|
||||
is Intent.SetLink -> dispatch(Result.LinkChanged(link = intent.link))
|
||||
is Intent.SelectCategory -> dispatch(Result.CategoryChanged(intent.category))
|
||||
}
|
||||
|
@ -18,69 +18,44 @@ plugins {
|
||||
id("multiplatform-setup")
|
||||
id("android-setup")
|
||||
id("kotlin-parcelize")
|
||||
//kotlin("native.cocoapods")
|
||||
}
|
||||
|
||||
// Required be cocoapods
|
||||
version = "1.0"
|
||||
fun org.jetbrains.kotlin.gradle.dsl.KotlinNativeBinaryContainer.generateFramework() {
|
||||
framework {
|
||||
baseName = "SpotiFlyer"
|
||||
linkerOpts.add("-lsqlite3")
|
||||
export(project(":common:dependency-injection"))
|
||||
export(project(":common:data-models"))
|
||||
export(project(":common:database"))
|
||||
export(project(":common:main"))
|
||||
export(project(":common:list"))
|
||||
export(Decompose.decompose)
|
||||
export(MVIKotlin.mvikotlinMain)
|
||||
export(MVIKotlin.mvikotlinLogging)
|
||||
}
|
||||
}
|
||||
|
||||
kotlin {
|
||||
|
||||
/*IOS Target Can be only built on Mac*/
|
||||
if(HostOS.isMac){
|
||||
if(HostOS.isMac) {
|
||||
val sdkName: String? = System.getenv("SDK_NAME")
|
||||
val isiOSDevice = sdkName.orEmpty().startsWith("iphoneos")
|
||||
if (isiOSDevice) {
|
||||
iosArm64("ios"){
|
||||
binaries {
|
||||
framework {
|
||||
baseName = "SpotiFlyer"
|
||||
linkerOpts.add("-lsqlite3")
|
||||
export(project(":common:database"))
|
||||
export(project(":common:main"))
|
||||
export(project(":common:list"))
|
||||
export(project(":common:dependency-injection"))
|
||||
export(project(":common:data-models"))
|
||||
export(Decompose.decompose)
|
||||
export(MVIKotlin.mvikotlin)
|
||||
}
|
||||
generateFramework()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
iosX64("ios"){
|
||||
binaries {
|
||||
framework {
|
||||
baseName = "SpotiFlyer"
|
||||
linkerOpts.add("-lsqlite3")
|
||||
export(project(":common:database"))
|
||||
export(project(":common:main"))
|
||||
export(project(":common:list"))
|
||||
export(project(":common:dependency-injection"))
|
||||
export(project(":common:data-models"))
|
||||
export(Decompose.decompose)
|
||||
export(MVIKotlin.mvikotlin)
|
||||
}
|
||||
generateFramework()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*cocoapods {
|
||||
// Configure fields required by CocoaPods.
|
||||
summary = "SpotiFlyer Native Module"
|
||||
homepage = "https://github.com/Shabinder/SpotiFlyer"
|
||||
authors = "Shabinder Singh"
|
||||
// You can change the name of the produced framework.
|
||||
// By default, it is the name of the Gradle project.
|
||||
frameworkName = "SpotiFlyer"
|
||||
ios.deploymentTarget = "11.0"
|
||||
|
||||
*//*pod("dependency_injection"){
|
||||
version = "1.0"
|
||||
source = path(rootProject.file("common/dependency-injection"))
|
||||
}*//*
|
||||
}*/
|
||||
|
||||
sourceSets {
|
||||
commonMain {
|
||||
dependencies {
|
||||
@ -90,12 +65,11 @@ kotlin {
|
||||
implementation(project(":common:list"))
|
||||
implementation(project(":common:main"))
|
||||
implementation(SqlDelight.coroutineExtensions)
|
||||
implementation(MVIKotlin.coroutines)
|
||||
implementation(MVIKotlin.mvikotlin)
|
||||
implementation(Decompose.decompose)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*Required to Export `packForXcode`*/
|
||||
sourceSets {
|
||||
named("iosMain") {
|
||||
dependencies {
|
||||
@ -105,7 +79,8 @@ kotlin {
|
||||
api(project(":common:list"))
|
||||
api(project(":common:main"))
|
||||
api(Decompose.decompose)
|
||||
api(MVIKotlin.mvikotlin)
|
||||
api(MVIKotlin.mvikotlinMain)
|
||||
api(MVIKotlin.mvikotlinLogging)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -115,7 +90,8 @@ val packForXcode by tasks.creating(Sync::class) {
|
||||
group = "build"
|
||||
val mode = System.getenv("CONFIGURATION") ?: "DEBUG"
|
||||
val targetName = "ios"
|
||||
val framework = kotlin.targets.getByName<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget>(targetName).binaries.getFramework(mode)
|
||||
val framework = kotlin.targets.getByName<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget>(targetName)
|
||||
.binaries.getFramework(mode)
|
||||
inputs.property("mode", mode)
|
||||
dependsOn(framework.linkTask)
|
||||
val targetDir = File(buildDir, "xcode-frameworks")
|
||||
|
@ -1,46 +0,0 @@
|
||||
Pod::Spec.new do |spec|
|
||||
spec.name = 'root'
|
||||
spec.version = '1.0'
|
||||
spec.homepage = 'https://github.com/Shabinder/SpotiFlyer'
|
||||
spec.source = { :git => "Not Published", :tag => "Cocoapods/#{spec.name}/#{spec.version}" }
|
||||
spec.authors = 'Shabinder Singh'
|
||||
spec.license = ''
|
||||
spec.summary = 'SpotiFlyer Native Module'
|
||||
|
||||
spec.static_framework = true
|
||||
spec.vendored_frameworks = "build/cocoapods/framework/SpotiFlyer.framework"
|
||||
spec.libraries = "c++"
|
||||
spec.module_name = "#{spec.name}_umbrella"
|
||||
|
||||
spec.ios.deployment_target = '11.0'
|
||||
|
||||
|
||||
|
||||
spec.pod_target_xcconfig = {
|
||||
'KOTLIN_TARGET[sdk=iphonesimulator*]' => 'ios_x64',
|
||||
'KOTLIN_TARGET[sdk=iphoneos*]' => 'ios_arm',
|
||||
'KOTLIN_TARGET[sdk=watchsimulator*]' => 'watchos_x64',
|
||||
'KOTLIN_TARGET[sdk=watchos*]' => 'watchos_arm',
|
||||
'KOTLIN_TARGET[sdk=appletvsimulator*]' => 'tvos_x64',
|
||||
'KOTLIN_TARGET[sdk=appletvos*]' => 'tvos_arm64',
|
||||
'KOTLIN_TARGET[sdk=macosx*]' => 'macos_x64'
|
||||
}
|
||||
|
||||
spec.script_phases = [
|
||||
{
|
||||
:name => 'Build root',
|
||||
:execution_position => :before_compile,
|
||||
:shell_path => '/bin/sh',
|
||||
:script => <<-SCRIPT
|
||||
set -ev
|
||||
REPO_ROOT="$PODS_TARGET_SRCROOT"
|
||||
"$REPO_ROOT/../../gradlew" -p "$REPO_ROOT" :common:root:syncFramework \
|
||||
-Pkotlin.native.cocoapods.target=$KOTLIN_TARGET \
|
||||
-Pkotlin.native.cocoapods.configuration=$CONFIGURATION \
|
||||
-Pkotlin.native.cocoapods.cflags="$OTHER_CFLAGS" \
|
||||
-Pkotlin.native.cocoapods.paths.headers="$HEADER_SEARCH_PATHS" \
|
||||
-Pkotlin.native.cocoapods.paths.frameworks="$FRAMEWORK_SEARCH_PATHS"
|
||||
SCRIPT
|
||||
}
|
||||
]
|
||||
end
|
@ -16,6 +16,7 @@
|
||||
|
||||
package com.shabinder.common.root.integration
|
||||
|
||||
import co.touchlab.stately.freeze
|
||||
import com.arkivanov.decompose.ComponentContext
|
||||
import com.arkivanov.decompose.RouterState
|
||||
import com.arkivanov.decompose.pop
|
||||
@ -36,6 +37,7 @@ import com.shabinder.common.root.SpotiFlyerRoot
|
||||
import com.shabinder.common.root.SpotiFlyerRoot.Child
|
||||
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.launch
|
||||
|
||||
@ -45,12 +47,12 @@ internal class SpotiFlyerRootImpl(
|
||||
) : SpotiFlyerRoot, ComponentContext by componentContext, Dependencies by dependencies, Actions by dependencies.actions {
|
||||
|
||||
init {
|
||||
methods = actions
|
||||
GlobalScope.launch {
|
||||
/*Authenticate Spotify Client*/
|
||||
if (methods.currentPlatform is AllPlatforms.Js) {
|
||||
methods.value = actions.freeze()
|
||||
GlobalScope.launch(Dispatchers.Default) {
|
||||
//*Authenticate Spotify Client*//*
|
||||
/*if (methods.value.currentPlatform is AllPlatforms.Js) {
|
||||
fetchPlatformQueryResult.spotifyProvider.authenticateSpotifyClient(override = true)
|
||||
} else fetchPlatformQueryResult.spotifyProvider.authenticateSpotifyClient()
|
||||
} else fetchPlatformQueryResult.spotifyProvider.authenticateSpotifyClient()*/
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -41,5 +41,6 @@ kotlin.native.disableCompilerDaemon=true
|
||||
kotlin.mpp.stability.nowarn=true
|
||||
kotlin.mpp.enableGranularSourceSetsMetadata=true
|
||||
kotlin.native.enableDependencyPropagation=false
|
||||
kotlin.native.cacheKind=none
|
||||
xcodeproj=./spotiflyer-ios
|
||||
#kotlin.native.cacheKind=none
|
||||
#org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=1024m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit d7df9dda6df716aff3d78dda0d522b57b5a2c0c6
|
||||
Subproject commit 31517f90ef04efa4aea88c61ca627647c146f471
|
Loading…
Reference in New Issue
Block a user