mirror of
https://github.com/Shabinder/SpotiFlyer.git
synced 2024-11-22 01:04:31 +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
|
||||
with:
|
||||
draft: true
|
||||
tag: "SpotiFlyer-v3.3.0"
|
||||
tag: "v3.3.0"
|
||||
artifacts: "desktop/build/compose/jars/*.jar,desktop/build/compose/binaries/main/*/*"
|
||||
token: ${{ secrets.GH_TOKEN }}
|
||||
commit: main
|
||||
@ -65,7 +65,7 @@ jobs:
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
draft: true
|
||||
tag: "SpotiFlyer-v3.3.0"
|
||||
tag: "v3.3.0"
|
||||
artifacts: "desktop/build/compose/jars/*.jar,desktop/build/compose/binaries/main/*/*"
|
||||
token: ${{ secrets.GH_TOKEN }}
|
||||
commit: main
|
||||
@ -98,7 +98,7 @@ jobs:
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
draft: true
|
||||
tag: "SpotiFlyer-v3.3.0"
|
||||
tag: "v3.3.0"
|
||||
artifacts: "desktop/build/compose/jars/*.jar,desktop/build/compose/binaries/main/*/*"
|
||||
token: ${{ secrets.GH_TOKEN }}
|
||||
commit: main
|
||||
|
@ -36,7 +36,7 @@
|
||||
tools:ignore="ScopedStorage" />
|
||||
<uses-permission android:name="android.permission.READ_STORAGE_PERMISSION" />
|
||||
<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.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
||||
@ -58,16 +58,22 @@
|
||||
android:extractNativeLibs="true"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
tools:targetApi="q">
|
||||
<activity android:name=".MainActivity"
|
||||
android:launchMode="singleTask"
|
||||
android:label="SpotiFlyer"
|
||||
<activity
|
||||
android:name=".ui.SplashScreenActivity"
|
||||
android:theme="@style/SplashTheme"
|
||||
android:hardwareAccelerated="true"
|
||||
android:theme="@style/Theme.SpotiFlyer"
|
||||
>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name=".MainActivity"
|
||||
android:launchMode="singleTask"
|
||||
android:hardwareAccelerated="true"
|
||||
android:theme="@style/Theme.SpotiFlyer"
|
||||
>
|
||||
<intent-filter>
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<action android:name="android.intent.action.SEND" />
|
||||
@ -76,13 +82,15 @@
|
||||
</activity>
|
||||
|
||||
<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>
|
||||
<action android:name="org.openudid.GETUDID" />
|
||||
</intent-filter>
|
||||
</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>
|
||||
<action android:name="com.android.vending.INSTALL_REFERRER" />
|
||||
</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.file_manager.FileManager
|
||||
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.models.*
|
||||
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 java.io.File
|
||||
|
||||
@ExperimentalAnimationApi
|
||||
@OptIn(ExperimentalAnimationApi::class)
|
||||
class MainActivity : ComponentActivity() {
|
||||
|
||||
private val fetcher: FetchPlatformQueryResult by inject()
|
||||
private val fileManager: FileManager by inject()
|
||||
private val preferenceManager: PreferenceManager by inject()
|
||||
private val analyticsManager: AnalyticsManager by inject { parametersOf(this) }
|
||||
private val applicationInit: ApplicationInit by inject()
|
||||
private val callBacks: SpotiFlyerRootCallBacks get() = this.rootComponent.callBacks
|
||||
private val trackStatusFlow = MutableSharedFlow<HashMap<String, DownloadStatus>>(1)
|
||||
private var permissionGranted = mutableStateOf(true)
|
||||
@ -263,6 +265,7 @@ class MainActivity : ComponentActivity() {
|
||||
override val analyticsManager: AnalyticsManager = this@MainActivity.analyticsManager
|
||||
override val downloadProgressFlow: MutableSharedFlow<HashMap<String, DownloadStatus>> =
|
||||
trackStatusFlow
|
||||
override val appInit: ApplicationInit = applicationInit
|
||||
override val actions = object : Actions {
|
||||
|
||||
override val platformActions = object : PlatformActions {
|
||||
@ -431,7 +434,7 @@ class MainActivity : ComponentActivity() {
|
||||
while (!this@MainActivity::rootComponent.isInitialized) {
|
||||
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">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
<foreground android:drawable="@drawable/ic_spotiflyer_logo"/>
|
||||
</adaptive-icon>
|
@ -17,5 +17,5 @@
|
||||
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
<foreground android:drawable="@drawable/ic_spotiflyer_logo"/>
|
||||
</adaptive-icon>
|
@ -22,8 +22,12 @@
|
||||
<item name="colorAccent">@color/colorAccent</item>
|
||||
<item name="android:textColor">@color/white</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:navigationBarColor">@android:color/transparent</item>
|
||||
</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>
|
@ -35,10 +35,16 @@ allprojects {
|
||||
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")
|
||||
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 {
|
||||
project.extensions.findByType<org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension>()?.let { kmpExt ->
|
||||
kmpExt.sourceSets.run {
|
||||
|
@ -19,6 +19,7 @@
|
||||
plugins {
|
||||
id("com.android.library")
|
||||
id("ktlint-setup")
|
||||
id("compiler-args")
|
||||
}
|
||||
|
||||
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("kotlin-parcelize")
|
||||
id("ktlint-setup")
|
||||
id("compiler-args")
|
||||
}
|
||||
|
||||
kotlin {
|
||||
|
@ -17,6 +17,7 @@
|
||||
plugins {
|
||||
id("com.android.library")
|
||||
id("kotlin-multiplatform")
|
||||
id("compiler-args")
|
||||
}
|
||||
|
||||
kotlin {
|
||||
|
@ -20,6 +20,7 @@ plugins {
|
||||
id("org.jetbrains.compose")
|
||||
id("ktlint-setup")
|
||||
id("kotlin-parcelize")
|
||||
id("compiler-args")
|
||||
}
|
||||
|
||||
kotlin {
|
||||
|
@ -23,9 +23,6 @@ plugins {
|
||||
|
||||
kotlin {
|
||||
sourceSets {
|
||||
all {
|
||||
languageSettings.useExperimentalAnnotation("androidx.compose.animation")
|
||||
}
|
||||
commonMain {
|
||||
dependencies {
|
||||
implementation(compose.material)
|
||||
|
@ -26,7 +26,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
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.uikit.Dialog
|
||||
import com.shabinder.common.uikit.OpenCollectiveLogo
|
||||
@ -87,7 +87,7 @@ fun DonationDialog(
|
||||
modifier = Modifier.fillMaxWidth().clickable(
|
||||
onClick = {
|
||||
onDismiss()
|
||||
methods.value.openPlatform("", "https://opencollective.com/spotiflyer/donate")
|
||||
Actions.instance.openPlatform("", "https://opencollective.com/spotiflyer/donate")
|
||||
}
|
||||
)
|
||||
.padding(vertical = 6.dp)
|
||||
@ -110,7 +110,7 @@ fun DonationDialog(
|
||||
modifier = Modifier.fillMaxWidth().clickable(
|
||||
onClick = {
|
||||
onDismiss()
|
||||
methods.value.openPlatform("", "https://www.paypal.com/paypalme/shabinder")
|
||||
Actions.instance.openPlatform("", "https://www.paypal.com/paypalme/shabinder")
|
||||
}
|
||||
)
|
||||
.padding(vertical = 6.dp)
|
||||
@ -133,7 +133,7 @@ fun DonationDialog(
|
||||
.clickable(
|
||||
onClick = {
|
||||
onDismiss()
|
||||
methods.value.giveDonation()
|
||||
Actions.instance.giveDonation()
|
||||
}
|
||||
),
|
||||
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.style.TextAlign
|
||||
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.uikit.Dialog
|
||||
import com.shabinder.common.uikit.configurations.SpotiFlyerTypography
|
||||
@ -67,7 +67,7 @@ fun ErrorInfoDialog(error: Throwable): ErrorInfoDialogCallBacks {
|
||||
TextButton(onClick = onDismissDialog, colors = ButtonDefaults.buttonColors()) {
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ import com.shabinder.common.core_components.picture.Picture
|
||||
import com.shabinder.common.list.SpotiFlyerList
|
||||
import com.shabinder.common.models.DownloadStatus
|
||||
import com.shabinder.common.models.TrackDetails
|
||||
import com.shabinder.common.models.methods
|
||||
import com.shabinder.common.models.Actions
|
||||
import com.shabinder.common.translations.Strings
|
||||
import com.shabinder.common.uikit.DownloadAllImage
|
||||
import com.shabinder.common.uikit.DownloadImageArrow
|
||||
@ -84,7 +84,7 @@ fun SpotiFlyerListContent(
|
||||
LaunchedEffect(model.errorOccurred) {
|
||||
/*Handle if Any Exception Occurred*/
|
||||
model.errorOccurred?.let {
|
||||
methods.value.showPopUpMessage(it.message ?: Strings.errorOccurred())
|
||||
Actions.instance.showPopUpMessage(it.message ?: Strings.errorOccurred())
|
||||
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.HomeCategory
|
||||
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.uikit.GaanaLogo
|
||||
import com.shabinder.common.uikit.GithubLogo
|
||||
@ -229,7 +229,7 @@ fun SearchPanel(
|
||||
OutlinedButton(
|
||||
modifier = Modifier.padding(12.dp).wrapContentWidth(),
|
||||
onClick = {
|
||||
if (link.isBlank()) methods.value.showPopUpMessage(Strings.enterALink())
|
||||
if (link.isBlank()) Actions.instance.showPopUpMessage(Strings.enterALink())
|
||||
else {
|
||||
// TODO if(!isOnline(ctx)) showPopUpMessage("Check Your Internet Connection") else
|
||||
onSearch(link)
|
||||
@ -279,7 +279,7 @@ fun AboutColumn(
|
||||
"${Strings.open()} Spotify",
|
||||
tint = Color.Unspecified,
|
||||
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))
|
||||
@ -288,7 +288,7 @@ fun AboutColumn(
|
||||
"${Strings.open()} Gaana",
|
||||
tint = Color.Unspecified,
|
||||
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))
|
||||
@ -297,7 +297,7 @@ fun AboutColumn(
|
||||
"${Strings.open()} Jio Saavn",
|
||||
tint = Color.Unspecified,
|
||||
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))
|
||||
@ -306,7 +306,7 @@ fun AboutColumn(
|
||||
"${Strings.open()} Youtube",
|
||||
tint = Color.Unspecified,
|
||||
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))
|
||||
@ -315,7 +315,7 @@ fun AboutColumn(
|
||||
"${Strings.open()} Youtube Music",
|
||||
tint = Color.Unspecified,
|
||||
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(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
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)
|
||||
) {
|
||||
@ -355,7 +355,7 @@ fun AboutColumn(
|
||||
}
|
||||
Row(
|
||||
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
|
||||
) {
|
||||
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)
|
||||
.clickable(
|
||||
onClick = {
|
||||
methods.value.shareApp()
|
||||
Actions.instance.shareApp()
|
||||
}
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
|
@ -29,14 +29,7 @@ import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.core.updateTransition
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
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.foundation.layout.*
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.IconButton
|
||||
import androidx.compose.material.MaterialTheme
|
||||
@ -52,6 +45,7 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
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 com.arkivanov.decompose.extensions.compose.jetbrains.Children
|
||||
@ -179,7 +173,8 @@ fun AppBar(
|
||||
Image(
|
||||
SpotiFlyerLogo(),
|
||||
Strings.spotiflyerLogo(),
|
||||
Modifier.size(32.dp),
|
||||
Modifier.requiredHeight(66.dp).requiredWidth(42.dp),
|
||||
contentScale = ContentScale.FillHeight
|
||||
)
|
||||
Spacer(Modifier.padding(horizontal = 4.dp))
|
||||
Text(
|
||||
|
@ -17,13 +17,7 @@
|
||||
package com.shabinder.common.uikit.screens.splash
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
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.foundation.layout.*
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
@ -56,7 +50,7 @@ fun Splash(modifier: Modifier = Modifier, onTimeout: () -> Unit) {
|
||||
delay(SplashWaitTime)
|
||||
currentOnTimeout()
|
||||
}
|
||||
Image(SpotiFlyerLogo(), Strings.spotiflyerLogo())
|
||||
Image(SpotiFlyerLogo(), Strings.spotiflyerLogo(),modifier = Modifier.fillMaxSize())
|
||||
MadeInIndia(Modifier.align(Alignment.BottomCenter))
|
||||
}
|
||||
}
|
||||
|
@ -10,9 +10,17 @@ import org.koin.dsl.module
|
||||
|
||||
internal class AndroidAnalyticsManager(private val mainActivity: Activity) : AnalyticsManager {
|
||||
|
||||
companion object {
|
||||
private var isInitialised = false
|
||||
}
|
||||
|
||||
init {
|
||||
// Don't Init If Instantiated on Diff Activities
|
||||
if (!isInitialised) {
|
||||
isInitialised = true
|
||||
init()
|
||||
}
|
||||
}
|
||||
|
||||
override fun init() {
|
||||
Countly.sharedInstance().init(
|
||||
@ -45,7 +53,8 @@ internal class AndroidAnalyticsManager(private val mainActivity: Activity) : Ana
|
||||
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() {
|
||||
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.dispatcherIO
|
||||
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.methods
|
||||
import com.shabinder.common.models.Actions
|
||||
import com.shabinder.database.Database
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
@ -70,7 +69,7 @@ class AndroidFileManager(
|
||||
|
||||
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
|
||||
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 =
|
||||
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.failure
|
||||
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 kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
@ -165,7 +165,7 @@ class DesktopFileManager(
|
||||
}
|
||||
SuspendableEvent.success(trackDetails.outputFilePath)
|
||||
} 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()
|
||||
logger.e { "${songFile.absolutePath} could not be created" }
|
||||
SuspendableEvent.error(e)
|
||||
|
@ -17,7 +17,7 @@ actual interface PlatformActions {
|
||||
fun sendTracksToService(array: List<TrackDetails>)
|
||||
}
|
||||
|
||||
actual val StubPlatformActions = object : PlatformActions {
|
||||
internal actual val StubPlatformActions = object : PlatformActions {
|
||||
override val imageCacheDir = ""
|
||||
|
||||
override val sharedPreferences: SharedPreferences? = null
|
||||
|
@ -1,11 +1,7 @@
|
||||
package com.shabinder.common.models
|
||||
|
||||
import co.touchlab.stately.freeze
|
||||
|
||||
/*
|
||||
* Holder to call platform actions from anywhere
|
||||
* */
|
||||
val methods: NativeAtomicReference<Actions> = NativeAtomicReference(stubActions().freeze())
|
||||
import kotlin.jvm.JvmStatic
|
||||
|
||||
/*
|
||||
* Interface Having All Platform Dependent Functions
|
||||
@ -42,6 +38,20 @@ interface Actions {
|
||||
// Open / Redirect to another Platform
|
||||
fun openPlatform(packageID: String, platformLink: String)
|
||||
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 {
|
||||
|
@ -2,4 +2,4 @@ package com.shabinder.common.models
|
||||
|
||||
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 val StubPlatformActions = object : PlatformActions {}
|
||||
internal actual val StubPlatformActions = object : PlatformActions {}
|
||||
|
@ -2,4 +2,4 @@ package com.shabinder.common.models
|
||||
|
||||
|
||||
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/>.
|
||||
-->
|
||||
|
||||
<vector android:height="150dp" android:viewportHeight="512"
|
||||
android:viewportWidth="512" android:width="150dp"
|
||||
xmlns:aapt="http://schemas.android.com/aapt" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:pathData="M256,256m-256,0a256,256 0,1 1,512 0a256,256 0,1 1,-512 0">
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:gravity="center"
|
||||
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:endX="437.019" android:endY="74.981"
|
||||
android:startX="74.981" android:startY="437.019" android:type="linear">
|
||||
<item android:color="#FF736BFD" android:offset="0"/>
|
||||
<item android:color="#FFF54187" android:offset="1"/>
|
||||
<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">
|
||||
<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:endX="384.197" android:endY="490.009"
|
||||
android:startX="487.832" android:startY="386.374" android:type="linear">
|
||||
<item android:color="#FF736BFD" android:offset="0"/>
|
||||
<item android:color="#FFF54187" android:offset="1"/>
|
||||
<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"
|
||||
<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:strokeColor="#000" android:strokeWidth=".75"/>
|
||||
<path android:fillColor="#00000000"
|
||||
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:strokeColor="#000" android:strokeWidth="6"/>
|
||||
android:strokeWidth="6"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#000000"/>
|
||||
</group>
|
||||
</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(
|
||||
providersModule(enableNetworkLogs),
|
||||
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.State
|
||||
import com.shabinder.common.list.store.SpotiFlyerListStore.Intent
|
||||
import com.shabinder.common.models.DownloadStatus
|
||||
import com.shabinder.common.models.PlatformQueryResult
|
||||
import com.shabinder.common.models.TrackDetails
|
||||
import com.shabinder.common.models.methods
|
||||
import com.shabinder.common.models.*
|
||||
import com.shabinder.common.providers.downloadTracks
|
||||
import kotlinx.coroutines.flow.collect
|
||||
|
||||
@ -112,14 +109,14 @@ internal class SpotiFlyerListStoreProvider(dependencies: SpotiFlyerList.Dependen
|
||||
)
|
||||
|
||||
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)
|
||||
}
|
||||
is Intent.StartDownload -> {
|
||||
dispatch(Result.UpdateTrackItem(intent.track.copy(downloaded = DownloadStatus.Queued)))
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ import com.shabinder.common.main.SpotiFlyerMain.*
|
||||
import com.shabinder.common.main.store.SpotiFlyerMainStore.Intent
|
||||
import com.shabinder.common.main.store.SpotiFlyerMainStoreProvider
|
||||
import com.shabinder.common.main.store.getStore
|
||||
import com.shabinder.common.models.methods
|
||||
import com.shabinder.common.models.Actions
|
||||
|
||||
internal class SpotiFlyerMainImpl(
|
||||
componentContext: ComponentContext,
|
||||
@ -57,8 +57,8 @@ internal class SpotiFlyerMainImpl(
|
||||
override val analytics = mainAnalytics
|
||||
|
||||
override fun onLinkSearch(link: String) {
|
||||
if (methods.value.isInternetAvailable) mainOutput.callback(Output.Search(link = link))
|
||||
else methods.value.showPopUpMessage("Check Network Connection Please")
|
||||
if (Actions.instance.isInternetAvailable) mainOutput.callback(Output.Search(link = link))
|
||||
else Actions.instance.showPopUpMessage("Check Network Connection Please")
|
||||
}
|
||||
|
||||
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.store.SpotiFlyerMainStore.Intent
|
||||
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.mapToList
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@ -75,9 +75,9 @@ internal class SpotiFlyerMainStoreProvider(dependencies: SpotiFlyerMain.Dependen
|
||||
|
||||
override suspend fun executeIntent(intent: Intent, getState: () -> State) {
|
||||
when (intent) {
|
||||
is Intent.OpenPlatform -> methods.value.openPlatform(intent.platformID, intent.platformLink)
|
||||
is Intent.GiveDonation -> methods.value.giveDonation()
|
||||
is Intent.ShareApp -> methods.value.shareApp()
|
||||
is Intent.OpenPlatform -> Actions.instance.openPlatform(intent.platformID, intent.platformLink)
|
||||
is Intent.GiveDonation -> Actions.instance.giveDonation()
|
||||
is Intent.ShareApp -> Actions.instance.shareApp()
|
||||
is Intent.SetLink -> dispatch(Result.LinkChanged(link = intent.link))
|
||||
is Intent.SelectCategory -> dispatch(Result.CategoryChanged(intent.category))
|
||||
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.extensions.coroutines.SuspendExecutor
|
||||
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.State
|
||||
import com.shabinder.common.preference.store.SpotiFlyerPreferenceStore.Intent
|
||||
@ -56,9 +56,9 @@ internal class SpotiFlyerPreferenceStoreProvider(
|
||||
|
||||
override suspend fun executeIntent(intent: Intent, getState: () -> State) {
|
||||
when (intent) {
|
||||
is Intent.OpenPlatform -> methods.value.openPlatform(intent.platformID, intent.platformLink)
|
||||
is Intent.GiveDonation -> methods.value.giveDonation()
|
||||
is Intent.ShareApp -> methods.value.shareApp()
|
||||
is Intent.OpenPlatform -> Actions.instance.openPlatform(intent.platformID, intent.platformLink)
|
||||
is Intent.GiveDonation -> Actions.instance.giveDonation()
|
||||
is Intent.ShareApp -> Actions.instance.shareApp()
|
||||
is Intent.ToggleAnalytics -> {
|
||||
dispatch(Result.AnalyticsToggled(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.models.TrackDetails
|
||||
import com.shabinder.common.models.methods
|
||||
import com.shabinder.common.models.Actions
|
||||
|
||||
|
||||
actual suspend fun downloadTracks(
|
||||
@ -27,6 +27,6 @@ actual suspend fun downloadTracks(
|
||||
fileManager: FileManager
|
||||
) {
|
||||
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.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.utils.globalJson
|
||||
import io.ktor.client.*
|
||||
@ -32,9 +32,12 @@ import io.ktor.http.*
|
||||
import kotlin.native.concurrent.SharedImmutable
|
||||
|
||||
suspend fun authenticateSpotify(): SuspendableEvent<TokenData, Throwable> = SuspendableEvent {
|
||||
if (methods.value.isInternetAvailable) {
|
||||
if (Actions.instance.isInternetAvailable) {
|
||||
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()
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import com.shabinder.common.models.AllPlatforms
|
||||
import com.shabinder.common.models.DownloadResult
|
||||
import com.shabinder.common.models.DownloadStatus
|
||||
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.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.collect
|
||||
@ -48,7 +48,7 @@ actual suspend fun downloadTracks(
|
||||
}
|
||||
}
|
||||
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(
|
||||
0
|
||||
) { 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.file_manager.FileManager
|
||||
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.main.SpotiFlyerMain
|
||||
import com.shabinder.common.models.Actions
|
||||
@ -51,6 +52,7 @@ interface SpotiFlyerRoot {
|
||||
}
|
||||
|
||||
interface Dependencies {
|
||||
val appInit: ApplicationInit
|
||||
val storeFactory: StoreFactory
|
||||
val database: Database?
|
||||
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.list.SpotiFlyerList
|
||||
import com.shabinder.common.main.SpotiFlyerMain
|
||||
import com.shabinder.common.models.Actions
|
||||
import com.shabinder.common.models.Consumer
|
||||
import com.shabinder.common.models.dispatcherIO
|
||||
import com.shabinder.common.models.methods
|
||||
import com.shabinder.common.models.Actions
|
||||
import com.shabinder.common.preference.SpotiFlyerPreference
|
||||
import com.shabinder.common.root.SpotiFlyerRoot
|
||||
import com.shabinder.common.root.SpotiFlyerRoot.Child
|
||||
import com.shabinder.common.root.SpotiFlyerRoot.Dependencies
|
||||
import com.shabinder.common.root.callbacks.SpotiFlyerRootCallBacks
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
internal class SpotiFlyerRootImpl(
|
||||
componentContext: ComponentContext,
|
||||
@ -47,11 +42,10 @@ internal class SpotiFlyerRootImpl(
|
||||
Actions by dependencies.actions {
|
||||
|
||||
init {
|
||||
AnalyticsEvent.AppLaunch.track(analyticsManager)
|
||||
instanceKeeper.ensureNeverFrozen()
|
||||
methods.value = dependencies.actions.freeze()
|
||||
|
||||
/*Init App Launch & Authenticate Spotify Client*/
|
||||
initAppLaunchAndAuthenticateSpotify(dependencies.fetchQuery::authenticateSpotifyClient)
|
||||
Actions.instance = dependencies.actions.freeze()
|
||||
appInit.init()
|
||||
}
|
||||
|
||||
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 {
|
||||
@Parcelize
|
||||
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.preference_manager.PreferenceManager
|
||||
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.TrackDetails
|
||||
import com.shabinder.common.models.methods
|
||||
import com.shabinder.common.models.Actions
|
||||
import com.shabinder.common.providers.FetchPlatformQueryResult
|
||||
import com.shabinder.common.root.SpotiFlyerRoot
|
||||
import com.shabinder.common.translations.Strings
|
||||
@ -98,7 +97,7 @@ fun main() {
|
||||
try {
|
||||
FFmpeg.atPath().addArgument("-version").execute()
|
||||
} 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(
|
||||
componentContext = componentContext,
|
||||
dependencies = object : SpotiFlyerRoot.Dependencies {
|
||||
override val appInit: ApplicationInit = koin.get()
|
||||
override val storeFactory = DefaultStoreFactory
|
||||
override val fetchQuery: FetchPlatformQueryResult = 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;
|
||||
|
||||
public interface FFtask {
|
||||
package nl.bravobit.ffmpeg
|
||||
|
||||
interface FFtask {
|
||||
/**
|
||||
* Sends 'q' to the ff binary running process asynchronously
|
||||
*/
|
||||
void sendQuitSignal();
|
||||
fun sendQuitSignal()
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* @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