Actions and Code Refactoring

This commit is contained in:
shabinder 2021-09-25 01:03:46 +05:30
parent 6fb1815252
commit e4c22c5d0c
58 changed files with 602 additions and 539 deletions

View File

@ -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

View File

@ -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>

View File

@ -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)
} }
} }
} }

View File

@ -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
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

View File

@ -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>

View 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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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 {

View File

@ -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 {

View 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")
}
}
}

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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)

View File

@ -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

View File

@ -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())
} }
} }

View File

@ -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()
} }
} }

View File

@ -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

View File

@ -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(

View File

@ -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))
} }
} }

View File

@ -10,9 +10,17 @@ 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 {
// Don't Init If Instantiated on Diff Activities
if (!isInitialised) {
isInitialised = true
init() init()
} }
}
override fun init() { override fun init() {
Countly.sharedInstance().init( Countly.sharedInstance().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()

View File

@ -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) {

View File

@ -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)

View File

@ -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

View File

@ -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 {

View File

@ -2,4 +2,4 @@ package com.shabinder.common.models
expect interface PlatformActions expect interface PlatformActions
expect val StubPlatformActions: PlatformActions internal expect val StubPlatformActions: PlatformActions

View File

@ -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 {}

View File

@ -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 {}

View File

@ -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"
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"> <aapt:attr name="android:fillColor">
<gradient android:endX="437.019" android:endY="74.981" <gradient
android:startX="74.981" android:startY="437.019" android:type="linear"> android:startY="437.0193"
<item android:color="#FF736BFD" android:offset="0"/> android:startX="74.9807"
<item android:color="#FFF54187" android:offset="1"/> 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> </gradient>
</aapt:attr> </aapt:attr>
</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"/> <path
<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"/> android:fillColor="#FF000000"
<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"/> 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: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="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"> <aapt:attr name="android:fillColor">
<gradient android:endX="384.197" android:endY="490.009" <gradient
android:startX="487.832" android:startY="386.374" android:type="linear"> android:startY="386.3741"
<item android:color="#FF736BFD" android:offset="0"/> android:startX="487.8323"
<item android:color="#FFF54187" android:offset="1"/> 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> </gradient>
</aapt:attr> </aapt:attr>
</path> </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: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"/> android:strokeWidth="0.75"
<path android:fillColor="#00000000" 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: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> </vector>

View File

@ -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())
}
}

View File

@ -33,6 +33,7 @@ fun initKoin(enableNetworkLogs: Boolean = false, appDeclaration: KoinAppDeclarat
listOf( listOf(
providersModule(enableNetworkLogs), providersModule(enableNetworkLogs),
databaseModule(), databaseModule(),
appInitModule(),
) )
) )
} }

View File

@ -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
} }
} }

View File

@ -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) {

View File

@ -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 -> {

View File

@ -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)

View File

@ -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))
} }
} }

View File

@ -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()
} }

View File

@ -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 {

View File

@ -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

View File

@ -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()

View File

@ -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()

View File

@ -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;
}
}
}

View File

@ -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
}
}
}
}

View File

@ -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
} }

View File

@ -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);
}
}

View File

@ -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)
}
}

View File

@ -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);
}
}
}

View File

@ -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)
}
}
}

View File

@ -1,15 +0,0 @@
package nl.bravobit.ffmpeg;
public interface ResponseHandler {
/**
* on Start
*/
void onStart();
/**
* on Finish
*/
void onFinish();
}

View File

@ -0,0 +1,13 @@
package nl.bravobit.ffmpeg
interface ResponseHandler {
/**
* on Start
*/
fun onStart()
/**
* on Finish
*/
fun onFinish()
}

View File

@ -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;
}
}

View File

@ -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
}
}

View File

@ -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;
}
}

View 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
}
}