mirror of
https://github.com/Shabinder/SpotiFlyer.git
synced 2024-11-22 09:04:32 +01:00
Actions and Code Refactoring
This commit is contained in:
parent
6fb1815252
commit
e4c22c5d0c
6
.github/workflows/build-desktop-jars.yml
vendored
6
.github/workflows/build-desktop-jars.yml
vendored
@ -31,7 +31,7 @@ jobs:
|
|||||||
uses: ncipollo/release-action@v1
|
uses: ncipollo/release-action@v1
|
||||||
with:
|
with:
|
||||||
draft: true
|
draft: true
|
||||||
tag: "SpotiFlyer-v3.3.0"
|
tag: "v3.3.0"
|
||||||
artifacts: "desktop/build/compose/jars/*.jar,desktop/build/compose/binaries/main/*/*"
|
artifacts: "desktop/build/compose/jars/*.jar,desktop/build/compose/binaries/main/*/*"
|
||||||
token: ${{ secrets.GH_TOKEN }}
|
token: ${{ secrets.GH_TOKEN }}
|
||||||
commit: main
|
commit: main
|
||||||
@ -65,7 +65,7 @@ jobs:
|
|||||||
uses: ncipollo/release-action@v1
|
uses: ncipollo/release-action@v1
|
||||||
with:
|
with:
|
||||||
draft: true
|
draft: true
|
||||||
tag: "SpotiFlyer-v3.3.0"
|
tag: "v3.3.0"
|
||||||
artifacts: "desktop/build/compose/jars/*.jar,desktop/build/compose/binaries/main/*/*"
|
artifacts: "desktop/build/compose/jars/*.jar,desktop/build/compose/binaries/main/*/*"
|
||||||
token: ${{ secrets.GH_TOKEN }}
|
token: ${{ secrets.GH_TOKEN }}
|
||||||
commit: main
|
commit: main
|
||||||
@ -98,7 +98,7 @@ jobs:
|
|||||||
uses: ncipollo/release-action@v1
|
uses: ncipollo/release-action@v1
|
||||||
with:
|
with:
|
||||||
draft: true
|
draft: true
|
||||||
tag: "SpotiFlyer-v3.3.0"
|
tag: "v3.3.0"
|
||||||
artifacts: "desktop/build/compose/jars/*.jar,desktop/build/compose/binaries/main/*/*"
|
artifacts: "desktop/build/compose/jars/*.jar,desktop/build/compose/binaries/main/*/*"
|
||||||
token: ${{ secrets.GH_TOKEN }}
|
token: ${{ secrets.GH_TOKEN }}
|
||||||
commit: main
|
commit: main
|
||||||
|
@ -36,7 +36,7 @@
|
|||||||
tools:ignore="ScopedStorage" />
|
tools:ignore="ScopedStorage" />
|
||||||
<uses-permission android:name="android.permission.READ_STORAGE_PERMISSION" />
|
<uses-permission android:name="android.permission.READ_STORAGE_PERMISSION" />
|
||||||
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
|
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
|
||||||
tools:ignore="ScopedStorage" />
|
tools:ignore="ProtectedPermissions,ScopedStorage" />
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
||||||
@ -58,16 +58,22 @@
|
|||||||
android:extractNativeLibs="true"
|
android:extractNativeLibs="true"
|
||||||
android:requestLegacyExternalStorage="true"
|
android:requestLegacyExternalStorage="true"
|
||||||
tools:targetApi="q">
|
tools:targetApi="q">
|
||||||
<activity android:name=".MainActivity"
|
<activity
|
||||||
android:launchMode="singleTask"
|
android:name=".ui.SplashScreenActivity"
|
||||||
android:label="SpotiFlyer"
|
android:theme="@style/SplashTheme"
|
||||||
android:hardwareAccelerated="true"
|
android:hardwareAccelerated="true"
|
||||||
android:theme="@style/Theme.SpotiFlyer"
|
|
||||||
>
|
>
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
|
||||||
|
<activity android:name=".MainActivity"
|
||||||
|
android:launchMode="singleTask"
|
||||||
|
android:hardwareAccelerated="true"
|
||||||
|
android:theme="@style/Theme.SpotiFlyer"
|
||||||
|
>
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
<action android:name="android.intent.action.SEND" />
|
<action android:name="android.intent.action.SEND" />
|
||||||
@ -76,13 +82,15 @@
|
|||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<service android:name=".service.ForegroundService"/>
|
<service android:name=".service.ForegroundService"/>
|
||||||
<service android:name="org.openudid.OpenUDID_service">
|
<service android:name="org.openudid.OpenUDID_service"
|
||||||
|
tools:ignore="ExportedService,IntentFilterExportedReceiver">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="org.openudid.GETUDID" />
|
<action android:name="org.openudid.GETUDID" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<receiver android:name="ly.count.android.sdk.ReferrerReceiver" android:exported="true">
|
<receiver android:name="ly.count.android.sdk.ReferrerReceiver" android:exported="true"
|
||||||
|
tools:ignore="ExportedReceiver">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="com.android.vending.INSTALL_REFERRER" />
|
<action android:name="com.android.vending.INSTALL_REFERRER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
@ -55,6 +55,7 @@ import com.shabinder.common.core_components.ConnectionLiveData
|
|||||||
import com.shabinder.common.core_components.analytics.AnalyticsManager
|
import com.shabinder.common.core_components.analytics.AnalyticsManager
|
||||||
import com.shabinder.common.core_components.file_manager.FileManager
|
import com.shabinder.common.core_components.file_manager.FileManager
|
||||||
import com.shabinder.common.core_components.preference_manager.PreferenceManager
|
import com.shabinder.common.core_components.preference_manager.PreferenceManager
|
||||||
|
import com.shabinder.common.di.ApplicationInit
|
||||||
import com.shabinder.common.di.observeAsState
|
import com.shabinder.common.di.observeAsState
|
||||||
import com.shabinder.common.models.*
|
import com.shabinder.common.models.*
|
||||||
import com.shabinder.common.models.PlatformActions.Companion.SharedPreferencesKey
|
import com.shabinder.common.models.PlatformActions.Companion.SharedPreferencesKey
|
||||||
@ -79,13 +80,14 @@ import org.koin.android.ext.android.inject
|
|||||||
import org.koin.core.parameter.parametersOf
|
import org.koin.core.parameter.parametersOf
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
@ExperimentalAnimationApi
|
@OptIn(ExperimentalAnimationApi::class)
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
|
|
||||||
private val fetcher: FetchPlatformQueryResult by inject()
|
private val fetcher: FetchPlatformQueryResult by inject()
|
||||||
private val fileManager: FileManager by inject()
|
private val fileManager: FileManager by inject()
|
||||||
private val preferenceManager: PreferenceManager by inject()
|
private val preferenceManager: PreferenceManager by inject()
|
||||||
private val analyticsManager: AnalyticsManager by inject { parametersOf(this) }
|
private val analyticsManager: AnalyticsManager by inject { parametersOf(this) }
|
||||||
|
private val applicationInit: ApplicationInit by inject()
|
||||||
private val callBacks: SpotiFlyerRootCallBacks get() = this.rootComponent.callBacks
|
private val callBacks: SpotiFlyerRootCallBacks get() = this.rootComponent.callBacks
|
||||||
private val trackStatusFlow = MutableSharedFlow<HashMap<String, DownloadStatus>>(1)
|
private val trackStatusFlow = MutableSharedFlow<HashMap<String, DownloadStatus>>(1)
|
||||||
private var permissionGranted = mutableStateOf(true)
|
private var permissionGranted = mutableStateOf(true)
|
||||||
@ -263,6 +265,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
override val analyticsManager: AnalyticsManager = this@MainActivity.analyticsManager
|
override val analyticsManager: AnalyticsManager = this@MainActivity.analyticsManager
|
||||||
override val downloadProgressFlow: MutableSharedFlow<HashMap<String, DownloadStatus>> =
|
override val downloadProgressFlow: MutableSharedFlow<HashMap<String, DownloadStatus>> =
|
||||||
trackStatusFlow
|
trackStatusFlow
|
||||||
|
override val appInit: ApplicationInit = applicationInit
|
||||||
override val actions = object : Actions {
|
override val actions = object : Actions {
|
||||||
|
|
||||||
override val platformActions = object : PlatformActions {
|
override val platformActions = object : PlatformActions {
|
||||||
@ -431,7 +434,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
while (!this@MainActivity::rootComponent.isInitialized) {
|
while (!this@MainActivity::rootComponent.isInitialized) {
|
||||||
delay(100)
|
delay(100)
|
||||||
}
|
}
|
||||||
if (methods.value.isInternetAvailable) callBacks.searchLink(link)
|
if (Actions.instance.isInternetAvailable) callBacks.searchLink(link)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,34 @@
|
|||||||
|
package com.shabinder.spotiflyer.ui
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.compose.animation.ExperimentalAnimationApi
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import com.shabinder.common.core_components.analytics.AnalyticsManager
|
||||||
|
import com.shabinder.common.di.ApplicationInit
|
||||||
|
import com.shabinder.spotiflyer.MainActivity
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.koin.android.ext.android.inject
|
||||||
|
import org.koin.androidx.scope.ScopeActivity
|
||||||
|
import org.koin.core.parameter.parametersOf
|
||||||
|
|
||||||
|
class SplashScreenActivity : AppCompatActivity() {
|
||||||
|
|
||||||
|
private val applicationInit: ApplicationInit by inject()
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
applicationInit.init()
|
||||||
|
lifecycleScope.launch {
|
||||||
|
delay(SPLASH_DELAY)
|
||||||
|
startActivity(Intent(this@SplashScreenActivity, MainActivity::class.java))
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val SPLASH_DELAY = 2000L
|
||||||
|
}
|
||||||
|
}
|
BIN
android/src/main/res/drawable-xxhdpi/made_with_love.webp
Normal file
BIN
android/src/main/res/drawable-xxhdpi/made_with_love.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
BIN
android/src/main/res/drawable-xxhdpi/spotiflyer.webp
Normal file
BIN
android/src/main/res/drawable-xxhdpi/spotiflyer.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 53 KiB |
@ -1,75 +0,0 @@
|
|||||||
<!--
|
|
||||||
~ Copyright (c) 2021 Shabinder Singh
|
|
||||||
~ This program is free software: you can redistribute it and/or modify
|
|
||||||
~ it under the terms of the GNU General Public License as published by
|
|
||||||
~ the Free Software Foundation, either version 3 of the License, or
|
|
||||||
~ (at your option) any later version.
|
|
||||||
~
|
|
||||||
~ This program is distributed in the hope that it will be useful,
|
|
||||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
~ GNU General Public License for more details.
|
|
||||||
~
|
|
||||||
~ You should have received a copy of the GNU General Public License
|
|
||||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:aapt="http://schemas.android.com/aapt"
|
|
||||||
android:width="108dp"
|
|
||||||
android:height="108dp"
|
|
||||||
android:viewportWidth="108"
|
|
||||||
android:viewportHeight="108">
|
|
||||||
<group android:scaleX="0.10546875"
|
|
||||||
android:scaleY="0.10546875"
|
|
||||||
android:translateX="27"
|
|
||||||
android:translateY="27">
|
|
||||||
<path
|
|
||||||
android:pathData="M256,256m-256,0a256,256 0,1 1,512 0a256,256 0,1 1,-512 0">
|
|
||||||
<aapt:attr name="android:fillColor">
|
|
||||||
<gradient
|
|
||||||
android:startY="437.0193"
|
|
||||||
android:startX="74.9807"
|
|
||||||
android:endY="74.9807"
|
|
||||||
android:endX="437.0193"
|
|
||||||
android:type="linear">
|
|
||||||
<item android:offset="0" android:color="#FF736BFD"/>
|
|
||||||
<item android:offset="1" android:color="#FFF54187"/>
|
|
||||||
</gradient>
|
|
||||||
</aapt:attr>
|
|
||||||
</path>
|
|
||||||
<path
|
|
||||||
android:fillColor="#FF000000"
|
|
||||||
android:pathData="M377,356.7c-68.9,-45.4 -155.6,-56.4 -257.6,-32.7c-20.5,4.8 -13.6,35.8 7.3,31.2C290.7,317 351.6,386 368.2,386C384,386 390.2,365.4 377,356.7z"/>
|
|
||||||
<path
|
|
||||||
android:fillColor="#FF000000"
|
|
||||||
android:pathData="M112.1,275.1C203.9,253.4 308.1,266 384,308c18.5,10.2 34,-17.8 15.5,-28c-82.7,-45.7 -195.6,-59.5 -294.7,-36C84.2,248.8 91.5,280 112.1,275.1L112.1,275.1z"/>
|
|
||||||
<path
|
|
||||||
android:fillColor="#FF000000"
|
|
||||||
android:pathData="M100,191.9c96.6,-29.6 232.2,-13.4 308.7,36.9c17.6,11.5 35.3,-15.1 17.6,-26.7c-84.9,-55.8 -229.2,-73.3 -335.6,-40.8C70.4,167.5 79.9,198.1 100,191.9L100,191.9z"/>
|
|
||||||
<path
|
|
||||||
android:pathData="M507.8,438.2c-1.6,97.2 -141.9,97.1 -143.5,0C365.9,341 506.2,341 507.8,438.2z">
|
|
||||||
<aapt:attr name="android:fillColor">
|
|
||||||
<gradient
|
|
||||||
android:startY="386.3741"
|
|
||||||
android:startX="487.8323"
|
|
||||||
android:endY="490.009"
|
|
||||||
android:endX="384.1974"
|
|
||||||
android:type="linear">
|
|
||||||
<item android:offset="0" android:color="#FF736BFD"/>
|
|
||||||
<item android:offset="1" android:color="#FFF54187"/>
|
|
||||||
</gradient>
|
|
||||||
</aapt:attr>
|
|
||||||
</path>
|
|
||||||
<path
|
|
||||||
android:fillColor="#FF000000"
|
|
||||||
android:pathData="M486.8,456.8c-0.6,-2.4 -6.9,-1 -8.5,-1.4c11.5,-82 -82.4,-86.7 -87.1,-22.2c0.3,1.8 -1,6.7 2.2,6.6c0,0 8.6,0 8.6,0c3.1,0.1 2,-4.7 2.2,-6.6c0.1,-23.3 35,-23.3 35.2,0c0,0 0,6.9 0,6.9c-0.1,2.8 4.4,2.8 4.3,0c5,-35.2 -43.8,-40.1 -43.8,-4.7h-4.3c-1.6,-53.7 77.2,-55.9 78.4,-2.2c0,0 0,24.4 0,24.4c-0.1,2.9 3.8,2.1 5.6,2.2l-20.7,21l-20.7,-21c1.8,-0.1 5.6,0.7 5.6,-2.2c0,0 0,-8.8 0,-8.8c0,-2.8 -4.4,-2.8 -4.3,0c0,0 0,6.6 0,6.6c-2.2,0.2 -11.3,-1.3 -8,3.7c0,0 25.9,26.3 25.9,26.3c0.8,0.9 2.2,0.9 3.1,0C460.6,484.4 489.4,458.3 486.8,456.8z"
|
|
||||||
android:strokeWidth="0.75"
|
|
||||||
android:strokeColor="#000000"/>
|
|
||||||
<path
|
|
||||||
android:pathData="M510,437.5c-1.7,96.2 -142.1,96.2 -143.8,0C367.9,341.3 508.4,341.3 510,437.5z"
|
|
||||||
android:strokeWidth="6"
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:strokeColor="#000000"/>
|
|
||||||
</group>
|
|
||||||
</vector>
|
|
12
android/src/main/res/drawable/ic_splash.xml
Normal file
12
android/src/main/res/drawable/ic_splash.xml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:paddingBottom="20dp"
|
||||||
|
android:paddingRight="20dp"
|
||||||
|
android:paddingLeft="20dp"
|
||||||
|
android:paddingTop="20dp">
|
||||||
|
<!-- Your product logo - 144dp color version of your app icon -->
|
||||||
|
<item
|
||||||
|
android:drawable="@drawable/spotiflyer"
|
||||||
|
android:gravity="center">
|
||||||
|
</item>
|
||||||
|
</layer-list>
|
@ -17,5 +17,5 @@
|
|||||||
|
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<background android:drawable="@color/ic_launcher_background"/>
|
<background android:drawable="@color/ic_launcher_background"/>
|
||||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
<foreground android:drawable="@drawable/ic_spotiflyer_logo"/>
|
||||||
</adaptive-icon>
|
</adaptive-icon>
|
@ -17,5 +17,5 @@
|
|||||||
|
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<background android:drawable="@color/ic_launcher_background"/>
|
<background android:drawable="@color/ic_launcher_background"/>
|
||||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
<foreground android:drawable="@drawable/ic_spotiflyer_logo"/>
|
||||||
</adaptive-icon>
|
</adaptive-icon>
|
@ -22,8 +22,12 @@
|
|||||||
<item name="colorAccent">@color/colorAccent</item>
|
<item name="colorAccent">@color/colorAccent</item>
|
||||||
<item name="android:textColor">@color/white</item>
|
<item name="android:textColor">@color/white</item>
|
||||||
<item name="android:background">@android:color/black</item>
|
<item name="android:background">@android:color/black</item>
|
||||||
<item name="android:backgroundTint">@android:color/black</item>
|
|
||||||
<item name="android:statusBarColor">@android:color/transparent</item>
|
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||||
<item name="android:navigationBarColor">@android:color/transparent</item>
|
<item name="android:navigationBarColor">@android:color/transparent</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="SplashTheme" parent="Theme.SpotiFlyer">
|
||||||
|
<item name="android:windowBackground">@drawable/ic_splash</item>
|
||||||
|
<item name="android:background">@android:color/transparent</item>
|
||||||
|
</style>
|
||||||
</resources>
|
</resources>
|
@ -35,10 +35,16 @@ allprojects {
|
|||||||
download = false
|
download = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
|
tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinCompile<*>>().configureEach {
|
||||||
dependsOn(":common:data-models:generateI18n4kFiles")
|
dependsOn(":common:data-models:generateI18n4kFiles")
|
||||||
kotlinOptions { jvmTarget = "1.8" }
|
kotlinOptions {
|
||||||
|
if(this is org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions) {
|
||||||
|
jvmTarget = "1.8"
|
||||||
|
}
|
||||||
|
freeCompilerArgs = (freeCompilerArgs + listOf("-Xopt-in=kotlin.RequiresOptIn"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
afterEvaluate {
|
afterEvaluate {
|
||||||
project.extensions.findByType<org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension>()?.let { kmpExt ->
|
project.extensions.findByType<org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension>()?.let { kmpExt ->
|
||||||
kmpExt.sourceSets.run {
|
kmpExt.sourceSets.run {
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id("com.android.library")
|
id("com.android.library")
|
||||||
id("ktlint-setup")
|
id("ktlint-setup")
|
||||||
|
id("compiler-args")
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
|
13
buildSrc/src/main/kotlin/compiler-args.gradle.kts
Normal file
13
buildSrc/src/main/kotlin/compiler-args.gradle.kts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
plugins {
|
||||||
|
kotlin("multiplatform")
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
sourceSets {
|
||||||
|
all {
|
||||||
|
languageSettings.useExperimentalAnnotation("kotlin.RequiresOptIn")
|
||||||
|
languageSettings.useExperimentalAnnotation("androidx.compose.animation")
|
||||||
|
languageSettings.useExperimentalAnnotation("kotlinx.serialization.ExperimentalSerializationApi")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -20,6 +20,7 @@ plugins {
|
|||||||
id("org.jetbrains.compose")
|
id("org.jetbrains.compose")
|
||||||
id("kotlin-parcelize")
|
id("kotlin-parcelize")
|
||||||
id("ktlint-setup")
|
id("ktlint-setup")
|
||||||
|
id("compiler-args")
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id("com.android.library")
|
id("com.android.library")
|
||||||
id("kotlin-multiplatform")
|
id("kotlin-multiplatform")
|
||||||
|
id("compiler-args")
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
|
@ -20,6 +20,7 @@ plugins {
|
|||||||
id("org.jetbrains.compose")
|
id("org.jetbrains.compose")
|
||||||
id("ktlint-setup")
|
id("ktlint-setup")
|
||||||
id("kotlin-parcelize")
|
id("kotlin-parcelize")
|
||||||
|
id("compiler-args")
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
|
@ -23,9 +23,6 @@ plugins {
|
|||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
sourceSets {
|
sourceSets {
|
||||||
all {
|
|
||||||
languageSettings.useExperimentalAnnotation("androidx.compose.animation")
|
|
||||||
}
|
|
||||||
commonMain {
|
commonMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(compose.material)
|
implementation(compose.material)
|
||||||
|
@ -26,7 +26,7 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.shabinder.common.models.methods
|
import com.shabinder.common.models.Actions
|
||||||
import com.shabinder.common.translations.Strings
|
import com.shabinder.common.translations.Strings
|
||||||
import com.shabinder.common.uikit.Dialog
|
import com.shabinder.common.uikit.Dialog
|
||||||
import com.shabinder.common.uikit.OpenCollectiveLogo
|
import com.shabinder.common.uikit.OpenCollectiveLogo
|
||||||
@ -87,7 +87,7 @@ fun DonationDialog(
|
|||||||
modifier = Modifier.fillMaxWidth().clickable(
|
modifier = Modifier.fillMaxWidth().clickable(
|
||||||
onClick = {
|
onClick = {
|
||||||
onDismiss()
|
onDismiss()
|
||||||
methods.value.openPlatform("", "https://opencollective.com/spotiflyer/donate")
|
Actions.instance.openPlatform("", "https://opencollective.com/spotiflyer/donate")
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.padding(vertical = 6.dp)
|
.padding(vertical = 6.dp)
|
||||||
@ -110,7 +110,7 @@ fun DonationDialog(
|
|||||||
modifier = Modifier.fillMaxWidth().clickable(
|
modifier = Modifier.fillMaxWidth().clickable(
|
||||||
onClick = {
|
onClick = {
|
||||||
onDismiss()
|
onDismiss()
|
||||||
methods.value.openPlatform("", "https://www.paypal.com/paypalme/shabinder")
|
Actions.instance.openPlatform("", "https://www.paypal.com/paypalme/shabinder")
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.padding(vertical = 6.dp)
|
.padding(vertical = 6.dp)
|
||||||
@ -133,7 +133,7 @@ fun DonationDialog(
|
|||||||
.clickable(
|
.clickable(
|
||||||
onClick = {
|
onClick = {
|
||||||
onDismiss()
|
onDismiss()
|
||||||
methods.value.giveDonation()
|
Actions.instance.giveDonation()
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
@ -25,7 +25,7 @@ import androidx.compose.ui.graphics.Color
|
|||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.shabinder.common.models.methods
|
import com.shabinder.common.models.Actions
|
||||||
import com.shabinder.common.translations.Strings
|
import com.shabinder.common.translations.Strings
|
||||||
import com.shabinder.common.uikit.Dialog
|
import com.shabinder.common.uikit.Dialog
|
||||||
import com.shabinder.common.uikit.configurations.SpotiFlyerTypography
|
import com.shabinder.common.uikit.configurations.SpotiFlyerTypography
|
||||||
@ -67,7 +67,7 @@ fun ErrorInfoDialog(error: Throwable): ErrorInfoDialogCallBacks {
|
|||||||
TextButton(onClick = onDismissDialog, colors = ButtonDefaults.buttonColors()) {
|
TextButton(onClick = onDismissDialog, colors = ButtonDefaults.buttonColors()) {
|
||||||
Text(Strings.dismiss())
|
Text(Strings.dismiss())
|
||||||
}
|
}
|
||||||
TextButton(onClick = { methods.value.copyToClipboard(error.stackTraceToString()) }, colors = ButtonDefaults.buttonColors()) {
|
TextButton(onClick = { Actions.instance.copyToClipboard(error.stackTraceToString()) }, colors = ButtonDefaults.buttonColors()) {
|
||||||
Text(Strings.copyToClipboard())
|
Text(Strings.copyToClipboard())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,7 @@ import com.shabinder.common.core_components.picture.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.Actions
|
||||||
import com.shabinder.common.translations.Strings
|
import com.shabinder.common.translations.Strings
|
||||||
import com.shabinder.common.uikit.DownloadAllImage
|
import com.shabinder.common.uikit.DownloadAllImage
|
||||||
import com.shabinder.common.uikit.DownloadImageArrow
|
import com.shabinder.common.uikit.DownloadImageArrow
|
||||||
@ -84,7 +84,7 @@ fun SpotiFlyerListContent(
|
|||||||
LaunchedEffect(model.errorOccurred) {
|
LaunchedEffect(model.errorOccurred) {
|
||||||
/*Handle if Any Exception Occurred*/
|
/*Handle if Any Exception Occurred*/
|
||||||
model.errorOccurred?.let {
|
model.errorOccurred?.let {
|
||||||
methods.value.showPopUpMessage(it.message ?: Strings.errorOccurred())
|
Actions.instance.showPopUpMessage(it.message ?: Strings.errorOccurred())
|
||||||
component.onBackPressed()
|
component.onBackPressed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -82,7 +82,7 @@ import com.shabinder.common.core_components.picture.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
|
||||||
import com.shabinder.common.models.DownloadRecord
|
import com.shabinder.common.models.DownloadRecord
|
||||||
import com.shabinder.common.models.methods
|
import com.shabinder.common.models.Actions
|
||||||
import com.shabinder.common.translations.Strings
|
import com.shabinder.common.translations.Strings
|
||||||
import com.shabinder.common.uikit.GaanaLogo
|
import com.shabinder.common.uikit.GaanaLogo
|
||||||
import com.shabinder.common.uikit.GithubLogo
|
import com.shabinder.common.uikit.GithubLogo
|
||||||
@ -229,7 +229,7 @@ fun SearchPanel(
|
|||||||
OutlinedButton(
|
OutlinedButton(
|
||||||
modifier = Modifier.padding(12.dp).wrapContentWidth(),
|
modifier = Modifier.padding(12.dp).wrapContentWidth(),
|
||||||
onClick = {
|
onClick = {
|
||||||
if (link.isBlank()) methods.value.showPopUpMessage(Strings.enterALink())
|
if (link.isBlank()) Actions.instance.showPopUpMessage(Strings.enterALink())
|
||||||
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)
|
||||||
@ -279,7 +279,7 @@ fun AboutColumn(
|
|||||||
"${Strings.open()} Spotify",
|
"${Strings.open()} Spotify",
|
||||||
tint = Color.Unspecified,
|
tint = Color.Unspecified,
|
||||||
modifier = Modifier.clip(SpotiFlyerShapes.small).clickable(
|
modifier = Modifier.clip(SpotiFlyerShapes.small).clickable(
|
||||||
onClick = { methods.value.openPlatform("com.spotify.music", "https://open.spotify.com") }
|
onClick = { Actions.instance.openPlatform("com.spotify.music", "https://open.spotify.com") }
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
Spacer(modifier = modifier.padding(start = 16.dp))
|
Spacer(modifier = modifier.padding(start = 16.dp))
|
||||||
@ -288,7 +288,7 @@ fun AboutColumn(
|
|||||||
"${Strings.open()} Gaana",
|
"${Strings.open()} Gaana",
|
||||||
tint = Color.Unspecified,
|
tint = Color.Unspecified,
|
||||||
modifier = Modifier.clip(SpotiFlyerShapes.small).clickable(
|
modifier = Modifier.clip(SpotiFlyerShapes.small).clickable(
|
||||||
onClick = { methods.value.openPlatform("com.gaana", "https://www.gaana.com") }
|
onClick = { Actions.instance.openPlatform("com.gaana", "https://www.gaana.com") }
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
Spacer(modifier = modifier.padding(start = 16.dp))
|
Spacer(modifier = modifier.padding(start = 16.dp))
|
||||||
@ -297,7 +297,7 @@ fun AboutColumn(
|
|||||||
"${Strings.open()} Jio Saavn",
|
"${Strings.open()} Jio Saavn",
|
||||||
tint = Color.Unspecified,
|
tint = Color.Unspecified,
|
||||||
modifier = Modifier.clickable(
|
modifier = Modifier.clickable(
|
||||||
onClick = { methods.value.openPlatform("com.jio.media.jiobeats", "https://www.jiosaavn.com/") }
|
onClick = { Actions.instance.openPlatform("com.jio.media.jiobeats", "https://www.jiosaavn.com/") }
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
Spacer(modifier = modifier.padding(start = 16.dp))
|
Spacer(modifier = modifier.padding(start = 16.dp))
|
||||||
@ -306,7 +306,7 @@ fun AboutColumn(
|
|||||||
"${Strings.open()} Youtube",
|
"${Strings.open()} Youtube",
|
||||||
tint = Color.Unspecified,
|
tint = Color.Unspecified,
|
||||||
modifier = Modifier.clip(SpotiFlyerShapes.small).clickable(
|
modifier = Modifier.clip(SpotiFlyerShapes.small).clickable(
|
||||||
onClick = { methods.value.openPlatform("com.google.android.youtube", "https://m.youtube.com") }
|
onClick = { Actions.instance.openPlatform("com.google.android.youtube", "https://m.youtube.com") }
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
Spacer(modifier = modifier.padding(start = 12.dp))
|
Spacer(modifier = modifier.padding(start = 12.dp))
|
||||||
@ -315,7 +315,7 @@ fun AboutColumn(
|
|||||||
"${Strings.open()} Youtube Music",
|
"${Strings.open()} Youtube Music",
|
||||||
tint = Color.Unspecified,
|
tint = Color.Unspecified,
|
||||||
modifier = Modifier.clip(SpotiFlyerShapes.small).clickable(
|
modifier = Modifier.clip(SpotiFlyerShapes.small).clickable(
|
||||||
onClick = { methods.value.openPlatform("com.google.android.apps.youtube.music", "https://music.youtube.com/") }
|
onClick = { Actions.instance.openPlatform("com.google.android.apps.youtube.music", "https://music.youtube.com/") }
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -336,7 +336,7 @@ fun AboutColumn(
|
|||||||
Row(
|
Row(
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
modifier = Modifier.fillMaxWidth().clickable(
|
modifier = Modifier.fillMaxWidth().clickable(
|
||||||
onClick = { methods.value.openPlatform("", "https://github.com/Shabinder/SpotiFlyer") }
|
onClick = { Actions.instance.openPlatform("", "https://github.com/Shabinder/SpotiFlyer") }
|
||||||
)
|
)
|
||||||
.padding(vertical = 6.dp)
|
.padding(vertical = 6.dp)
|
||||||
) {
|
) {
|
||||||
@ -355,7 +355,7 @@ fun AboutColumn(
|
|||||||
}
|
}
|
||||||
Row(
|
Row(
|
||||||
modifier = modifier.fillMaxWidth().padding(vertical = 6.dp)
|
modifier = modifier.fillMaxWidth().padding(vertical = 6.dp)
|
||||||
.clickable(onClick = { methods.value.openPlatform("", "https://github.com/Shabinder/SpotiFlyer/blob/main/CONTRIBUTING.md") }),
|
.clickable(onClick = { Actions.instance.openPlatform("", "https://github.com/Shabinder/SpotiFlyer/blob/main/CONTRIBUTING.md") }),
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
Icon(Icons.Rounded.Flag, Strings.help() + Strings.translate(), Modifier.size(32.dp))
|
Icon(Icons.Rounded.Flag, Strings.help() + Strings.translate(), Modifier.size(32.dp))
|
||||||
@ -395,7 +395,7 @@ fun AboutColumn(
|
|||||||
modifier = modifier.fillMaxWidth().padding(vertical = 6.dp)
|
modifier = modifier.fillMaxWidth().padding(vertical = 6.dp)
|
||||||
.clickable(
|
.clickable(
|
||||||
onClick = {
|
onClick = {
|
||||||
methods.value.shareApp()
|
Actions.instance.shareApp()
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
@ -29,14 +29,7 @@ import androidx.compose.animation.core.tween
|
|||||||
import androidx.compose.animation.core.updateTransition
|
import androidx.compose.animation.core.updateTransition
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.Row
|
|
||||||
import androidx.compose.foundation.layout.Spacer
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.foundation.layout.size
|
|
||||||
import androidx.compose.material.Icon
|
import androidx.compose.material.Icon
|
||||||
import androidx.compose.material.IconButton
|
import androidx.compose.material.IconButton
|
||||||
import androidx.compose.material.MaterialTheme
|
import androidx.compose.material.MaterialTheme
|
||||||
@ -52,6 +45,7 @@ import androidx.compose.ui.Alignment
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.alpha
|
import androidx.compose.ui.draw.alpha
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.unit.Dp
|
import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.arkivanov.decompose.extensions.compose.jetbrains.Children
|
import com.arkivanov.decompose.extensions.compose.jetbrains.Children
|
||||||
@ -179,7 +173,8 @@ fun AppBar(
|
|||||||
Image(
|
Image(
|
||||||
SpotiFlyerLogo(),
|
SpotiFlyerLogo(),
|
||||||
Strings.spotiflyerLogo(),
|
Strings.spotiflyerLogo(),
|
||||||
Modifier.size(32.dp),
|
Modifier.requiredHeight(66.dp).requiredWidth(42.dp),
|
||||||
|
contentScale = ContentScale.FillHeight
|
||||||
)
|
)
|
||||||
Spacer(Modifier.padding(horizontal = 4.dp))
|
Spacer(Modifier.padding(horizontal = 4.dp))
|
||||||
Text(
|
Text(
|
||||||
|
@ -17,13 +17,7 @@
|
|||||||
package com.shabinder.common.uikit.screens.splash
|
package com.shabinder.common.uikit.screens.splash
|
||||||
|
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.layout.Box
|
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.Row
|
|
||||||
import androidx.compose.foundation.layout.Spacer
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.material.Icon
|
import androidx.compose.material.Icon
|
||||||
import androidx.compose.material.Text
|
import androidx.compose.material.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
@ -56,7 +50,7 @@ fun Splash(modifier: Modifier = Modifier, onTimeout: () -> Unit) {
|
|||||||
delay(SplashWaitTime)
|
delay(SplashWaitTime)
|
||||||
currentOnTimeout()
|
currentOnTimeout()
|
||||||
}
|
}
|
||||||
Image(SpotiFlyerLogo(), Strings.spotiflyerLogo())
|
Image(SpotiFlyerLogo(), Strings.spotiflyerLogo(),modifier = Modifier.fillMaxSize())
|
||||||
MadeInIndia(Modifier.align(Alignment.BottomCenter))
|
MadeInIndia(Modifier.align(Alignment.BottomCenter))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,8 +10,16 @@ import org.koin.dsl.module
|
|||||||
|
|
||||||
internal class AndroidAnalyticsManager(private val mainActivity: Activity) : AnalyticsManager {
|
internal class AndroidAnalyticsManager(private val mainActivity: Activity) : AnalyticsManager {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private var isInitialised = false
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
init()
|
// Don't Init If Instantiated on Diff Activities
|
||||||
|
if (!isInitialised) {
|
||||||
|
isInitialised = true
|
||||||
|
init()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun init() {
|
override fun init() {
|
||||||
@ -45,7 +53,8 @@ internal class AndroidAnalyticsManager(private val mainActivity: Activity) : Ana
|
|||||||
Countly.sharedInstance().consent().giveConsentAll()
|
Countly.sharedInstance().consent().giveConsentAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun isTracking(): Boolean = Countly.sharedInstance().consent().getConsent(Countly.CountlyFeatureNames.events)
|
override fun isTracking(): Boolean =
|
||||||
|
Countly.sharedInstance().consent().getConsent(Countly.CountlyFeatureNames.events)
|
||||||
|
|
||||||
override fun revokeConsent() {
|
override fun revokeConsent() {
|
||||||
Countly.sharedInstance().consent().removeConsentAll()
|
Countly.sharedInstance().consent().removeConsentAll()
|
||||||
|
@ -35,9 +35,8 @@ import com.shabinder.common.di.getMemoryEfficientBitmap
|
|||||||
import com.shabinder.common.models.TrackDetails
|
import com.shabinder.common.models.TrackDetails
|
||||||
import com.shabinder.common.models.dispatcherIO
|
import com.shabinder.common.models.dispatcherIO
|
||||||
import com.shabinder.common.models.event.coroutines.SuspendableEvent
|
import com.shabinder.common.models.event.coroutines.SuspendableEvent
|
||||||
import com.shabinder.common.models.event.coroutines.failure
|
|
||||||
import com.shabinder.common.models.event.coroutines.map
|
import com.shabinder.common.models.event.coroutines.map
|
||||||
import com.shabinder.common.models.methods
|
import com.shabinder.common.models.Actions
|
||||||
import com.shabinder.database.Database
|
import com.shabinder.database.Database
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
@ -70,7 +69,7 @@ class AndroidFileManager(
|
|||||||
|
|
||||||
override fun fileSeparator(): String = File.separator
|
override fun fileSeparator(): String = File.separator
|
||||||
|
|
||||||
override fun imageCacheDir(): String = methods.value.platformActions.imageCacheDir
|
override fun imageCacheDir(): String = Actions.instance.platformActions.imageCacheDir
|
||||||
|
|
||||||
// fun call in order to always access Updated Value
|
// fun call in order to always access Updated Value
|
||||||
override fun defaultDir(): String = (preferenceManager.downloadDir ?: defaultBaseDir) +
|
override fun defaultDir(): String = (preferenceManager.downloadDir ?: defaultBaseDir) +
|
||||||
@ -159,7 +158,7 @@ class AndroidFileManager(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun addToLibrary(path: String) = methods.value.platformActions.addToLibrary(path)
|
override fun addToLibrary(path: String) = Actions.instance.platformActions.addToLibrary(path)
|
||||||
|
|
||||||
override suspend fun loadImage(url: String, reqWidth: Int, reqHeight: Int): Picture =
|
override suspend fun loadImage(url: String, reqWidth: Int, reqHeight: Int): Picture =
|
||||||
withContext(dispatcherIO) {
|
withContext(dispatcherIO) {
|
||||||
|
@ -36,7 +36,7 @@ import com.shabinder.common.models.dispatcherIO
|
|||||||
import com.shabinder.common.models.event.coroutines.SuspendableEvent
|
import com.shabinder.common.models.event.coroutines.SuspendableEvent
|
||||||
import com.shabinder.common.models.event.coroutines.failure
|
import com.shabinder.common.models.event.coroutines.failure
|
||||||
import com.shabinder.common.models.event.coroutines.map
|
import com.shabinder.common.models.event.coroutines.map
|
||||||
import com.shabinder.common.models.methods
|
import com.shabinder.common.models.Actions
|
||||||
import com.shabinder.database.Database
|
import com.shabinder.database.Database
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
@ -165,7 +165,7 @@ class DesktopFileManager(
|
|||||||
}
|
}
|
||||||
SuspendableEvent.success(trackDetails.outputFilePath)
|
SuspendableEvent.success(trackDetails.outputFilePath)
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
if(e is JaffreeException) methods.value.showPopUpMessage("No FFmpeg found at path.")
|
if(e is JaffreeException) Actions.instance.showPopUpMessage("No FFmpeg found at path.")
|
||||||
if (songFile.exists()) songFile.delete()
|
if (songFile.exists()) songFile.delete()
|
||||||
logger.e { "${songFile.absolutePath} could not be created" }
|
logger.e { "${songFile.absolutePath} could not be created" }
|
||||||
SuspendableEvent.error(e)
|
SuspendableEvent.error(e)
|
||||||
|
@ -17,7 +17,7 @@ actual interface PlatformActions {
|
|||||||
fun sendTracksToService(array: List<TrackDetails>)
|
fun sendTracksToService(array: List<TrackDetails>)
|
||||||
}
|
}
|
||||||
|
|
||||||
actual val StubPlatformActions = object : PlatformActions {
|
internal actual val StubPlatformActions = object : PlatformActions {
|
||||||
override val imageCacheDir = ""
|
override val imageCacheDir = ""
|
||||||
|
|
||||||
override val sharedPreferences: SharedPreferences? = null
|
override val sharedPreferences: SharedPreferences? = null
|
||||||
|
@ -1,11 +1,7 @@
|
|||||||
package com.shabinder.common.models
|
package com.shabinder.common.models
|
||||||
|
|
||||||
import co.touchlab.stately.freeze
|
import co.touchlab.stately.freeze
|
||||||
|
import kotlin.jvm.JvmStatic
|
||||||
/*
|
|
||||||
* Holder to call platform actions from anywhere
|
|
||||||
* */
|
|
||||||
val methods: NativeAtomicReference<Actions> = NativeAtomicReference(stubActions().freeze())
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Interface Having All Platform Dependent Functions
|
* Interface Having All Platform Dependent Functions
|
||||||
@ -42,6 +38,20 @@ interface Actions {
|
|||||||
// Open / Redirect to another Platform
|
// Open / Redirect to another Platform
|
||||||
fun openPlatform(packageID: String, platformLink: String)
|
fun openPlatform(packageID: String, platformLink: String)
|
||||||
fun writeMp3Tags(trackDetails: TrackDetails)
|
fun writeMp3Tags(trackDetails: TrackDetails)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
/*
|
||||||
|
* Holder to call platform actions from anywhere
|
||||||
|
* */
|
||||||
|
@JvmStatic
|
||||||
|
var instance: Actions
|
||||||
|
get() = methodsAtomicRef.value
|
||||||
|
set(value) {
|
||||||
|
methodsAtomicRef.value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
private val methodsAtomicRef = NativeAtomicReference(stubActions().freeze())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun stubActions(): Actions = object : Actions {
|
private fun stubActions(): Actions = object : Actions {
|
||||||
|
@ -2,4 +2,4 @@ package com.shabinder.common.models
|
|||||||
|
|
||||||
expect interface PlatformActions
|
expect interface PlatformActions
|
||||||
|
|
||||||
expect val StubPlatformActions: PlatformActions
|
internal expect val StubPlatformActions: PlatformActions
|
||||||
|
@ -2,4 +2,4 @@ package com.shabinder.common.models
|
|||||||
|
|
||||||
actual interface PlatformActions
|
actual interface PlatformActions
|
||||||
|
|
||||||
actual val StubPlatformActions = object : PlatformActions {}
|
internal actual val StubPlatformActions = object : PlatformActions {}
|
||||||
|
@ -2,4 +2,4 @@ package com.shabinder.common.models
|
|||||||
|
|
||||||
|
|
||||||
actual interface PlatformActions
|
actual interface PlatformActions
|
||||||
actual val StubPlatformActions = object : PlatformActions {}
|
internal actual val StubPlatformActions = object : PlatformActions {}
|
@ -14,34 +14,63 @@
|
|||||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<vector android:height="150dp" android:viewportHeight="512"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:viewportWidth="512" android:width="150dp"
|
xmlns:aapt="http://schemas.android.com/aapt"
|
||||||
xmlns:aapt="http://schemas.android.com/aapt" xmlns:android="http://schemas.android.com/apk/res/android">
|
android:width="108dp"
|
||||||
<path android:pathData="M256,256m-256,0a256,256 0,1 1,512 0a256,256 0,1 1,-512 0">
|
android:height="108dp"
|
||||||
<aapt:attr name="android:fillColor">
|
android:gravity="center"
|
||||||
<gradient android:endX="437.019" android:endY="74.981"
|
android:viewportWidth="108"
|
||||||
android:startX="74.981" android:startY="437.019" android:type="linear">
|
android:viewportHeight="108">
|
||||||
<item android:color="#FF736BFD" android:offset="0"/>
|
<group android:scaleX="0.10546875"
|
||||||
<item android:color="#FFF54187" android:offset="1"/>
|
android:scaleY="0.10546875"
|
||||||
</gradient>
|
android:translateX="27"
|
||||||
</aapt:attr>
|
android:translateY="27">
|
||||||
</path>
|
<path
|
||||||
<path android:fillColor="#FF000000" android:pathData="M377,356.7c-68.9,-45.4 -155.6,-56.4 -257.6,-32.7c-20.5,4.8 -13.6,35.8 7.3,31.2C290.7,317 351.6,386 368.2,386C384,386 390.2,365.4 377,356.7z"/>
|
android:pathData="M256,256m-256,0a256,256 0,1 1,512 0a256,256 0,1 1,-512 0">
|
||||||
<path android:fillColor="#FF000000" android:pathData="M112.1,275.1C203.9,253.4 308.1,266 384,308c18.5,10.2 34,-17.8 15.5,-28c-82.7,-45.7 -195.6,-59.5 -294.7,-36C84.2,248.8 91.5,280 112.1,275.1L112.1,275.1z"/>
|
<aapt:attr name="android:fillColor">
|
||||||
<path android:fillColor="#FF000000" android:pathData="M100,191.9c96.6,-29.6 232.2,-13.4 308.7,36.9c17.6,11.5 35.3,-15.1 17.6,-26.7c-84.9,-55.8 -229.2,-73.3 -335.6,-40.8C70.4,167.5 79.9,198.1 100,191.9L100,191.9z"/>
|
<gradient
|
||||||
<path android:pathData="M507.8,438.2c-1.6,97.2 -141.9,97.1 -143.5,0C365.9,341 506.2,341 507.8,438.2z">
|
android:startY="437.0193"
|
||||||
<aapt:attr name="android:fillColor">
|
android:startX="74.9807"
|
||||||
<gradient android:endX="384.197" android:endY="490.009"
|
android:endY="74.9807"
|
||||||
android:startX="487.832" android:startY="386.374" android:type="linear">
|
android:endX="437.0193"
|
||||||
<item android:color="#FF736BFD" android:offset="0"/>
|
android:type="linear">
|
||||||
<item android:color="#FFF54187" android:offset="1"/>
|
<item android:offset="0" android:color="#FF736BFD"/>
|
||||||
</gradient>
|
<item android:offset="1" android:color="#FFF54187"/>
|
||||||
</aapt:attr>
|
</gradient>
|
||||||
</path>
|
</aapt:attr>
|
||||||
<path android:fillColor="#FF000000"
|
</path>
|
||||||
android:pathData="M486.8,456.8c-0.6,-2.4 -6.9,-1 -8.5,-1.4c11.5,-82 -82.4,-86.7 -87.1,-22.2c0.3,1.8 -1,6.7 2.2,6.6c0,0 8.6,0 8.6,0c3.1,0.1 2,-4.7 2.2,-6.6c0.1,-23.3 35,-23.3 35.2,0c0,0 0,6.9 0,6.9c-0.1,2.8 4.4,2.8 4.3,0c5,-35.2 -43.8,-40.1 -43.8,-4.7h-4.3c-1.6,-53.7 77.2,-55.9 78.4,-2.2c0,0 0,24.4 0,24.4c-0.1,2.9 3.8,2.1 5.6,2.2l-20.7,21l-20.7,-21c1.8,-0.1 5.6,0.7 5.6,-2.2c0,0 0,-8.8 0,-8.8c0,-2.8 -4.4,-2.8 -4.3,0c0,0 0,6.6 0,6.6c-2.2,0.2 -11.3,-1.3 -8,3.7c0,0 25.9,26.3 25.9,26.3c0.8,0.9 2.2,0.9 3.1,0C460.6,484.4 489.4,458.3 486.8,456.8z"
|
<path
|
||||||
android:strokeColor="#000" android:strokeWidth=".75"/>
|
android:fillColor="#FF000000"
|
||||||
<path android:fillColor="#00000000"
|
android:pathData="M377,356.7c-68.9,-45.4 -155.6,-56.4 -257.6,-32.7c-20.5,4.8 -13.6,35.8 7.3,31.2C290.7,317 351.6,386 368.2,386C384,386 390.2,365.4 377,356.7z"/>
|
||||||
android:pathData="M510,437.5c-1.7,96.2 -142.1,96.2 -143.8,0C367.9,341.3 508.4,341.3 510,437.5z"
|
<path
|
||||||
android:strokeColor="#000" android:strokeWidth="6"/>
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M112.1,275.1C203.9,253.4 308.1,266 384,308c18.5,10.2 34,-17.8 15.5,-28c-82.7,-45.7 -195.6,-59.5 -294.7,-36C84.2,248.8 91.5,280 112.1,275.1L112.1,275.1z"/>
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M100,191.9c96.6,-29.6 232.2,-13.4 308.7,36.9c17.6,11.5 35.3,-15.1 17.6,-26.7c-84.9,-55.8 -229.2,-73.3 -335.6,-40.8C70.4,167.5 79.9,198.1 100,191.9L100,191.9z"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M507.8,438.2c-1.6,97.2 -141.9,97.1 -143.5,0C365.9,341 506.2,341 507.8,438.2z">
|
||||||
|
<aapt:attr name="android:fillColor">
|
||||||
|
<gradient
|
||||||
|
android:startY="386.3741"
|
||||||
|
android:startX="487.8323"
|
||||||
|
android:endY="490.009"
|
||||||
|
android:endX="384.1974"
|
||||||
|
android:type="linear">
|
||||||
|
<item android:offset="0" android:color="#FF736BFD"/>
|
||||||
|
<item android:offset="1" android:color="#FFF54187"/>
|
||||||
|
</gradient>
|
||||||
|
</aapt:attr>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M486.8,456.8c-0.6,-2.4 -6.9,-1 -8.5,-1.4c11.5,-82 -82.4,-86.7 -87.1,-22.2c0.3,1.8 -1,6.7 2.2,6.6c0,0 8.6,0 8.6,0c3.1,0.1 2,-4.7 2.2,-6.6c0.1,-23.3 35,-23.3 35.2,0c0,0 0,6.9 0,6.9c-0.1,2.8 4.4,2.8 4.3,0c5,-35.2 -43.8,-40.1 -43.8,-4.7h-4.3c-1.6,-53.7 77.2,-55.9 78.4,-2.2c0,0 0,24.4 0,24.4c-0.1,2.9 3.8,2.1 5.6,2.2l-20.7,21l-20.7,-21c1.8,-0.1 5.6,0.7 5.6,-2.2c0,0 0,-8.8 0,-8.8c0,-2.8 -4.4,-2.8 -4.3,0c0,0 0,6.6 0,6.6c-2.2,0.2 -11.3,-1.3 -8,3.7c0,0 25.9,26.3 25.9,26.3c0.8,0.9 2.2,0.9 3.1,0C460.6,484.4 489.4,458.3 486.8,456.8z"
|
||||||
|
android:strokeWidth="0.75"
|
||||||
|
android:strokeColor="#000000"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M510,437.5c-1.7,96.2 -142.1,96.2 -143.8,0C367.9,341.3 508.4,341.3 510,437.5z"
|
||||||
|
android:strokeWidth="6"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#000000"/>
|
||||||
|
</group>
|
||||||
</vector>
|
</vector>
|
||||||
|
@ -0,0 +1,36 @@
|
|||||||
|
package com.shabinder.common.di
|
||||||
|
|
||||||
|
import com.shabinder.common.core_components.analytics.AnalyticsEvent
|
||||||
|
import com.shabinder.common.core_components.analytics.AnalyticsManager
|
||||||
|
import com.shabinder.common.models.dispatcherIO
|
||||||
|
import com.shabinder.common.providers.spotify.SpotifyProvider
|
||||||
|
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||||
|
import kotlinx.coroutines.GlobalScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.koin.dsl.module
|
||||||
|
|
||||||
|
class ApplicationInit(
|
||||||
|
private val spotifyProvider: SpotifyProvider,
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
private var isFirstLaunch = true
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Init Basic Necessary Items in here,
|
||||||
|
* will be called,
|
||||||
|
* Android / IOS: Splash Screen
|
||||||
|
* Desktop: App Startup
|
||||||
|
* */
|
||||||
|
@OptIn(DelicateCoroutinesApi::class)
|
||||||
|
fun init() = GlobalScope.launch(dispatcherIO) {
|
||||||
|
isFirstLaunch = false
|
||||||
|
spotifyProvider.authenticateSpotifyClient()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun appInitModule() = module {
|
||||||
|
single {
|
||||||
|
ApplicationInit(get())
|
||||||
|
}
|
||||||
|
}
|
@ -33,6 +33,7 @@ fun initKoin(enableNetworkLogs: Boolean = false, appDeclaration: KoinAppDeclarat
|
|||||||
listOf(
|
listOf(
|
||||||
providersModule(enableNetworkLogs),
|
providersModule(enableNetworkLogs),
|
||||||
databaseModule(),
|
databaseModule(),
|
||||||
|
appInitModule(),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -23,10 +23,7 @@ import com.arkivanov.mvikotlin.extensions.coroutines.SuspendExecutor
|
|||||||
import com.shabinder.common.list.SpotiFlyerList
|
import com.shabinder.common.list.SpotiFlyerList
|
||||||
import com.shabinder.common.list.SpotiFlyerList.State
|
import com.shabinder.common.list.SpotiFlyerList.State
|
||||||
import com.shabinder.common.list.store.SpotiFlyerListStore.Intent
|
import com.shabinder.common.list.store.SpotiFlyerListStore.Intent
|
||||||
import com.shabinder.common.models.DownloadStatus
|
import com.shabinder.common.models.*
|
||||||
import com.shabinder.common.models.PlatformQueryResult
|
|
||||||
import com.shabinder.common.models.TrackDetails
|
|
||||||
import com.shabinder.common.models.methods
|
|
||||||
import com.shabinder.common.providers.downloadTracks
|
import com.shabinder.common.providers.downloadTracks
|
||||||
import kotlinx.coroutines.flow.collect
|
import kotlinx.coroutines.flow.collect
|
||||||
|
|
||||||
@ -112,14 +109,14 @@ internal class SpotiFlyerListStoreProvider(dependencies: SpotiFlyerList.Dependen
|
|||||||
)
|
)
|
||||||
|
|
||||||
val finalList = intent.trackList.filter { it.downloaded == DownloadStatus.NotDownloaded }
|
val finalList = intent.trackList.filter { it.downloaded == DownloadStatus.NotDownloaded }
|
||||||
if (finalList.isEmpty()) methods.value.showPopUpMessage("All Songs are Processed")
|
if (finalList.isEmpty()) Actions.instance.showPopUpMessage("All Songs are Processed")
|
||||||
else downloadTracks(finalList, fetchQuery, fileManager)
|
else downloadTracks(finalList, fetchQuery, fileManager)
|
||||||
}
|
}
|
||||||
is Intent.StartDownload -> {
|
is Intent.StartDownload -> {
|
||||||
dispatch(Result.UpdateTrackItem(intent.track.copy(downloaded = DownloadStatus.Queued)))
|
dispatch(Result.UpdateTrackItem(intent.track.copy(downloaded = DownloadStatus.Queued)))
|
||||||
downloadTracks(listOf(intent.track), fetchQuery, fileManager)
|
downloadTracks(listOf(intent.track), fetchQuery, fileManager)
|
||||||
}
|
}
|
||||||
is Intent.RefreshTracksStatuses -> methods.value.queryActiveTracks()
|
is Intent.RefreshTracksStatuses -> Actions.instance.queryActiveTracks()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -161,6 +158,7 @@ internal class SpotiFlyerListStoreProvider(dependencies: SpotiFlyerList.Dependen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return updatedList
|
return updatedList
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ import com.shabinder.common.main.SpotiFlyerMain.*
|
|||||||
import com.shabinder.common.main.store.SpotiFlyerMainStore.Intent
|
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.Actions
|
||||||
|
|
||||||
internal class SpotiFlyerMainImpl(
|
internal class SpotiFlyerMainImpl(
|
||||||
componentContext: ComponentContext,
|
componentContext: ComponentContext,
|
||||||
@ -57,8 +57,8 @@ internal class SpotiFlyerMainImpl(
|
|||||||
override val analytics = mainAnalytics
|
override val analytics = mainAnalytics
|
||||||
|
|
||||||
override fun onLinkSearch(link: String) {
|
override fun onLinkSearch(link: String) {
|
||||||
if (methods.value.isInternetAvailable) mainOutput.callback(Output.Search(link = link))
|
if (Actions.instance.isInternetAvailable) mainOutput.callback(Output.Search(link = link))
|
||||||
else methods.value.showPopUpMessage("Check Network Connection Please")
|
else Actions.instance.showPopUpMessage("Check Network Connection Please")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onInputLinkChanged(link: String) {
|
override fun onInputLinkChanged(link: String) {
|
||||||
|
@ -24,7 +24,7 @@ import com.shabinder.common.main.SpotiFlyerMain
|
|||||||
import com.shabinder.common.main.SpotiFlyerMain.State
|
import com.shabinder.common.main.SpotiFlyerMain.State
|
||||||
import com.shabinder.common.main.store.SpotiFlyerMainStore.Intent
|
import com.shabinder.common.main.store.SpotiFlyerMainStore.Intent
|
||||||
import com.shabinder.common.models.DownloadRecord
|
import com.shabinder.common.models.DownloadRecord
|
||||||
import com.shabinder.common.models.methods
|
import com.shabinder.common.models.Actions
|
||||||
import com.squareup.sqldelight.runtime.coroutines.asFlow
|
import com.squareup.sqldelight.runtime.coroutines.asFlow
|
||||||
import com.squareup.sqldelight.runtime.coroutines.mapToList
|
import com.squareup.sqldelight.runtime.coroutines.mapToList
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@ -75,9 +75,9 @@ internal class SpotiFlyerMainStoreProvider(dependencies: SpotiFlyerMain.Dependen
|
|||||||
|
|
||||||
override suspend fun executeIntent(intent: Intent, getState: () -> State) {
|
override suspend fun executeIntent(intent: Intent, getState: () -> State) {
|
||||||
when (intent) {
|
when (intent) {
|
||||||
is Intent.OpenPlatform -> methods.value.openPlatform(intent.platformID, intent.platformLink)
|
is Intent.OpenPlatform -> Actions.instance.openPlatform(intent.platformID, intent.platformLink)
|
||||||
is Intent.GiveDonation -> methods.value.giveDonation()
|
is Intent.GiveDonation -> Actions.instance.giveDonation()
|
||||||
is Intent.ShareApp -> methods.value.shareApp()
|
is Intent.ShareApp -> Actions.instance.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))
|
||||||
is Intent.ToggleAnalytics -> {
|
is Intent.ToggleAnalytics -> {
|
||||||
|
@ -21,7 +21,7 @@ import com.arkivanov.mvikotlin.core.store.SimpleBootstrapper
|
|||||||
import com.arkivanov.mvikotlin.core.store.Store
|
import com.arkivanov.mvikotlin.core.store.Store
|
||||||
import com.arkivanov.mvikotlin.extensions.coroutines.SuspendExecutor
|
import com.arkivanov.mvikotlin.extensions.coroutines.SuspendExecutor
|
||||||
import com.shabinder.common.models.AudioQuality
|
import com.shabinder.common.models.AudioQuality
|
||||||
import com.shabinder.common.models.methods
|
import com.shabinder.common.models.Actions
|
||||||
import com.shabinder.common.preference.SpotiFlyerPreference
|
import com.shabinder.common.preference.SpotiFlyerPreference
|
||||||
import com.shabinder.common.preference.SpotiFlyerPreference.State
|
import com.shabinder.common.preference.SpotiFlyerPreference.State
|
||||||
import com.shabinder.common.preference.store.SpotiFlyerPreferenceStore.Intent
|
import com.shabinder.common.preference.store.SpotiFlyerPreferenceStore.Intent
|
||||||
@ -56,9 +56,9 @@ internal class SpotiFlyerPreferenceStoreProvider(
|
|||||||
|
|
||||||
override suspend fun executeIntent(intent: Intent, getState: () -> State) {
|
override suspend fun executeIntent(intent: Intent, getState: () -> State) {
|
||||||
when (intent) {
|
when (intent) {
|
||||||
is Intent.OpenPlatform -> methods.value.openPlatform(intent.platformID, intent.platformLink)
|
is Intent.OpenPlatform -> Actions.instance.openPlatform(intent.platformID, intent.platformLink)
|
||||||
is Intent.GiveDonation -> methods.value.giveDonation()
|
is Intent.GiveDonation -> Actions.instance.giveDonation()
|
||||||
is Intent.ShareApp -> methods.value.shareApp()
|
is Intent.ShareApp -> Actions.instance.shareApp()
|
||||||
is Intent.ToggleAnalytics -> {
|
is Intent.ToggleAnalytics -> {
|
||||||
dispatch(Result.AnalyticsToggled(intent.enabled))
|
dispatch(Result.AnalyticsToggled(intent.enabled))
|
||||||
preferenceManager.toggleAnalytics(intent.enabled)
|
preferenceManager.toggleAnalytics(intent.enabled)
|
||||||
|
@ -18,7 +18,7 @@ package com.shabinder.common.providers
|
|||||||
|
|
||||||
import com.shabinder.common.core_components.file_manager.FileManager
|
import com.shabinder.common.core_components.file_manager.FileManager
|
||||||
import com.shabinder.common.models.TrackDetails
|
import com.shabinder.common.models.TrackDetails
|
||||||
import com.shabinder.common.models.methods
|
import com.shabinder.common.models.Actions
|
||||||
|
|
||||||
|
|
||||||
actual suspend fun downloadTracks(
|
actual suspend fun downloadTracks(
|
||||||
@ -27,6 +27,6 @@ actual suspend fun downloadTracks(
|
|||||||
fileManager: FileManager
|
fileManager: FileManager
|
||||||
) {
|
) {
|
||||||
if (list.isNotEmpty()) {
|
if (list.isNotEmpty()) {
|
||||||
methods.value.platformActions.sendTracksToService(ArrayList(list))
|
Actions.instance.platformActions.sendTracksToService(ArrayList(list))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ package com.shabinder.common.providers.spotify.requests
|
|||||||
|
|
||||||
import com.shabinder.common.models.SpotiFlyerException
|
import com.shabinder.common.models.SpotiFlyerException
|
||||||
import com.shabinder.common.models.event.coroutines.SuspendableEvent
|
import com.shabinder.common.models.event.coroutines.SuspendableEvent
|
||||||
import com.shabinder.common.models.methods
|
import com.shabinder.common.models.Actions
|
||||||
import com.shabinder.common.models.spotify.TokenData
|
import com.shabinder.common.models.spotify.TokenData
|
||||||
import com.shabinder.common.utils.globalJson
|
import com.shabinder.common.utils.globalJson
|
||||||
import io.ktor.client.*
|
import io.ktor.client.*
|
||||||
@ -32,9 +32,12 @@ import io.ktor.http.*
|
|||||||
import kotlin.native.concurrent.SharedImmutable
|
import kotlin.native.concurrent.SharedImmutable
|
||||||
|
|
||||||
suspend fun authenticateSpotify(): SuspendableEvent<TokenData, Throwable> = SuspendableEvent {
|
suspend fun authenticateSpotify(): SuspendableEvent<TokenData, Throwable> = SuspendableEvent {
|
||||||
if (methods.value.isInternetAvailable) {
|
if (Actions.instance.isInternetAvailable) {
|
||||||
spotifyAuthClient.post("https://accounts.spotify.com/api/token") {
|
spotifyAuthClient.post("https://accounts.spotify.com/api/token") {
|
||||||
body = FormDataContent(Parameters.build { append("grant_type", "client_credentials") })
|
body = FormDataContent(Parameters.build {
|
||||||
|
@Suppress("EXPERIMENTAL_API_USAGE_FUTURE_ERROR")
|
||||||
|
append("grant_type", "client_credentials")
|
||||||
|
})
|
||||||
}
|
}
|
||||||
} else throw SpotiFlyerException.NoInternetException()
|
} else throw SpotiFlyerException.NoInternetException()
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ import com.shabinder.common.models.AllPlatforms
|
|||||||
import com.shabinder.common.models.DownloadResult
|
import com.shabinder.common.models.DownloadResult
|
||||||
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.Actions
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.flow.collect
|
import kotlinx.coroutines.flow.collect
|
||||||
@ -48,7 +48,7 @@ actual suspend fun downloadTracks(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
is DownloadResult.Success -> { // Todo clear map
|
is DownloadResult.Success -> { // Todo clear map
|
||||||
dir.saveFileWithMetadata(it.byteArray, track, methods.value::writeMp3Tags)
|
dir.saveFileWithMetadata(it.byteArray, track, Actions.instance::writeMp3Tags)
|
||||||
DownloadProgressFlow.replayCache.getOrElse(
|
DownloadProgressFlow.replayCache.getOrElse(
|
||||||
0
|
0
|
||||||
) { hashMapOf() }.toMutableMap().apply {
|
) { hashMapOf() }.toMutableMap().apply {
|
||||||
|
@ -23,6 +23,7 @@ import com.arkivanov.mvikotlin.core.store.StoreFactory
|
|||||||
import com.shabinder.common.core_components.analytics.AnalyticsManager
|
import com.shabinder.common.core_components.analytics.AnalyticsManager
|
||||||
import com.shabinder.common.core_components.file_manager.FileManager
|
import com.shabinder.common.core_components.file_manager.FileManager
|
||||||
import com.shabinder.common.core_components.preference_manager.PreferenceManager
|
import com.shabinder.common.core_components.preference_manager.PreferenceManager
|
||||||
|
import com.shabinder.common.di.ApplicationInit
|
||||||
import com.shabinder.common.list.SpotiFlyerList
|
import com.shabinder.common.list.SpotiFlyerList
|
||||||
import com.shabinder.common.main.SpotiFlyerMain
|
import com.shabinder.common.main.SpotiFlyerMain
|
||||||
import com.shabinder.common.models.Actions
|
import com.shabinder.common.models.Actions
|
||||||
@ -51,6 +52,7 @@ interface SpotiFlyerRoot {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface Dependencies {
|
interface Dependencies {
|
||||||
|
val appInit: ApplicationInit
|
||||||
val storeFactory: StoreFactory
|
val storeFactory: StoreFactory
|
||||||
val database: Database?
|
val database: Database?
|
||||||
val fetchQuery: FetchPlatformQueryResult
|
val fetchQuery: FetchPlatformQueryResult
|
||||||
|
@ -26,19 +26,14 @@ import com.shabinder.common.core_components.analytics.AnalyticsEvent
|
|||||||
import com.shabinder.common.core_components.analytics.AnalyticsView
|
import com.shabinder.common.core_components.analytics.AnalyticsView
|
||||||
import com.shabinder.common.list.SpotiFlyerList
|
import com.shabinder.common.list.SpotiFlyerList
|
||||||
import com.shabinder.common.main.SpotiFlyerMain
|
import com.shabinder.common.main.SpotiFlyerMain
|
||||||
import com.shabinder.common.models.Actions
|
|
||||||
import com.shabinder.common.models.Consumer
|
import com.shabinder.common.models.Consumer
|
||||||
import com.shabinder.common.models.dispatcherIO
|
import com.shabinder.common.models.Actions
|
||||||
import com.shabinder.common.models.methods
|
|
||||||
import com.shabinder.common.preference.SpotiFlyerPreference
|
import com.shabinder.common.preference.SpotiFlyerPreference
|
||||||
import com.shabinder.common.root.SpotiFlyerRoot
|
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.DelicateCoroutinesApi
|
|
||||||
import kotlinx.coroutines.GlobalScope
|
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
internal class SpotiFlyerRootImpl(
|
internal class SpotiFlyerRootImpl(
|
||||||
componentContext: ComponentContext,
|
componentContext: ComponentContext,
|
||||||
@ -47,11 +42,10 @@ internal class SpotiFlyerRootImpl(
|
|||||||
Actions by dependencies.actions {
|
Actions by dependencies.actions {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
AnalyticsEvent.AppLaunch.track(analyticsManager)
|
||||||
instanceKeeper.ensureNeverFrozen()
|
instanceKeeper.ensureNeverFrozen()
|
||||||
methods.value = dependencies.actions.freeze()
|
Actions.instance = dependencies.actions.freeze()
|
||||||
|
appInit.init()
|
||||||
/*Init App Launch & Authenticate Spotify Client*/
|
|
||||||
initAppLaunchAndAuthenticateSpotify(dependencies.fetchQuery::authenticateSpotifyClient)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private val router =
|
private val router =
|
||||||
@ -183,15 +177,6 @@ internal class SpotiFlyerRootImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(DelicateCoroutinesApi::class)
|
|
||||||
private fun initAppLaunchAndAuthenticateSpotify(authenticator: suspend () -> Unit) {
|
|
||||||
GlobalScope.launch(dispatcherIO) {
|
|
||||||
AnalyticsEvent.AppLaunch.track(analyticsManager)
|
|
||||||
/*Authenticate Spotify Client*/
|
|
||||||
authenticator()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private sealed class Configuration : Parcelable {
|
private sealed class Configuration : Parcelable {
|
||||||
@Parcelize
|
@Parcelize
|
||||||
object Main : Configuration()
|
object Main : Configuration()
|
||||||
|
@ -38,10 +38,9 @@ import com.shabinder.common.core_components.file_manager.DownloadProgressFlow
|
|||||||
import com.shabinder.common.core_components.file_manager.FileManager
|
import com.shabinder.common.core_components.file_manager.FileManager
|
||||||
import com.shabinder.common.core_components.preference_manager.PreferenceManager
|
import com.shabinder.common.core_components.preference_manager.PreferenceManager
|
||||||
import com.shabinder.common.core_components.utils.isInternetAccessible
|
import com.shabinder.common.core_components.utils.isInternetAccessible
|
||||||
import com.shabinder.common.models.Actions
|
|
||||||
import com.shabinder.common.models.PlatformActions
|
import com.shabinder.common.models.PlatformActions
|
||||||
import com.shabinder.common.models.TrackDetails
|
import com.shabinder.common.models.TrackDetails
|
||||||
import com.shabinder.common.models.methods
|
import com.shabinder.common.models.Actions
|
||||||
import com.shabinder.common.providers.FetchPlatformQueryResult
|
import com.shabinder.common.providers.FetchPlatformQueryResult
|
||||||
import com.shabinder.common.root.SpotiFlyerRoot
|
import com.shabinder.common.root.SpotiFlyerRoot
|
||||||
import com.shabinder.common.translations.Strings
|
import com.shabinder.common.translations.Strings
|
||||||
@ -98,7 +97,7 @@ fun main() {
|
|||||||
try {
|
try {
|
||||||
FFmpeg.atPath().addArgument("-version").execute()
|
FFmpeg.atPath().addArgument("-version").execute()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
if (e is JaffreeException) methods.value.showPopUpMessage("WARNING!\nFFmpeg not found at path")
|
if (e is JaffreeException) Actions.instance.showPopUpMessage("WARNING!\nFFmpeg not found at path")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -113,6 +112,7 @@ private fun spotiFlyerRoot(componentContext: ComponentContext): SpotiFlyerRoot =
|
|||||||
SpotiFlyerRoot(
|
SpotiFlyerRoot(
|
||||||
componentContext = componentContext,
|
componentContext = componentContext,
|
||||||
dependencies = object : SpotiFlyerRoot.Dependencies {
|
dependencies = object : SpotiFlyerRoot.Dependencies {
|
||||||
|
override val appInit: ApplicationInit = koin.get()
|
||||||
override val storeFactory = DefaultStoreFactory
|
override val storeFactory = DefaultStoreFactory
|
||||||
override val fetchQuery: FetchPlatformQueryResult = koin.get()
|
override val fetchQuery: FetchPlatformQueryResult = koin.get()
|
||||||
override val fileManager: FileManager = koin.get()
|
override val fileManager: FileManager = koin.get()
|
||||||
|
@ -1,88 +0,0 @@
|
|||||||
package nl.bravobit.ffmpeg;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.os.AsyncTask;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class FFmpeg implements FFbinaryInterface {
|
|
||||||
|
|
||||||
private final FFbinaryContextProvider context;
|
|
||||||
|
|
||||||
private static final long MINIMUM_TIMEOUT = 10 * 1000;
|
|
||||||
private long timeout = Long.MAX_VALUE;
|
|
||||||
|
|
||||||
private static FFmpeg instance = null;
|
|
||||||
|
|
||||||
private FFmpeg(FFbinaryContextProvider context) {
|
|
||||||
this.context = context;
|
|
||||||
Log.setDebug(Util.isDebug(this.context.provide()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static FFmpeg getInstance(final Context context) {
|
|
||||||
if (instance == null) {
|
|
||||||
instance = new FFmpeg(new FFbinaryContextProvider() {
|
|
||||||
@Override
|
|
||||||
public Context provide() {
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isSupported() {
|
|
||||||
// get ffmpeg file
|
|
||||||
File ffmpeg = FileUtils.getFFmpeg(context.provide());
|
|
||||||
|
|
||||||
// check if ffmpeg can be executed
|
|
||||||
if (!ffmpeg.canExecute()) {
|
|
||||||
// try to make executable
|
|
||||||
Log.e("ffmpeg cannot execute");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.d("ffmpeg is ready!");
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FFtask execute(Map<String, String> environvenmentVars, String[] cmd, FFcommandExecuteResponseHandler ffmpegExecuteResponseHandler) {
|
|
||||||
if (cmd.length != 0) {
|
|
||||||
final String[] command = new String[cmd.length + 1];
|
|
||||||
command[0] = FileUtils.getFFmpeg(context.provide()).getAbsolutePath();
|
|
||||||
System.arraycopy(cmd, 0, command, 1, cmd.length);
|
|
||||||
FFcommandExecuteAsyncTask task = new FFcommandExecuteAsyncTask(command, environvenmentVars, timeout, ffmpegExecuteResponseHandler);
|
|
||||||
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
|
||||||
return task;
|
|
||||||
} else {
|
|
||||||
throw new IllegalArgumentException("shell command cannot be empty");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FFtask execute(String[] cmd, FFcommandExecuteResponseHandler ffmpegExecuteResponseHandler) {
|
|
||||||
return execute(null, cmd, ffmpegExecuteResponseHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isCommandRunning(FFtask task) {
|
|
||||||
return task != null && !task.isProcessCompleted();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean killRunningProcesses(FFtask task) {
|
|
||||||
return task != null && task.killRunningProcess();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setTimeout(long timeout) {
|
|
||||||
if (timeout >= MINIMUM_TIMEOUT) {
|
|
||||||
this.timeout = timeout;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,86 @@
|
|||||||
|
package nl.bravobit.ffmpeg
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import nl.bravobit.ffmpeg.Log.setDebug
|
||||||
|
import nl.bravobit.ffmpeg.Util.isDebug
|
||||||
|
import nl.bravobit.ffmpeg.FileUtils.getFFmpeg
|
||||||
|
import nl.bravobit.ffmpeg.Log.e
|
||||||
|
import nl.bravobit.ffmpeg.Log.d
|
||||||
|
import android.os.AsyncTask
|
||||||
|
import java.lang.IllegalArgumentException
|
||||||
|
|
||||||
|
class FFmpeg private constructor(private val context: FFbinaryContextProvider) : FFbinaryInterface {
|
||||||
|
private var timeout = Long.MAX_VALUE
|
||||||
|
|
||||||
|
init {
|
||||||
|
setDebug(isDebug(context.provide()))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isSupported(): Boolean {
|
||||||
|
// get ffmpeg file
|
||||||
|
val ffmpeg = getFFmpeg(context.provide())
|
||||||
|
|
||||||
|
// check if ffmpeg can be executed
|
||||||
|
if (!ffmpeg.canExecute()) {
|
||||||
|
// try to make executable
|
||||||
|
e("ffmpeg cannot execute")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
d("ffmpeg is ready!")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun execute(
|
||||||
|
environmentVars: Map<String, String>,
|
||||||
|
cmd: Array<String>,
|
||||||
|
ffmpegExecuteResponseHandler: FFcommandExecuteResponseHandler
|
||||||
|
): FFtask {
|
||||||
|
return if (cmd.isNotEmpty()) {
|
||||||
|
val command = arrayOfNulls<String>(cmd.size + 1)
|
||||||
|
command[0] = getFFmpeg(context.provide()).absolutePath
|
||||||
|
System.arraycopy(cmd, 0, command, 1, cmd.size)
|
||||||
|
val task = FFcommandExecuteAsyncTask(
|
||||||
|
command,
|
||||||
|
environmentVars,
|
||||||
|
timeout,
|
||||||
|
ffmpegExecuteResponseHandler
|
||||||
|
)
|
||||||
|
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
|
||||||
|
task
|
||||||
|
} else {
|
||||||
|
throw IllegalArgumentException("shell command cannot be empty")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun execute(
|
||||||
|
cmd: Array<String>,
|
||||||
|
ffmpegExecuteResponseHandler: FFcommandExecuteResponseHandler
|
||||||
|
): FFtask {
|
||||||
|
return execute(emptyMap(), cmd, ffmpegExecuteResponseHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isCommandRunning(task: FFtask): Boolean {
|
||||||
|
return !task.isProcessCompleted
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun killRunningProcesses(task: FFtask): Boolean {
|
||||||
|
return task.killRunningProcess()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setTimeout(timeout: Long) {
|
||||||
|
if (timeout >= MINIMUM_TIMEOUT) {
|
||||||
|
this.timeout = timeout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val MINIMUM_TIMEOUT = (10 * 1000).toLong()
|
||||||
|
private var instance: FFmpeg? = null
|
||||||
|
|
||||||
|
fun getInstance(context: Context): FFmpeg {
|
||||||
|
return instance ?: FFmpeg { context }.also {
|
||||||
|
instance = it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,22 +1,21 @@
|
|||||||
package nl.bravobit.ffmpeg;
|
package nl.bravobit.ffmpeg
|
||||||
|
|
||||||
public interface FFtask {
|
|
||||||
|
|
||||||
|
interface FFtask {
|
||||||
/**
|
/**
|
||||||
* Sends 'q' to the ff binary running process asynchronously
|
* Sends 'q' to the ff binary running process asynchronously
|
||||||
*/
|
*/
|
||||||
void sendQuitSignal();
|
fun sendQuitSignal()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if process is completed
|
* Checks if process is completed
|
||||||
* @return <code>true</code> if a process is running
|
* @return `true` if a process is running
|
||||||
*/
|
*/
|
||||||
boolean isProcessCompleted();
|
val isProcessCompleted: Boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Kill given running process
|
* Kill given running process
|
||||||
*
|
*
|
||||||
* @return true if process is killed successfully
|
* @return true if process is killed successfully
|
||||||
*/
|
*/
|
||||||
boolean killRunningProcess();
|
fun killRunningProcess(): Boolean
|
||||||
}
|
}
|
@ -1,20 +0,0 @@
|
|||||||
package nl.bravobit.ffmpeg;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
class FileUtils {
|
|
||||||
private static final String FFMPEG_FILE_NAME = "lib..ffmpeg..so";
|
|
||||||
private static final String FFPROBE_FILE_NAME = "lib..ffprobe..so";
|
|
||||||
|
|
||||||
static File getFFmpeg(Context context) {
|
|
||||||
File folder = new File(context.getApplicationInfo().nativeLibraryDir);
|
|
||||||
return new File(folder, FFMPEG_FILE_NAME);
|
|
||||||
}
|
|
||||||
|
|
||||||
static File getFFprobe(Context context) {
|
|
||||||
File folder = new File(context.getApplicationInfo().nativeLibraryDir);
|
|
||||||
return new File(folder, FFPROBE_FILE_NAME);
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,22 @@
|
|||||||
|
package nl.bravobit.ffmpeg
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
internal object FileUtils {
|
||||||
|
|
||||||
|
private const val FFMPEG_FILE_NAME = "lib..ffmpeg..so"
|
||||||
|
private const val FFPROBE_FILE_NAME = "lib..ffprobe..so"
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun getFFmpeg(context: Context): File {
|
||||||
|
val folder = File(context.applicationInfo.nativeLibraryDir)
|
||||||
|
return File(folder, FFMPEG_FILE_NAME)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun getFFprobe(context: Context): File {
|
||||||
|
val folder = File(context.applicationInfo.nativeLibraryDir)
|
||||||
|
return File(folder, FFPROBE_FILE_NAME)
|
||||||
|
}
|
||||||
|
}
|
@ -1,58 +0,0 @@
|
|||||||
package nl.bravobit.ffmpeg;
|
|
||||||
|
|
||||||
class Log {
|
|
||||||
|
|
||||||
private static String TAG = FFmpeg.class.getSimpleName();
|
|
||||||
private static boolean DEBUG = false;
|
|
||||||
|
|
||||||
public static void setDebug(boolean debug) {
|
|
||||||
Log.DEBUG = debug;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void setTag(String tag) {
|
|
||||||
Log.TAG = tag;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void d(Object obj) {
|
|
||||||
if (DEBUG) {
|
|
||||||
android.util.Log.d(TAG, obj != null ? obj.toString() : "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void e(Object obj) {
|
|
||||||
if (DEBUG) {
|
|
||||||
android.util.Log.e(TAG, obj != null ? obj.toString() : "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void w(Object obj) {
|
|
||||||
if (DEBUG) {
|
|
||||||
android.util.Log.w(TAG, obj != null ? obj.toString() : "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void i(Object obj) {
|
|
||||||
if (DEBUG) {
|
|
||||||
android.util.Log.i(TAG, obj != null ? obj.toString() : "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void v(Object obj) {
|
|
||||||
if (DEBUG) {
|
|
||||||
android.util.Log.v(TAG, obj != null ? obj.toString() : "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void e(Object obj, Throwable throwable) {
|
|
||||||
if (DEBUG) {
|
|
||||||
android.util.Log.e(TAG, obj != null ? obj.toString() : "", throwable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void e(Throwable throwable) {
|
|
||||||
if (DEBUG) {
|
|
||||||
android.util.Log.e(TAG, "", throwable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,67 @@
|
|||||||
|
package nl.bravobit.ffmpeg
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
|
||||||
|
internal object Log {
|
||||||
|
|
||||||
|
private var TAG = FFmpeg::class.java.simpleName
|
||||||
|
private var DEBUG = false
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun setDebug(debug: Boolean) {
|
||||||
|
DEBUG = debug
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setTag(tag: String) {
|
||||||
|
TAG = tag
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun d(obj: Any?) {
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.d(TAG, obj?.toString() ?: "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun e(obj: Any?) {
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.e(TAG, obj?.toString() ?: "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun w(obj: Any?) {
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.w(TAG, obj?.toString() ?: "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun i(obj: Any?) {
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.i(TAG, obj?.toString() ?: "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun v(obj: Any?) {
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.v(TAG, obj?.toString() ?: "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun e(obj: Any?, throwable: Throwable?) {
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.e(TAG, obj?.toString() ?: "", throwable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun e(throwable: Throwable?) {
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.e(TAG, "", throwable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,15 +0,0 @@
|
|||||||
package nl.bravobit.ffmpeg;
|
|
||||||
|
|
||||||
public interface ResponseHandler {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* on Start
|
|
||||||
*/
|
|
||||||
void onStart();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* on Finish
|
|
||||||
*/
|
|
||||||
void onFinish();
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,13 @@
|
|||||||
|
package nl.bravobit.ffmpeg
|
||||||
|
|
||||||
|
interface ResponseHandler {
|
||||||
|
/**
|
||||||
|
* on Start
|
||||||
|
*/
|
||||||
|
fun onStart()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* on Finish
|
||||||
|
*/
|
||||||
|
fun onFinish()
|
||||||
|
}
|
@ -1,22 +0,0 @@
|
|||||||
package nl.bravobit.ffmpeg;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
class ShellCommand {
|
|
||||||
|
|
||||||
Process run(String[] commandString, Map<String, String> environment) {
|
|
||||||
Process process = null;
|
|
||||||
try {
|
|
||||||
ProcessBuilder processBuilder = new ProcessBuilder(commandString);
|
|
||||||
if (environment != null) {
|
|
||||||
processBuilder.environment().putAll(environment);
|
|
||||||
}
|
|
||||||
process = processBuilder.start();
|
|
||||||
} catch (Throwable t) {
|
|
||||||
Log.e("Exception while trying to run: " + Arrays.toString(commandString), t);
|
|
||||||
}
|
|
||||||
return process;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,17 @@
|
|||||||
|
package nl.bravobit.ffmpeg
|
||||||
|
|
||||||
|
internal class ShellCommand {
|
||||||
|
fun run(commandString: Array<String?>, environment: Map<String?, String?>?): Process? {
|
||||||
|
var process: Process? = null
|
||||||
|
try {
|
||||||
|
val processBuilder = ProcessBuilder(*commandString)
|
||||||
|
if (environment != null) {
|
||||||
|
processBuilder.environment().putAll(environment)
|
||||||
|
}
|
||||||
|
process = processBuilder.start()
|
||||||
|
} catch (t: Throwable) {
|
||||||
|
Log.e("Exception while trying to run: " + commandString.contentToString(), t)
|
||||||
|
}
|
||||||
|
return process
|
||||||
|
}
|
||||||
|
}
|
@ -1,105 +0,0 @@
|
|||||||
package nl.bravobit.ffmpeg;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.pm.ApplicationInfo;
|
|
||||||
import android.os.AsyncTask;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
|
|
||||||
class Util {
|
|
||||||
|
|
||||||
static boolean isDebug(Context context) {
|
|
||||||
return (context.getApplicationContext().getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static String convertInputStreamToString(InputStream inputStream) {
|
|
||||||
try {
|
|
||||||
BufferedReader r = new BufferedReader(new InputStreamReader(inputStream));
|
|
||||||
String str;
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
while ((str = r.readLine()) != null) {
|
|
||||||
sb.append(str);
|
|
||||||
}
|
|
||||||
return sb.toString();
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.e("error converting input stream to string", e);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void destroyProcess(Process process) {
|
|
||||||
if (process != null) {
|
|
||||||
try {
|
|
||||||
process.destroy();
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e("progress destroy error", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static boolean killAsync(AsyncTask asyncTask) {
|
|
||||||
return asyncTask != null && !asyncTask.isCancelled() && asyncTask.cancel(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
static boolean isProcessCompleted(Process process) {
|
|
||||||
try {
|
|
||||||
if (process == null) return true;
|
|
||||||
process.exitValue();
|
|
||||||
return true;
|
|
||||||
} catch (IllegalThreadStateException e) {
|
|
||||||
// do nothing
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface ObservePredicate {
|
|
||||||
Boolean isReadyToProceed();
|
|
||||||
}
|
|
||||||
|
|
||||||
static FFbinaryObserver observeOnce(final ObservePredicate predicate, final Runnable run, final int timeout) {
|
|
||||||
final android.os.Handler observer = new android.os.Handler();
|
|
||||||
|
|
||||||
|
|
||||||
final FFbinaryObserver observeAction = new FFbinaryObserver() {
|
|
||||||
private boolean canceled = false;
|
|
||||||
private int timeElapsed = 0;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if (timeElapsed + 40 > timeout) cancel();
|
|
||||||
timeElapsed += 40;
|
|
||||||
|
|
||||||
if (canceled) return;
|
|
||||||
|
|
||||||
boolean readyToProceed = false;
|
|
||||||
try {
|
|
||||||
readyToProceed = predicate.isReadyToProceed();
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.v("Observing " + e.getMessage());
|
|
||||||
observer.postDelayed(this, 40);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (readyToProceed) {
|
|
||||||
Log.v("Observed");
|
|
||||||
run.run();
|
|
||||||
} else {
|
|
||||||
Log.v("Observing");
|
|
||||||
observer.postDelayed(this, 40);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void cancel() {
|
|
||||||
canceled = true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
observer.post(observeAction);
|
|
||||||
|
|
||||||
return observeAction;
|
|
||||||
}
|
|
||||||
}
|
|
100
ffmpeg/android-ffmpeg/src/main/java/nl/bravobit/ffmpeg/Util.kt
Normal file
100
ffmpeg/android-ffmpeg/src/main/java/nl/bravobit/ffmpeg/Util.kt
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
package nl.bravobit.ffmpeg
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.pm.ApplicationInfo
|
||||||
|
import android.os.AsyncTask
|
||||||
|
import android.os.Handler
|
||||||
|
import java.io.BufferedReader
|
||||||
|
import java.io.IOException
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.io.InputStreamReader
|
||||||
|
|
||||||
|
internal object Util {
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun isDebug(context: Context): Boolean {
|
||||||
|
return context.applicationContext.applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun convertInputStreamToString(inputStream: InputStream?): String? {
|
||||||
|
try {
|
||||||
|
val r = BufferedReader(InputStreamReader(inputStream))
|
||||||
|
var str: String?
|
||||||
|
val sb = StringBuilder()
|
||||||
|
while (r.readLine().also { str = it } != null) {
|
||||||
|
sb.append(str)
|
||||||
|
}
|
||||||
|
return sb.toString()
|
||||||
|
} catch (e: IOException) {
|
||||||
|
Log.e("error converting input stream to string", e)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun destroyProcess(process: Process?) {
|
||||||
|
if (process != null) {
|
||||||
|
try {
|
||||||
|
process.destroy()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("progress destroy error", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun killAsync(asyncTask: AsyncTask<*, *, *>?): Boolean {
|
||||||
|
return asyncTask != null && !asyncTask.isCancelled && asyncTask.cancel(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun isProcessCompleted(process: Process?): Boolean {
|
||||||
|
try {
|
||||||
|
if (process == null) return true
|
||||||
|
process.exitValue()
|
||||||
|
return true
|
||||||
|
} catch (e: IllegalThreadStateException) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun observeOnce(predicate: ObservePredicate, run: Runnable, timeout: Int): FFbinaryObserver {
|
||||||
|
val observer = Handler()
|
||||||
|
val observeAction: FFbinaryObserver = object : FFbinaryObserver {
|
||||||
|
private var canceled = false
|
||||||
|
private var timeElapsed = 0
|
||||||
|
override fun run() {
|
||||||
|
if (timeElapsed + 40 > timeout) cancel()
|
||||||
|
timeElapsed += 40
|
||||||
|
if (canceled) return
|
||||||
|
var readyToProceed = false
|
||||||
|
readyToProceed = try {
|
||||||
|
predicate.isReadyToProceed
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.v("Observing " + e.message)
|
||||||
|
observer.postDelayed(this, 40)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (readyToProceed) {
|
||||||
|
Log.v("Observed")
|
||||||
|
run.run()
|
||||||
|
} else {
|
||||||
|
Log.v("Observing")
|
||||||
|
observer.postDelayed(this, 40)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun cancel() {
|
||||||
|
canceled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
observer.post(observeAction)
|
||||||
|
return observeAction
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ObservePredicate {
|
||||||
|
val isReadyToProceed: Boolean
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user