mirror of
https://github.com/Shabinder/SpotiFlyer.git
synced 2024-11-22 09:04:32 +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 {
|
plugins {
|
||||||
id("com.android.application")
|
id("com.android.application")
|
||||||
kotlin("android")
|
kotlin("android")
|
||||||
|
id("kotlin-parcelize")
|
||||||
id("org.jetbrains.compose")
|
id("org.jetbrains.compose")
|
||||||
id("com.google.gms.google-services")
|
id("com.google.gms.google-services")
|
||||||
id("com.google.firebase.crashlytics")
|
id("com.google.firebase.crashlytics")
|
||||||
@ -96,37 +97,42 @@ dependencies {
|
|||||||
implementation(compose.materialIconsExtended)
|
implementation(compose.materialIconsExtended)
|
||||||
implementation(Androidx.androidxActivity)
|
implementation(Androidx.androidxActivity)
|
||||||
|
|
||||||
|
// Project's SubModules
|
||||||
implementation(project(":common:database"))
|
implementation(project(":common:database"))
|
||||||
implementation(project(":common:compose"))
|
implementation(project(":common:compose"))
|
||||||
implementation(project(":common:root"))
|
implementation(project(":common:root"))
|
||||||
implementation(project(":common:dependency-injection"))
|
implementation(project(":common:dependency-injection"))
|
||||||
implementation(project(":common:data-models"))
|
implementation(project(":common:data-models"))
|
||||||
|
|
||||||
|
// Koin
|
||||||
implementation(Koin.android)
|
implementation(Koin.android)
|
||||||
implementation(Koin.compose)
|
implementation(Koin.compose)
|
||||||
|
|
||||||
implementation("com.google.accompanist:accompanist-insets:0.8.1")
|
|
||||||
|
|
||||||
// DECOMPOSE
|
// DECOMPOSE
|
||||||
implementation(Decompose.decompose)
|
implementation(Decompose.decompose)
|
||||||
implementation(Decompose.extensionsCompose)
|
implementation(Decompose.extensionsCompose)
|
||||||
|
|
||||||
|
// MVI
|
||||||
|
implementation(MVIKotlin.mvikotlin)
|
||||||
|
implementation(MVIKotlin.mvikotlinMain)
|
||||||
|
implementation(MVIKotlin.mvikotlinLogging)
|
||||||
|
implementation(MVIKotlin.mvikotlinTimeTravel)
|
||||||
|
|
||||||
// Firebase
|
// Firebase
|
||||||
implementation(platform("com.google.firebase:firebase-bom:27.1.0"))
|
implementation(platform("com.google.firebase:firebase-bom:27.1.0"))
|
||||||
implementation("com.google.firebase:firebase-analytics-ktx")
|
implementation("com.google.firebase:firebase-analytics-ktx")
|
||||||
implementation("com.google.firebase:firebase-crashlytics-ktx")
|
implementation("com.google.firebase:firebase-crashlytics-ktx")
|
||||||
implementation("com.google.firebase:firebase-perf-ktx")
|
implementation("com.google.firebase:firebase-perf-ktx")
|
||||||
|
|
||||||
|
// Extras
|
||||||
Extras.Android.apply {
|
Extras.Android.apply {
|
||||||
implementation(appUpdator)
|
implementation(appUpdator)
|
||||||
implementation(razorpay)
|
implementation(razorpay)
|
||||||
}
|
}
|
||||||
implementation(MVIKotlin.mvikotlin)
|
|
||||||
implementation(MVIKotlin.mvikotlinMain)
|
implementation("dev.icerock.moko:parcelize:0.6.1")
|
||||||
implementation(MVIKotlin.mvikotlinLogging)
|
implementation("com.github.shabinder:storage-chooser:2.0.4.45")
|
||||||
implementation(MVIKotlin.mvikotlinTimeTravel)
|
implementation("com.google.accompanist:accompanist-insets:0.8.1")
|
||||||
implementation(Decompose.decompose)
|
|
||||||
implementation(Decompose.extensionsCompose)
|
|
||||||
|
|
||||||
// Test
|
// Test
|
||||||
testImplementation("junit:junit:4.13.2")
|
testImplementation("junit:junit:4.13.2")
|
||||||
|
@ -210,14 +210,14 @@ class MainActivity : ComponentActivity(), PaymentResultListener {
|
|||||||
)
|
)
|
||||||
|
|
||||||
override fun addToLibrary(path: String) {
|
override fun addToLibrary(path: String) {
|
||||||
MediaScannerConnection.scanFile(
|
MediaScannerConnection.scanFile (
|
||||||
applicationContext,
|
applicationContext,
|
||||||
listOf(path).toTypedArray(), null, null
|
listOf(path).toTypedArray(), null, null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun sendTracksToService(array: ArrayList<TrackDetails>) {
|
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)
|
val serviceIntent = Intent(this@MainActivity, ForegroundService::class.java)
|
||||||
serviceIntent.putParcelableArrayListExtra("object", list as ArrayList)
|
serviceIntent.putParcelableArrayListExtra("object", list as ArrayList)
|
||||||
ContextCompat.startForegroundService(this@MainActivity, serviceIntent)
|
ContextCompat.startForegroundService(this@MainActivity, serviceIntent)
|
||||||
@ -378,7 +378,7 @@ class MainActivity : ComponentActivity(), PaymentResultListener {
|
|||||||
while(!this@MainActivity::root.isInitialized){
|
while(!this@MainActivity::root.isInitialized){
|
||||||
delay(100)
|
delay(100)
|
||||||
}
|
}
|
||||||
if(methods.isInternetAvailable)callBacks.searchLink(link)
|
if(methods.value.isInternetAvailable)callBacks.searchLink(link)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,6 @@ plugins {
|
|||||||
allprojects {
|
allprojects {
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
jcenter()
|
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
mavenLocal()
|
mavenLocal()
|
||||||
maven(url = "https://jitpack.io")
|
maven(url = "https://jitpack.io")
|
||||||
|
@ -22,10 +22,9 @@ group = "com.shabinder"
|
|||||||
version = "2.1"
|
version = "2.1"
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
jcenter()
|
google()
|
||||||
mavenLocal()
|
mavenLocal()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
google()
|
|
||||||
maven(url = "https://jitpack.io")
|
maven(url = "https://jitpack.io")
|
||||||
maven(url = "https://plugins.gradle.org/m2/")
|
maven(url = "https://plugins.gradle.org/m2/")
|
||||||
maven(url = "https://dl.bintray.com/kotlin/kotlin-js-wrappers")
|
maven(url = "https://dl.bintray.com/kotlin/kotlin-js-wrappers")
|
||||||
|
@ -18,6 +18,7 @@ plugins {
|
|||||||
id("com.android.library")
|
id("com.android.library")
|
||||||
id("kotlin-multiplatform")
|
id("kotlin-multiplatform")
|
||||||
id("org.jetbrains.compose")
|
id("org.jetbrains.compose")
|
||||||
|
id("kotlin-parcelize")
|
||||||
id("ktlint-setup")
|
id("ktlint-setup")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,10 +36,22 @@ kotlin {
|
|||||||
sourceSets {
|
sourceSets {
|
||||||
named("commonMain") {
|
named("commonMain") {
|
||||||
dependencies {
|
dependencies {
|
||||||
|
// Decompose
|
||||||
|
implementation(Decompose.decompose)
|
||||||
|
|
||||||
|
// MVI
|
||||||
|
implementation(MVIKotlin.coroutines)
|
||||||
|
implementation(MVIKotlin.mvikotlin)
|
||||||
|
|
||||||
implementation(compose.runtime)
|
implementation(compose.runtime)
|
||||||
implementation(compose.foundation)
|
implementation(compose.foundation)
|
||||||
implementation(compose.material)
|
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("kotlin-multiplatform")
|
||||||
id("org.jetbrains.compose")
|
id("org.jetbrains.compose")
|
||||||
id("ktlint-setup")
|
id("ktlint-setup")
|
||||||
|
id("kotlin-parcelize")
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
@ -30,7 +31,7 @@ kotlin {
|
|||||||
if (isiOSDevice) {
|
if (isiOSDevice) {
|
||||||
iosArm64("ios")
|
iosArm64("ios")
|
||||||
} else {
|
} else {
|
||||||
iosX64("ios")
|
iosX64("ios") {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,7 +57,32 @@ kotlin {
|
|||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
named("commonMain") {
|
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") {
|
named("androidMain") {
|
||||||
@ -67,8 +93,9 @@ kotlin {
|
|||||||
implementation(compose.material)
|
implementation(compose.material)
|
||||||
implementation(compose.foundation)
|
implementation(compose.foundation)
|
||||||
implementation(compose.materialIconsExtended)
|
implementation(compose.materialIconsExtended)
|
||||||
implementation(Decompose.decompose)
|
|
||||||
implementation(Decompose.extensionsCompose)
|
implementation(Decompose.extensionsCompose)
|
||||||
|
implementation(Ktor.clientAndroid)
|
||||||
|
implementation(Koin.android)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,12 +106,14 @@ kotlin {
|
|||||||
implementation(compose.material)
|
implementation(compose.material)
|
||||||
implementation(compose.desktop.common)
|
implementation(compose.desktop.common)
|
||||||
implementation(compose.materialIconsExtended)
|
implementation(compose.materialIconsExtended)
|
||||||
implementation(Decompose.decompose)
|
|
||||||
implementation(Decompose.extensionsCompose)
|
implementation(Decompose.extensionsCompose)
|
||||||
|
implementation(Ktor.clientApache)
|
||||||
|
implementation(Ktor.slf4j)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
named("jsMain") {
|
named("jsMain") {
|
||||||
dependencies {
|
dependencies {
|
||||||
|
implementation(Ktor.clientJs)
|
||||||
implementation("org.jetbrains:kotlin-react:17.0.1-pre.148-kotlin-1.4.30")
|
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-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")
|
implementation("org.jetbrains:kotlin-react-dom:17.0.1-pre.148-kotlin-1.4.30")
|
||||||
@ -92,7 +121,9 @@ kotlin {
|
|||||||
}
|
}
|
||||||
if(HostOS.isMac){
|
if(HostOS.isMac){
|
||||||
named("iosMain"){
|
named("iosMain"){
|
||||||
dependencies { }
|
dependencies {
|
||||||
|
implementation(Ktor.clientIos)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,6 @@ import org.jetbrains.compose.compose
|
|||||||
plugins {
|
plugins {
|
||||||
id("multiplatform-compose-setup")
|
id("multiplatform-compose-setup")
|
||||||
id("android-setup")
|
id("android-setup")
|
||||||
id("kotlin-parcelize")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
@ -33,8 +32,6 @@ kotlin {
|
|||||||
implementation(project(":common:database"))
|
implementation(project(":common:database"))
|
||||||
implementation(project(":common:data-models"))
|
implementation(project(":common:data-models"))
|
||||||
implementation(project(":common:dependency-injection"))
|
implementation(project(":common:dependency-injection"))
|
||||||
// DECOMPOSE
|
|
||||||
implementation(Decompose.decompose)
|
|
||||||
implementation(Decompose.extensionsCompose)
|
implementation(Decompose.extensionsCompose)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,7 @@ actual fun ImageLoad(
|
|||||||
var pic by remember(link) { mutableStateOf<ImageBitmap?>(null) }
|
var pic by remember(link) { mutableStateOf<ImageBitmap?>(null) }
|
||||||
|
|
||||||
LaunchedEffect(link) {
|
LaunchedEffect(link) {
|
||||||
withContext(methods.dispatcherIO) {
|
withContext(methods.value.dispatcherIO) {
|
||||||
pic = loader(link).image
|
pic = loader(link).image
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,6 @@ import androidx.compose.material.MaterialTheme
|
|||||||
import androidx.compose.material.Text
|
import androidx.compose.material.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.collectAsState
|
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
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.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
|
import com.arkivanov.decompose.extensions.compose.jetbrains.asState
|
||||||
import com.shabinder.common.di.Picture
|
import com.shabinder.common.di.Picture
|
||||||
import com.shabinder.common.list.SpotiFlyerList
|
import com.shabinder.common.list.SpotiFlyerList
|
||||||
import com.shabinder.common.models.DownloadStatus
|
import com.shabinder.common.models.DownloadStatus
|
||||||
import com.shabinder.common.models.TrackDetails
|
import com.shabinder.common.models.TrackDetails
|
||||||
import com.shabinder.common.models.methods
|
import com.shabinder.common.models.methods
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SpotiFlyerListContent(
|
fun SpotiFlyerListContent(
|
||||||
component: SpotiFlyerList,
|
component: SpotiFlyerList,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
val model by component.models.collectAsState(SpotiFlyerList.State())
|
val model by component.models.asState()
|
||||||
|
|
||||||
LaunchedEffect(model.errorOccurred) {
|
LaunchedEffect(model.errorOccurred) {
|
||||||
/*Handle if Any Exception Occurred*/
|
/*Handle if Any Exception Occurred*/
|
||||||
model.errorOccurred?.let {
|
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()
|
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.Flag
|
||||||
import androidx.compose.material.icons.rounded.Share
|
import androidx.compose.material.icons.rounded.Share
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.collectAsState
|
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
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.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
|
import com.arkivanov.decompose.extensions.compose.jetbrains.asState
|
||||||
import com.shabinder.common.di.Picture
|
import com.shabinder.common.di.Picture
|
||||||
import com.shabinder.common.main.SpotiFlyerMain
|
import com.shabinder.common.main.SpotiFlyerMain
|
||||||
import com.shabinder.common.main.SpotiFlyerMain.HomeCategory
|
import com.shabinder.common.main.SpotiFlyerMain.HomeCategory
|
||||||
@ -80,7 +80,7 @@ import com.shabinder.common.models.methods
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SpotiFlyerMainContent(component: SpotiFlyerMain) {
|
fun SpotiFlyerMainContent(component: SpotiFlyerMain) {
|
||||||
val model by component.models.collectAsState(SpotiFlyerMain.State())
|
val model by component.models.asState()
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
SearchPanel(
|
SearchPanel(
|
||||||
@ -193,7 +193,7 @@ fun SearchPanel(
|
|||||||
OutlinedButton(
|
OutlinedButton(
|
||||||
modifier = Modifier.padding(12.dp).wrapContentWidth(),
|
modifier = Modifier.padding(12.dp).wrapContentWidth(),
|
||||||
onClick = {
|
onClick = {
|
||||||
if (link.isBlank()) methods.showPopUpMessage("Enter A Link!")
|
if (link.isBlank()) methods.value.showPopUpMessage("Enter A Link!")
|
||||||
else {
|
else {
|
||||||
// TODO if(!isOnline(ctx)) showPopUpMessage("Check Your Internet Connection") else
|
// TODO if(!isOnline(ctx)) showPopUpMessage("Check Your Internet Connection") else
|
||||||
onSearch(link)
|
onSearch(link)
|
||||||
@ -235,7 +235,7 @@ fun AboutColumn(modifier: Modifier = Modifier) {
|
|||||||
"Open Spotify",
|
"Open Spotify",
|
||||||
tint = Color.Unspecified,
|
tint = Color.Unspecified,
|
||||||
modifier = Modifier.clip(SpotiFlyerShapes.small).clickable(
|
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))
|
Spacer(modifier = modifier.padding(start = 16.dp))
|
||||||
@ -244,7 +244,7 @@ fun AboutColumn(modifier: Modifier = Modifier) {
|
|||||||
"Open Gaana",
|
"Open Gaana",
|
||||||
tint = Color.Unspecified,
|
tint = Color.Unspecified,
|
||||||
modifier = Modifier.clip(SpotiFlyerShapes.small).clickable(
|
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))
|
Spacer(modifier = modifier.padding(start = 16.dp))
|
||||||
@ -253,7 +253,7 @@ fun AboutColumn(modifier: Modifier = Modifier) {
|
|||||||
"Open Youtube",
|
"Open Youtube",
|
||||||
tint = Color.Unspecified,
|
tint = Color.Unspecified,
|
||||||
modifier = Modifier.clip(SpotiFlyerShapes.small).clickable(
|
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))
|
Spacer(modifier = modifier.padding(start = 12.dp))
|
||||||
@ -262,7 +262,7 @@ fun AboutColumn(modifier: Modifier = Modifier) {
|
|||||||
"Open Youtube Music",
|
"Open Youtube Music",
|
||||||
tint = Color.Unspecified,
|
tint = Color.Unspecified,
|
||||||
modifier = Modifier.clip(SpotiFlyerShapes.small).clickable(
|
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(
|
Row(
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
modifier = Modifier.fillMaxWidth().clickable(
|
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)
|
.padding(vertical = 6.dp)
|
||||||
) {
|
) {
|
||||||
@ -302,7 +302,7 @@ fun AboutColumn(modifier: Modifier = Modifier) {
|
|||||||
}
|
}
|
||||||
Row(
|
Row(
|
||||||
modifier = modifier.fillMaxWidth().padding(vertical = 6.dp)
|
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
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
Icon(Icons.Rounded.Flag, "Help Translate", Modifier.size(32.dp))
|
Icon(Icons.Rounded.Flag, "Help Translate", Modifier.size(32.dp))
|
||||||
@ -320,7 +320,7 @@ fun AboutColumn(modifier: Modifier = Modifier) {
|
|||||||
}
|
}
|
||||||
Row(
|
Row(
|
||||||
modifier = modifier.fillMaxWidth().padding(vertical = 6.dp)
|
modifier = modifier.fillMaxWidth().padding(vertical = 6.dp)
|
||||||
.clickable(onClick = { methods.giveDonation() }),
|
.clickable(onClick = { methods.value.giveDonation() }),
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
Icon(Icons.Rounded.CardGiftcard, "Support Developer")
|
Icon(Icons.Rounded.CardGiftcard, "Support Developer")
|
||||||
@ -340,7 +340,7 @@ fun AboutColumn(modifier: Modifier = Modifier) {
|
|||||||
modifier = modifier.fillMaxWidth().padding(vertical = 6.dp)
|
modifier = modifier.fillMaxWidth().padding(vertical = 6.dp)
|
||||||
.clickable(
|
.clickable(
|
||||||
onClick = {
|
onClick = {
|
||||||
methods.shareApp()
|
methods.value.shareApp()
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
@ -48,7 +48,7 @@ actual fun ImageLoad(
|
|||||||
) {
|
) {
|
||||||
var pic by remember(link) { mutableStateOf<ImageBitmap?>(null) }
|
var pic by remember(link) { mutableStateOf<ImageBitmap?>(null) }
|
||||||
LaunchedEffect(link) {
|
LaunchedEffect(link) {
|
||||||
withContext(methods.dispatcherIO) {
|
withContext(methods.value.dispatcherIO) {
|
||||||
pic = loader(link).image
|
pic = loader(link).image
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,16 +24,7 @@ plugins {
|
|||||||
kotlin {
|
kotlin {
|
||||||
sourceSets {
|
sourceSets {
|
||||||
commonMain {
|
commonMain {
|
||||||
dependencies {
|
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 {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
package com.shabinder.common.models
|
||||||
|
|
||||||
|
import co.touchlab.stately.freeze
|
||||||
import kotlinx.coroutines.CoroutineDispatcher
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Holder to call platform actions from anywhere
|
* Holder to call platform actions from anywhere
|
||||||
* */
|
* */
|
||||||
lateinit var methods: Actions
|
var methods: NativeAtomicReference<Actions> = NativeAtomicReference(stubActions().freeze())
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Interface Having All Platform Dependent Functions
|
* Interface Having All Platform Dependent Functions
|
||||||
@ -45,3 +47,17 @@ interface Actions {
|
|||||||
// Current Platform Info
|
// Current Platform Info
|
||||||
val currentPlatform: AllPlatforms
|
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 {
|
commonMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":common:data-models"))
|
implementation(project(":common:data-models"))
|
||||||
|
|
||||||
// SQL Delight
|
// SQL Delight
|
||||||
implementation(SqlDelight.runtime)
|
implementation(SqlDelight.runtime)
|
||||||
implementation(SqlDelight.coroutineExtensions)
|
implementation(SqlDelight.coroutineExtensions)
|
||||||
api(Extras.kermit)
|
|
||||||
// koin
|
// koin
|
||||||
api(Koin.core)
|
implementation(Koin.test)
|
||||||
api(Koin.test)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,7 +53,8 @@ kotlin {
|
|||||||
implementation(SqlDelight.jdbcDriver)
|
implementation(SqlDelight.jdbcDriver)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(HostOS.isMac){
|
|
||||||
|
if(HostOS.isMac) {
|
||||||
val iosMain by getting {
|
val iosMain by getting {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(SqlDelight.nativeDriver)
|
implementation(SqlDelight.nativeDriver)
|
||||||
|
@ -20,75 +20,43 @@ plugins {
|
|||||||
id("multiplatform-setup")
|
id("multiplatform-setup")
|
||||||
id("android-setup")
|
id("android-setup")
|
||||||
kotlin("plugin.serialization")
|
kotlin("plugin.serialization")
|
||||||
kotlin("native.cocoapods")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
version = "1.0"
|
|
||||||
|
|
||||||
kotlin {
|
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 {
|
sourceSets {
|
||||||
commonMain {
|
commonMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":common:data-models"))
|
implementation(project(":common:data-models"))
|
||||||
implementation(project(":common:database"))
|
implementation(project(":common:database"))
|
||||||
implementation("org.jetbrains.kotlinx:atomicfu:0.16.1")
|
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("org.jetbrains.kotlinx:kotlinx-datetime:0.2.0")
|
||||||
implementation("com.shabinder.fuzzywuzzy:fuzzywuzzy:1.0")
|
implementation("com.shabinder.fuzzywuzzy:fuzzywuzzy:1.0")
|
||||||
api(Ktor.clientCore)
|
implementation(Extras.youtubeDownloader)
|
||||||
api(Ktor.clientSerialization)
|
implementation(MVIKotlin.rx)
|
||||||
api(Ktor.clientLogging)
|
|
||||||
api(Ktor.clientJson)
|
|
||||||
api(Ktor.auth)
|
|
||||||
api(Extras.youtubeDownloader)
|
|
||||||
api(Extras.kermit)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
androidMain {
|
androidMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(compose.materialIconsExtended)
|
implementation(compose.materialIconsExtended)
|
||||||
api(Koin.android)
|
implementation(Extras.Android.razorpay)
|
||||||
api(Ktor.clientAndroid)
|
implementation(Extras.mp3agic)
|
||||||
api(Extras.Android.razorpay)
|
//implementation(Extras.jaudioTagger)
|
||||||
api(Extras.mp3agic)
|
implementation("com.github.shabinder:storage-chooser:2.0.4.45")
|
||||||
api(Extras.jaudioTagger)
|
// implementation(files("$rootDir/libs/mobile-ffmpeg.aar"))
|
||||||
api("com.github.shabinder:storage-chooser:2.0.4.45")
|
|
||||||
// api(files("$rootDir/libs/mobile-ffmpeg.aar"))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
desktopMain {
|
desktopMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(compose.materialIconsExtended)
|
implementation(compose.materialIconsExtended)
|
||||||
api(Ktor.clientApache)
|
implementation(Extras.mp3agic)
|
||||||
api(Ktor.slf4j)
|
//implementation(Extras.jaudioTagger)
|
||||||
api(Extras.mp3agic)
|
|
||||||
api(Extras.jaudioTagger)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
jsMain {
|
jsMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(npm("browser-id3-writer", "4.4.0"))
|
implementation(npm("browser-id3-writer", "4.4.0"))
|
||||||
implementation(npm("file-saver", "2.0.4"))
|
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
|
dir: Dir
|
||||||
) {
|
) {
|
||||||
if (!list.isNullOrEmpty()) {
|
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"
|
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){
|
fun setDownloadDirectory(newBasePath:String){
|
||||||
sharedPreferences.edit().putString(DirKey,newBasePath).apply()
|
sharedPreferences.edit().putString(DirKey,newBasePath).apply()
|
||||||
@ -57,7 +57,7 @@ actual class Dir actual constructor(
|
|||||||
|
|
||||||
actual fun fileSeparator(): String = File.separator
|
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
|
// fun call in order to always access Updated Value
|
||||||
actual fun defaultDir(): String = sharedPreferences.getString(DirKey,defaultBaseDir)!! + File.separator +
|
actual fun defaultDir(): String = sharedPreferences.getString(DirKey,defaultBaseDir)!! + File.separator +
|
||||||
@ -84,7 +84,8 @@ actual class Dir actual constructor(
|
|||||||
@Suppress("BlockingMethodInNonBlockingContext")
|
@Suppress("BlockingMethodInNonBlockingContext")
|
||||||
actual suspend fun saveFileWithMetadata(
|
actual suspend fun saveFileWithMetadata(
|
||||||
mp3ByteArray: ByteArray,
|
mp3ByteArray: ByteArray,
|
||||||
trackDetails: TrackDetails
|
trackDetails: TrackDetails,
|
||||||
|
postProcess:(track: TrackDetails)->Unit
|
||||||
) {
|
) {
|
||||||
withContext(Dispatchers.IO){
|
withContext(Dispatchers.IO){
|
||||||
val songFile = File(trackDetails.outputFilePath)
|
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){
|
actual suspend fun loadImage(url: String): Picture = withContext(Dispatchers.IO){
|
||||||
val cachePath = imageCacheDir() + getNameURL(url)
|
val cachePath = imageCacheDir() + getNameURL(url)
|
||||||
|
@ -236,7 +236,7 @@ class ForegroundService : Service(), CoroutineScope {
|
|||||||
is DownloadResult.Success -> {
|
is DownloadResult.Success -> {
|
||||||
try {
|
try {
|
||||||
// Save File and Embed Metadata
|
// 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
|
allTracksStatus[track.title] = DownloadStatus.Converting
|
||||||
sendTrackBroadcast("Converting", track)
|
sendTrackBroadcast("Converting", track)
|
||||||
addToNotification("Processing ${track.title}")
|
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
|
// Checking if the received broadcast is for our enqueued download by matching download id
|
||||||
if (downloadID == id) {
|
if (downloadID == id) {
|
||||||
allTracksStatus[track.title] = DownloadStatus.Converting
|
allTracksStatus[track.title] = DownloadStatus.Converting
|
||||||
launch { dir.saveFileWithMetadata(byteArrayOf(), track); converted++ }
|
launch { dir.saveFileWithMetadata(byteArrayOf(), track){}; converted++ }
|
||||||
// Unregister this broadcast Receiver
|
// Unregister this broadcast Receiver
|
||||||
this@ForegroundService.unregisterReceiver(this)
|
this@ForegroundService.unregisterReceiver(this)
|
||||||
}
|
}
|
||||||
|
@ -42,6 +42,9 @@ fun initKoin(enableNetworkLogs: Boolean = false, appDeclaration: KoinAppDeclarat
|
|||||||
modules(commonModule(enableNetworkLogs = enableNetworkLogs), databaseModule())
|
modules(commonModule(enableNetworkLogs = enableNetworkLogs), databaseModule())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Called by IOS
|
||||||
|
fun initKoin() = initKoin(enableNetworkLogs = false) { }
|
||||||
|
|
||||||
fun commonModule(enableNetworkLogs: Boolean) = module {
|
fun commonModule(enableNetworkLogs: Boolean) = module {
|
||||||
single { createHttpClient(enableNetworkLogs = enableNetworkLogs) }
|
single { createHttpClient(enableNetworkLogs = enableNetworkLogs) }
|
||||||
single { Dir(get(), get()) }
|
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 cacheImage(image: Any, path: String) // in Android = ImageBitmap, Desktop = BufferedImage
|
||||||
suspend fun loadImage(url: String): Picture
|
suspend fun loadImage(url: String): Picture
|
||||||
suspend fun clearCache()
|
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)
|
fun addToLibrary(path: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ package com.shabinder.common.di
|
|||||||
import com.shabinder.common.models.TrackDetails
|
import com.shabinder.common.models.TrackDetails
|
||||||
import com.shabinder.common.models.methods
|
import com.shabinder.common.models.methods
|
||||||
import io.ktor.client.request.*
|
import io.ktor.client.request.*
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
expect suspend fun downloadTracks(
|
expect suspend fun downloadTracks(
|
||||||
@ -28,7 +29,7 @@ expect suspend fun downloadTracks(
|
|||||||
)
|
)
|
||||||
|
|
||||||
suspend fun isInternetAccessible(): Boolean {
|
suspend fun isInternetAccessible(): Boolean {
|
||||||
return withContext(methods.dispatcherIO) {
|
return withContext(methods.value.dispatcherIO) {
|
||||||
try {
|
try {
|
||||||
ktorHttpClient.head<String>("http://google.com")
|
ktorHttpClient.head<String>("http://google.com")
|
||||||
true
|
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.HttpClient
|
||||||
import io.ktor.client.request.get
|
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
|
corsProxy.url
|
||||||
} // "https://spotiflyer-cors.azurewebsites.net/" //"https://spotiflyer-cors.herokuapp.com/"//"https://cors.bridged.cc/"
|
} // "https://spotiflyer-cors.azurewebsites.net/" //"https://spotiflyer-cors.herokuapp.com/"//"https://cors.bridged.cc/"
|
||||||
else ""
|
else ""
|
||||||
|
@ -38,6 +38,7 @@ import io.ktor.client.request.header
|
|||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlin.native.concurrent.ThreadLocal
|
||||||
|
|
||||||
class SpotifyProvider(
|
class SpotifyProvider(
|
||||||
private val tokenStore: TokenStore,
|
private val tokenStore: TokenStore,
|
||||||
@ -48,7 +49,7 @@ class SpotifyProvider(
|
|||||||
/* init {
|
/* init {
|
||||||
logger.d { "Creating Spotify Provider" }
|
logger.d { "Creating Spotify Provider" }
|
||||||
GlobalScope.launch(Dispatchers.Default) {
|
GlobalScope.launch(Dispatchers.Default) {
|
||||||
if (methods.currentPlatform is AllPlatforms.Js) {
|
if (methods.value.currentPlatform is AllPlatforms.Js) {
|
||||||
authenticateSpotifyClient(override = true)
|
authenticateSpotifyClient(override = true)
|
||||||
} else authenticateSpotifyClient()
|
} else authenticateSpotifyClient()
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ class YoutubeMp3(
|
|||||||
suspend fun getMp3DownloadLink(videoID: String): String? = try {
|
suspend fun getMp3DownloadLink(videoID: String): String? = try {
|
||||||
getLinkFromYt1sMp3(videoID)?.let {
|
getLinkFromYt1sMp3(videoID)?.let {
|
||||||
logger.i { "Download Link: $it" }
|
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://kind-grasshopper-73.telebit.io/cors/$it"
|
||||||
// "https://spotiflyer.azurewebsites.net/$it" // Data OUT Limit issue
|
// "https://spotiflyer.azurewebsites.net/$it" // Data OUT Limit issue
|
||||||
else it
|
else it
|
||||||
|
@ -29,7 +29,7 @@ import io.ktor.http.Parameters
|
|||||||
|
|
||||||
suspend fun authenticateSpotify(): TokenData? {
|
suspend fun authenticateSpotify(): TokenData? {
|
||||||
return try {
|
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") })
|
body = FormDataContent(Parameters.build { append("grant_type", "client_credentials") })
|
||||||
} else null
|
} else null
|
||||||
}catch (e:Exception) {
|
}catch (e:Exception) {
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
package com.shabinder.common.di.utils
|
package com.shabinder.common.di.utils
|
||||||
|
|
||||||
// Dependencies:
|
// 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")
|
// implementation("org.jetbrains.kotlinx:atomicfu:0.14.4")
|
||||||
// Gist: https://gist.github.com/fluidsonic/ba32de21c156bbe8424c8d5fc20dcd8e
|
// Gist: https://gist.github.com/fluidsonic/ba32de21c156bbe8424c8d5fc20dcd8e
|
||||||
|
|
||||||
@ -37,7 +37,7 @@ import kotlinx.coroutines.withContext
|
|||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
class ParallelExecutor(
|
class ParallelExecutor(
|
||||||
parentContext: CoroutineContext = methods.dispatcherIO,
|
parentContext: CoroutineContext = methods.value.dispatcherIO,
|
||||||
) : Closeable {
|
) : Closeable {
|
||||||
|
|
||||||
private val concurrentOperationLimit = atomic(4)
|
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(
|
suspend fun downloadTrack(
|
||||||
videoID: String,
|
videoID: String,
|
||||||
trackDetails: TrackDetails,
|
trackDetails: TrackDetails,
|
||||||
saveFileWithMetaData: suspend (mp3ByteArray: ByteArray, trackDetails: TrackDetails) -> Unit
|
saveFileWithMetaData: suspend (mp3ByteArray: ByteArray, trackDetails: TrackDetails,postProcess:(TrackDetails)->Unit) -> Unit
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
val audioData = ytDownloader.getVideo(videoID).getData()
|
val audioData = ytDownloader.getVideo(videoID).getData()
|
||||||
@ -85,7 +85,7 @@ suspend fun downloadTrack(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
is DownloadResult.Success -> { // Todo clear map
|
is DownloadResult.Success -> { // Todo clear map
|
||||||
saveFileWithMetaData(it.byteArray, trackDetails)
|
saveFileWithMetaData(it.byteArray, trackDetails){}
|
||||||
DownloadProgressFlow.emit(
|
DownloadProgressFlow.emit(
|
||||||
DownloadProgressFlow.replayCache.getOrElse(
|
DownloadProgressFlow.replayCache.getOrElse(
|
||||||
0
|
0
|
||||||
|
@ -85,7 +85,8 @@ actual class Dir actual constructor(
|
|||||||
@Suppress("BlockingMethodInNonBlockingContext")
|
@Suppress("BlockingMethodInNonBlockingContext")
|
||||||
actual suspend fun saveFileWithMetadata(
|
actual suspend fun saveFileWithMetadata(
|
||||||
mp3ByteArray: ByteArray,
|
mp3ByteArray: ByteArray,
|
||||||
trackDetails: TrackDetails
|
trackDetails: TrackDetails,
|
||||||
|
postProcess:(track: TrackDetails)->Unit
|
||||||
) {
|
) {
|
||||||
val file = File(trackDetails.outputFilePath)
|
val file = File(trackDetails.outputFilePath)
|
||||||
file.writeBytes(mp3ByteArray)
|
file.writeBytes(mp3ByteArray)
|
||||||
|
@ -7,6 +7,5 @@ actual suspend fun downloadTracks(
|
|||||||
fetcher: FetchPlatformQueryResult,
|
fetcher: FetchPlatformQueryResult,
|
||||||
dir: Dir
|
dir: Dir
|
||||||
) {
|
) {
|
||||||
|
|
||||||
// TODO
|
// 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
|
package com.shabinder.common.di
|
||||||
|
|
||||||
import co.touchlab.kermit.Kermit
|
import co.touchlab.kermit.Kermit
|
||||||
import cocoapods.TagLibIOS.TLAudio
|
|
||||||
import com.shabinder.common.database.SpotiFlyerDatabase
|
import com.shabinder.common.database.SpotiFlyerDatabase
|
||||||
import com.shabinder.common.models.TrackDetails
|
import com.shabinder.common.models.TrackDetails
|
||||||
import com.shabinder.database.Database
|
import com.shabinder.database.Database
|
||||||
@ -27,7 +26,7 @@ actual class Dir actual constructor(
|
|||||||
) {
|
) {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
createDirectories()
|
//createDirectories()
|
||||||
}
|
}
|
||||||
|
|
||||||
actual fun isPresent(path: String): Boolean = NSFileManager.defaultManager.fileExistsAtPath(path)
|
actual fun isPresent(path: String): Boolean = NSFileManager.defaultManager.fileExistsAtPath(path)
|
||||||
@ -135,16 +134,18 @@ actual class Dir actual constructor(
|
|||||||
|
|
||||||
actual suspend fun saveFileWithMetadata(
|
actual suspend fun saveFileWithMetadata(
|
||||||
mp3ByteArray: ByteArray,
|
mp3ByteArray: ByteArray,
|
||||||
trackDetails: TrackDetails
|
trackDetails: TrackDetails,
|
||||||
|
postProcess:(track: TrackDetails)->Unit
|
||||||
) {
|
) {
|
||||||
when (trackDetails.outputFilePath.substringAfterLast('.')) {
|
when (trackDetails.outputFilePath.substringAfterLast('.')) {
|
||||||
".mp3" -> {
|
".mp3" -> {
|
||||||
val file = TLAudio(trackDetails.outputFilePath)
|
postProcess(trackDetails)
|
||||||
|
/*val file = TLAudio(trackDetails.outputFilePath)
|
||||||
file.addTagsAndSave(
|
file.addTagsAndSave(
|
||||||
trackDetails,
|
trackDetails,
|
||||||
this::loadCachedImage,
|
this::loadCachedImage,
|
||||||
this::addToLibrary
|
this::addToLibrary
|
||||||
)
|
)*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
package com.shabinder.common.di
|
package com.shabinder.common.di
|
||||||
|
/*
|
||||||
|
|
||||||
import cocoapods.TagLibIOS.TLAudio
|
import cocoapods.TagLibIOS.TLAudio
|
||||||
import com.shabinder.common.models.TrackDetails
|
import com.shabinder.common.models.TrackDetails
|
||||||
@ -34,4 +35,4 @@ suspend fun TLAudio.addTagsAndSave(
|
|||||||
}
|
}
|
||||||
} catch (e: Exception){ e.printStackTrace() }
|
} catch (e: Exception){ e.printStackTrace() }
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
|
@ -35,7 +35,7 @@ actual suspend fun downloadTracks(
|
|||||||
dir: Dir
|
dir: Dir
|
||||||
) {
|
) {
|
||||||
list.forEach {
|
list.forEach {
|
||||||
withContext(methods.dispatcherIO) {
|
withContext(methods.value.dispatcherIO) {
|
||||||
allTracksStatus[it.title] = DownloadStatus.Queued
|
allTracksStatus[it.title] = DownloadStatus.Queued
|
||||||
if (!it.videoID.isNullOrBlank()) { // Video ID already known!
|
if (!it.videoID.isNullOrBlank()) { // Video ID already known!
|
||||||
downloadTrack(it.videoID!!, it, fetcher, dir)
|
downloadTrack(it.videoID!!, it, fetcher, dir)
|
||||||
@ -66,7 +66,7 @@ suspend fun downloadTrack(videoID: String, track: TrackDetails, fetcher: FetchPl
|
|||||||
when (it) {
|
when (it) {
|
||||||
is DownloadResult.Success -> {
|
is DownloadResult.Success -> {
|
||||||
println("Download Completed")
|
println("Download Completed")
|
||||||
dir.saveFileWithMetadata(it.byteArray, track)
|
dir.saveFileWithMetadata(it.byteArray, track){}
|
||||||
}
|
}
|
||||||
is DownloadResult.Error -> {
|
is DownloadResult.Error -> {
|
||||||
allTracksStatus[track.title] = DownloadStatus.Failed
|
allTracksStatus[track.title] = DownloadStatus.Failed
|
||||||
|
@ -62,7 +62,8 @@ actual class Dir actual constructor(
|
|||||||
@Suppress("BlockingMethodInNonBlockingContext")
|
@Suppress("BlockingMethodInNonBlockingContext")
|
||||||
actual suspend fun saveFileWithMetadata(
|
actual suspend fun saveFileWithMetadata(
|
||||||
mp3ByteArray: ByteArray,
|
mp3ByteArray: ByteArray,
|
||||||
trackDetails: TrackDetails
|
trackDetails: TrackDetails,
|
||||||
|
postProcess:(track: TrackDetails)->Unit
|
||||||
) {
|
) {
|
||||||
val writer = ID3Writer(mp3ByteArray.toArrayBuffer())
|
val writer = ID3Writer(mp3ByteArray.toArrayBuffer())
|
||||||
val albumArt = downloadFile(corsApi + trackDetails.albumArtURL)
|
val albumArt = downloadFile(corsApi + trackDetails.albumArtURL)
|
||||||
|
@ -28,9 +28,6 @@ kotlin {
|
|||||||
implementation(project(":common:data-models"))
|
implementation(project(":common:data-models"))
|
||||||
implementation(project(":common:database"))
|
implementation(project(":common:database"))
|
||||||
implementation(SqlDelight.coroutineExtensions)
|
implementation(SqlDelight.coroutineExtensions)
|
||||||
implementation(MVIKotlin.coroutines)
|
|
||||||
implementation(MVIKotlin.mvikotlin)
|
|
||||||
implementation(Decompose.decompose)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
package com.shabinder.common.list
|
package com.shabinder.common.list
|
||||||
|
|
||||||
import com.arkivanov.decompose.ComponentContext
|
import com.arkivanov.decompose.ComponentContext
|
||||||
|
import com.arkivanov.decompose.value.Value
|
||||||
import com.arkivanov.mvikotlin.core.store.StoreFactory
|
import com.arkivanov.mvikotlin.core.store.StoreFactory
|
||||||
import com.shabinder.common.di.Dir
|
import com.shabinder.common.di.Dir
|
||||||
import com.shabinder.common.di.FetchPlatformQueryResult
|
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.DownloadStatus
|
||||||
import com.shabinder.common.models.PlatformQueryResult
|
import com.shabinder.common.models.PlatformQueryResult
|
||||||
import com.shabinder.common.models.TrackDetails
|
import com.shabinder.common.models.TrackDetails
|
||||||
import kotlinx.coroutines.flow.Flow
|
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
|
|
||||||
interface SpotiFlyerList {
|
interface SpotiFlyerList {
|
||||||
|
|
||||||
val models: Flow<State>
|
val models: Value<State>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Download All Tracks(after filtering already Downloaded)
|
* Download All Tracks(after filtering already Downloaded)
|
||||||
@ -66,9 +66,11 @@ interface SpotiFlyerList {
|
|||||||
val listOutput: Consumer<Output>
|
val listOutput: Consumer<Output>
|
||||||
val downloadProgressFlow: MutableSharedFlow<HashMap<String, DownloadStatus>>
|
val downloadProgressFlow: MutableSharedFlow<HashMap<String, DownloadStatus>>
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class Output {
|
sealed class Output {
|
||||||
object Finished : Output()
|
object Finished : Output()
|
||||||
}
|
}
|
||||||
|
|
||||||
data class State(
|
data class State(
|
||||||
val queryResult: PlatformQueryResult? = null,
|
val queryResult: PlatformQueryResult? = null,
|
||||||
val link: String = "",
|
val link: String = "",
|
||||||
|
@ -17,8 +17,9 @@
|
|||||||
package com.shabinder.common.list.integration
|
package com.shabinder.common.list.integration
|
||||||
|
|
||||||
import com.arkivanov.decompose.ComponentContext
|
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.Picture
|
||||||
|
import com.shabinder.common.di.utils.asValue
|
||||||
import com.shabinder.common.list.SpotiFlyerList
|
import com.shabinder.common.list.SpotiFlyerList
|
||||||
import com.shabinder.common.list.SpotiFlyerList.Dependencies
|
import com.shabinder.common.list.SpotiFlyerList.Dependencies
|
||||||
import com.shabinder.common.list.SpotiFlyerList.State
|
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.SpotiFlyerListStoreProvider
|
||||||
import com.shabinder.common.list.store.getStore
|
import com.shabinder.common.list.store.getStore
|
||||||
import com.shabinder.common.models.TrackDetails
|
import com.shabinder.common.models.TrackDetails
|
||||||
import kotlinx.coroutines.flow.Flow
|
|
||||||
|
|
||||||
internal class SpotiFlyerListImpl(
|
internal class SpotiFlyerListImpl(
|
||||||
componentContext: ComponentContext,
|
componentContext: ComponentContext,
|
||||||
@ -44,7 +44,7 @@ internal class SpotiFlyerListImpl(
|
|||||||
).provide()
|
).provide()
|
||||||
}
|
}
|
||||||
|
|
||||||
override val models: Flow<State> = store.states
|
override val models: Value<State> = store.asValue()
|
||||||
|
|
||||||
override fun onDownloadAllClicked(trackList: List<TrackDetails>) {
|
override fun onDownloadAllClicked(trackList: List<TrackDetails>) {
|
||||||
store.accept(Intent.StartDownloadAll(trackList))
|
store.accept(Intent.StartDownloadAll(trackList))
|
||||||
|
@ -92,7 +92,7 @@ internal class SpotiFlyerListStoreProvider(
|
|||||||
is Intent.StartDownloadAll -> {
|
is Intent.StartDownloadAll -> {
|
||||||
val finalList =
|
val finalList =
|
||||||
intent.trackList.filter { it.downloaded == DownloadStatus.NotDownloaded }
|
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)
|
else downloadTracks(finalList, fetchQuery, dir)
|
||||||
|
|
||||||
val list = intent.trackList.map {
|
val list = intent.trackList.map {
|
||||||
@ -106,7 +106,7 @@ internal class SpotiFlyerListStoreProvider(
|
|||||||
downloadTracks(listOf(intent.track), fetchQuery, dir)
|
downloadTracks(listOf(intent.track), fetchQuery, dir)
|
||||||
dispatch(Result.UpdateTrackItem(intent.track.copy(downloaded = DownloadStatus.Queued)))
|
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:data-models"))
|
||||||
implementation(project(":common:database"))
|
implementation(project(":common:database"))
|
||||||
implementation(SqlDelight.coroutineExtensions)
|
implementation(SqlDelight.coroutineExtensions)
|
||||||
implementation(MVIKotlin.coroutines)
|
|
||||||
implementation(MVIKotlin.mvikotlin)
|
|
||||||
implementation(Decompose.decompose)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
package com.shabinder.common.main
|
package com.shabinder.common.main
|
||||||
|
|
||||||
import com.arkivanov.decompose.ComponentContext
|
import com.arkivanov.decompose.ComponentContext
|
||||||
|
import com.arkivanov.decompose.value.Value
|
||||||
import com.arkivanov.mvikotlin.core.store.StoreFactory
|
import com.arkivanov.mvikotlin.core.store.StoreFactory
|
||||||
import com.shabinder.common.di.Dir
|
import com.shabinder.common.di.Dir
|
||||||
import com.shabinder.common.di.Picture
|
import com.shabinder.common.di.Picture
|
||||||
@ -28,7 +29,7 @@ import kotlinx.coroutines.flow.Flow
|
|||||||
|
|
||||||
interface SpotiFlyerMain {
|
interface SpotiFlyerMain {
|
||||||
|
|
||||||
val models: Flow<State>
|
val models: Value<State>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We Intend to Move to List Screen
|
* We Intend to Move to List Screen
|
||||||
@ -67,6 +68,7 @@ interface SpotiFlyerMain {
|
|||||||
val link: String = "",
|
val link: String = "",
|
||||||
val selectedCategory: HomeCategory = HomeCategory.About
|
val selectedCategory: HomeCategory = HomeCategory.About
|
||||||
)
|
)
|
||||||
|
|
||||||
enum class HomeCategory {
|
enum class HomeCategory {
|
||||||
About, History
|
About, History
|
||||||
}
|
}
|
||||||
|
@ -17,8 +17,9 @@
|
|||||||
package com.shabinder.common.main.integration
|
package com.shabinder.common.main.integration
|
||||||
|
|
||||||
import com.arkivanov.decompose.ComponentContext
|
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.Picture
|
||||||
|
import com.shabinder.common.di.utils.asValue
|
||||||
import com.shabinder.common.main.SpotiFlyerMain
|
import com.shabinder.common.main.SpotiFlyerMain
|
||||||
import com.shabinder.common.main.SpotiFlyerMain.Dependencies
|
import com.shabinder.common.main.SpotiFlyerMain.Dependencies
|
||||||
import com.shabinder.common.main.SpotiFlyerMain.HomeCategory
|
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.SpotiFlyerMainStoreProvider
|
||||||
import com.shabinder.common.main.store.getStore
|
import com.shabinder.common.main.store.getStore
|
||||||
import com.shabinder.common.models.methods
|
import com.shabinder.common.models.methods
|
||||||
import kotlinx.coroutines.flow.Flow
|
|
||||||
|
|
||||||
internal class SpotiFlyerMainImpl(
|
internal class SpotiFlyerMainImpl(
|
||||||
componentContext: ComponentContext,
|
componentContext: ComponentContext,
|
||||||
@ -43,11 +43,11 @@ internal class SpotiFlyerMainImpl(
|
|||||||
).provide()
|
).provide()
|
||||||
}
|
}
|
||||||
|
|
||||||
override val models: Flow<State> = store.states
|
override val models: Value<State> = store.asValue()
|
||||||
|
|
||||||
override fun onLinkSearch(link: String) {
|
override fun onLinkSearch(link: String) {
|
||||||
if (methods.isInternetAvailable) mainOutput.callback(Output.Search(link = link))
|
if (methods.value.isInternetAvailable) mainOutput.callback(Output.Search(link = link))
|
||||||
else methods.showPopUpMessage("Check Network Connection Please")
|
else methods.value.showPopUpMessage("Check Network Connection Please")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onInputLinkChanged(link: String) {
|
override fun onInputLinkChanged(link: String) {
|
||||||
|
@ -78,9 +78,9 @@ internal class SpotiFlyerMainStoreProvider(
|
|||||||
|
|
||||||
override suspend fun executeIntent(intent: Intent, getState: () -> State) {
|
override suspend fun executeIntent(intent: Intent, getState: () -> State) {
|
||||||
when (intent) {
|
when (intent) {
|
||||||
is Intent.OpenPlatform -> methods.openPlatform(intent.platformID, intent.platformLink)
|
is Intent.OpenPlatform -> methods.value.openPlatform(intent.platformID, intent.platformLink)
|
||||||
is Intent.GiveDonation -> methods.giveDonation()
|
is Intent.GiveDonation -> methods.value.giveDonation()
|
||||||
is Intent.ShareApp -> methods.shareApp()
|
is Intent.ShareApp -> methods.value.shareApp()
|
||||||
is Intent.SetLink -> dispatch(Result.LinkChanged(link = intent.link))
|
is Intent.SetLink -> dispatch(Result.LinkChanged(link = intent.link))
|
||||||
is Intent.SelectCategory -> dispatch(Result.CategoryChanged(intent.category))
|
is Intent.SelectCategory -> dispatch(Result.CategoryChanged(intent.category))
|
||||||
}
|
}
|
||||||
|
@ -18,68 +18,43 @@ plugins {
|
|||||||
id("multiplatform-setup")
|
id("multiplatform-setup")
|
||||||
id("android-setup")
|
id("android-setup")
|
||||||
id("kotlin-parcelize")
|
id("kotlin-parcelize")
|
||||||
//kotlin("native.cocoapods")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Required be cocoapods
|
fun org.jetbrains.kotlin.gradle.dsl.KotlinNativeBinaryContainer.generateFramework() {
|
||||||
version = "1.0"
|
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 {
|
kotlin {
|
||||||
|
|
||||||
/*IOS Target Can be only built on Mac*/
|
/*IOS Target Can be only built on Mac*/
|
||||||
if(HostOS.isMac){
|
if(HostOS.isMac) {
|
||||||
val sdkName: String? = System.getenv("SDK_NAME")
|
val sdkName: String? = System.getenv("SDK_NAME")
|
||||||
val isiOSDevice = sdkName.orEmpty().startsWith("iphoneos")
|
val isiOSDevice = sdkName.orEmpty().startsWith("iphoneos")
|
||||||
if (isiOSDevice) {
|
if (isiOSDevice) {
|
||||||
iosArm64("ios"){
|
iosArm64("ios"){
|
||||||
binaries {
|
binaries {
|
||||||
framework {
|
generateFramework()
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
iosX64("ios"){
|
iosX64("ios"){
|
||||||
binaries {
|
binaries {
|
||||||
framework {
|
generateFramework()
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/*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 {
|
sourceSets {
|
||||||
commonMain {
|
commonMain {
|
||||||
@ -90,12 +65,11 @@ kotlin {
|
|||||||
implementation(project(":common:list"))
|
implementation(project(":common:list"))
|
||||||
implementation(project(":common:main"))
|
implementation(project(":common:main"))
|
||||||
implementation(SqlDelight.coroutineExtensions)
|
implementation(SqlDelight.coroutineExtensions)
|
||||||
implementation(MVIKotlin.coroutines)
|
|
||||||
implementation(MVIKotlin.mvikotlin)
|
|
||||||
implementation(Decompose.decompose)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*Required to Export `packForXcode`*/
|
||||||
sourceSets {
|
sourceSets {
|
||||||
named("iosMain") {
|
named("iosMain") {
|
||||||
dependencies {
|
dependencies {
|
||||||
@ -105,7 +79,8 @@ kotlin {
|
|||||||
api(project(":common:list"))
|
api(project(":common:list"))
|
||||||
api(project(":common:main"))
|
api(project(":common:main"))
|
||||||
api(Decompose.decompose)
|
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"
|
group = "build"
|
||||||
val mode = System.getenv("CONFIGURATION") ?: "DEBUG"
|
val mode = System.getenv("CONFIGURATION") ?: "DEBUG"
|
||||||
val targetName = "ios"
|
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)
|
inputs.property("mode", mode)
|
||||||
dependsOn(framework.linkTask)
|
dependsOn(framework.linkTask)
|
||||||
val targetDir = File(buildDir, "xcode-frameworks")
|
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
|
package com.shabinder.common.root.integration
|
||||||
|
|
||||||
|
import co.touchlab.stately.freeze
|
||||||
import com.arkivanov.decompose.ComponentContext
|
import com.arkivanov.decompose.ComponentContext
|
||||||
import com.arkivanov.decompose.RouterState
|
import com.arkivanov.decompose.RouterState
|
||||||
import com.arkivanov.decompose.pop
|
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.Child
|
||||||
import com.shabinder.common.root.SpotiFlyerRoot.Dependencies
|
import com.shabinder.common.root.SpotiFlyerRoot.Dependencies
|
||||||
import com.shabinder.common.root.callbacks.SpotiFlyerRootCallBacks
|
import com.shabinder.common.root.callbacks.SpotiFlyerRootCallBacks
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@ -45,12 +47,12 @@ internal class SpotiFlyerRootImpl(
|
|||||||
) : SpotiFlyerRoot, ComponentContext by componentContext, Dependencies by dependencies, Actions by dependencies.actions {
|
) : SpotiFlyerRoot, ComponentContext by componentContext, Dependencies by dependencies, Actions by dependencies.actions {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
methods = actions
|
methods.value = actions.freeze()
|
||||||
GlobalScope.launch {
|
GlobalScope.launch(Dispatchers.Default) {
|
||||||
/*Authenticate Spotify Client*/
|
//*Authenticate Spotify Client*//*
|
||||||
if (methods.currentPlatform is AllPlatforms.Js) {
|
/*if (methods.value.currentPlatform is AllPlatforms.Js) {
|
||||||
fetchPlatformQueryResult.spotifyProvider.authenticateSpotifyClient(override = true)
|
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.stability.nowarn=true
|
||||||
kotlin.mpp.enableGranularSourceSetsMetadata=true
|
kotlin.mpp.enableGranularSourceSetsMetadata=true
|
||||||
kotlin.native.enableDependencyPropagation=false
|
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
|
#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