Revert Back to old/gold java.io.File!

- SAF is painfully slow.
- SAF brings nothing but more issues and hacks.
- External SdCard write Access wont be fixed (NO SAF).
This commit is contained in:
shabinder 2021-05-14 02:51:33 +05:30
parent a3e9b7c3c1
commit d9ba60dbbf
16 changed files with 178 additions and 48 deletions

View File

@ -120,12 +120,6 @@ dependencies {
implementation(MVIKotlin.mvikotlinLogging) implementation(MVIKotlin.mvikotlinLogging)
implementation(MVIKotlin.mvikotlinTimeTravel) implementation(MVIKotlin.mvikotlinTimeTravel)
// Firebase
implementation(platform("com.google.firebase:firebase-bom:27.1.0"))
implementation("com.google.firebase:firebase-analytics-ktx")
implementation("com.google.firebase:firebase-crashlytics-ktx")
implementation("com.google.firebase:firebase-perf-ktx")
// Extras // Extras
Extras.Android.apply { Extras.Android.apply {
implementation(Acra.notification) implementation(Acra.notification)
@ -134,6 +128,7 @@ dependencies {
implementation(matomo) implementation(matomo)
} }
//implementation("com.jakewharton.timber:timber:4.7.1")
implementation("dev.icerock.moko:parcelize:0.6.1") implementation("dev.icerock.moko:parcelize:0.6.1")
implementation("com.github.shabinder:storage-chooser:2.0.4.45") implementation("com.github.shabinder:storage-chooser:2.0.4.45")
implementation("com.google.accompanist:accompanist-insets:0.9.1") implementation("com.google.accompanist:accompanist-insets:0.9.1")

View File

@ -29,8 +29,26 @@ import org.koin.android.ext.koin.androidContext
import org.koin.android.ext.koin.androidLogger import org.koin.android.ext.koin.androidLogger
import org.koin.core.component.KoinComponent import org.koin.core.component.KoinComponent
import org.koin.core.logger.Level import org.koin.core.logger.Level
import org.matomo.sdk.Matomo
import org.matomo.sdk.Tracker
import org.matomo.sdk.TrackerBuilder
class App: Application(), KoinComponent { class App: Application(), KoinComponent {
val tracker: Tracker by lazy {
TrackerBuilder.createDefault(
"https://kind-grasshopper-73.telebit.io/matomo/matomo.php", 1)
.build(Matomo.getInstance(this)).apply {
if (BuildConfig.DEBUG) {
/*Timber.plant(DebugTree())
addTrackingCallback {
Timber.d(it.toMap().toString())
it
}*/
}
}
}
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()

View File

@ -56,22 +56,22 @@ import com.shabinder.common.models.Actions
import com.shabinder.common.models.DownloadStatus import com.shabinder.common.models.DownloadStatus
import com.shabinder.common.models.PlatformActions import com.shabinder.common.models.PlatformActions
import com.shabinder.common.models.PlatformActions.Companion.SharedPreferencesKey import com.shabinder.common.models.PlatformActions.Companion.SharedPreferencesKey
import com.shabinder.common.models.Status
import com.shabinder.common.models.TrackDetails import com.shabinder.common.models.TrackDetails
import com.shabinder.common.models.methods
import com.shabinder.common.root.SpotiFlyerRoot import com.shabinder.common.root.SpotiFlyerRoot
import com.shabinder.common.root.SpotiFlyerRoot.Analytics
import com.shabinder.common.root.callbacks.SpotiFlyerRootCallBacks import com.shabinder.common.root.callbacks.SpotiFlyerRootCallBacks
import com.shabinder.common.uikit.* import com.shabinder.common.uikit.*
import com.shabinder.spotiflyer.utils.*
import com.shabinder.common.models.Status
import com.shabinder.common.models.methods
import com.shabinder.spotiflyer.ui.NetworkDialog import com.shabinder.spotiflyer.ui.NetworkDialog
import com.shabinder.spotiflyer.ui.PermissionDialog import com.shabinder.spotiflyer.ui.PermissionDialog
import com.shabinder.spotiflyer.utils.*
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.coroutines.flow.* import kotlinx.coroutines.flow.*
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
import org.matomo.sdk.extra.TrackHelper
import java.io.File import java.io.File
const val disableDozeCode = 1223
@ExperimentalAnimationApi @ExperimentalAnimationApi
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
@ -85,6 +85,7 @@ class MainActivity : ComponentActivity() {
private lateinit var updateUIReceiver: BroadcastReceiver private lateinit var updateUIReceiver: BroadcastReceiver
private lateinit var queryReceiver: BroadcastReceiver private lateinit var queryReceiver: BroadcastReceiver
private val internetAvailability by lazy { ConnectionLiveData(applicationContext) } private val internetAvailability by lazy { ConnectionLiveData(applicationContext) }
private val tracker get() = (application as App).tracker
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -118,7 +119,8 @@ class MainActivity : ComponentActivity() {
PermissionDialog( PermissionDialog(
permissionGranted.value, permissionGranted.value,
{ requestStoragePermission() }, { requestStoragePermission() },
{ disableDozeMode(disableDozeCode) } { disableDozeMode(disableDozeCode) },
dir::enableAnalytics
) )
} }
} }
@ -130,6 +132,10 @@ class MainActivity : ComponentActivity() {
private fun initialise() { private fun initialise() {
checkIfLatestVersion() checkIfLatestVersion()
handleIntentFromExternalActivity() handleIntentFromExternalActivity()
if(dir.isAnalyticsEnabled){
// Download/App Install Event
TrackHelper.track().download().with(tracker)
}
} }
@Composable @Composable
@ -380,4 +386,8 @@ class MainActivity : ComponentActivity() {
} }
} }
} }
companion object {
const val disableDozeCode = 1223
}
} }

View File

@ -8,11 +8,13 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.AlertDialog import androidx.compose.material.AlertDialog
import androidx.compose.material.Icon import androidx.compose.material.Icon
import androidx.compose.material.Text import androidx.compose.material.Text
import androidx.compose.material.TextButton import androidx.compose.material.TextButton
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Insights
import androidx.compose.material.icons.rounded.SdStorage import androidx.compose.material.icons.rounded.SdStorage
import androidx.compose.material.icons.rounded.SystemSecurityUpdate import androidx.compose.material.icons.rounded.SystemSecurityUpdate
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@ -28,6 +30,7 @@ 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 androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.DialogProperties
import com.shabinder.common.uikit.SpotiFlyerShapes import com.shabinder.common.uikit.SpotiFlyerShapes
import com.shabinder.common.uikit.SpotiFlyerTypography import com.shabinder.common.uikit.SpotiFlyerTypography
import com.shabinder.common.uikit.colorPrimary import com.shabinder.common.uikit.colorPrimary
@ -38,13 +41,50 @@ import kotlinx.coroutines.delay
fun PermissionDialog( fun PermissionDialog(
permissionGranted: Boolean, permissionGranted: Boolean,
requestStoragePermission:() -> Unit, requestStoragePermission:() -> Unit,
disableDozeMode:() -> Unit disableDozeMode:() -> Unit,
enableAnalytics:() -> Unit
){ ){
var askForPermission by remember { mutableStateOf(false) } var askForPermission by remember { mutableStateOf(false) }
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
delay(2000) delay(2000)
askForPermission = true askForPermission = true
} }
// Analytics Permission Dialog
var askForAnalyticsPermission by remember { mutableStateOf(false) }
AnimatedVisibility(askForAnalyticsPermission) {
AlertDialog(
onDismissRequest = {
askForAnalyticsPermission = false
},
title = {
Row(verticalAlignment = Alignment.CenterVertically) {
Icon(Icons.Rounded.Insights,"Analytics",Modifier.size(52.dp))
Spacer(Modifier.padding(horizontal = 4.dp))
Text("Grant Analytics Access",style = SpotiFlyerTypography.h5,textAlign = TextAlign.Center)
}
},
backgroundColor = Color.DarkGray,
buttons = {
TextButton(
{
askForAnalyticsPermission = false
enableAnalytics()
},
Modifier.padding(bottom = 16.dp, start = 16.dp, end = 16.dp).fillMaxWidth()
.background(colorPrimary, shape = SpotiFlyerShapes.medium)
.padding(horizontal = 8.dp),
) {
Text("Sure!",color = Color.Black,fontSize = 18.sp,textAlign = TextAlign.Center)
}
},
text = {
Text("Your Data is Anonymized and will never be shared with any 3rd party service",style = SpotiFlyerTypography.body2,textAlign = TextAlign.Center)
},
properties = DialogProperties(dismissOnBackPress = true,dismissOnClickOutside = false)
)
}
AnimatedVisibility( AnimatedVisibility(
askForPermission && !permissionGranted askForPermission && !permissionGranted
) { ) {
@ -55,6 +95,7 @@ fun PermissionDialog(
{ {
requestStoragePermission() requestStoragePermission()
disableDozeMode() disableDozeMode()
askForAnalyticsPermission = true
}, },
Modifier.padding(bottom = 16.dp, start = 16.dp, end = 16.dp).fillMaxWidth() Modifier.padding(bottom = 16.dp, start = 16.dp, end = 16.dp).fillMaxWidth()
.background(colorPrimary, shape = SpotiFlyerShapes.medium) .background(colorPrimary, shape = SpotiFlyerShapes.medium)
@ -100,6 +141,23 @@ fun PermissionDialog(
) )
} }
} }
Row(
modifier = Modifier.fillMaxWidth().padding(top = 6.dp),
verticalAlignment = Alignment.CenterVertically
) {
Icon(Icons.Rounded.Insights,"Analytics")
Spacer(modifier = Modifier.padding(start = 16.dp))
Column {
Text(
text = "Analytics",
style = SpotiFlyerTypography.h6.copy(fontWeight = FontWeight.SemiBold)
)
Text(
text = "Share Analytics Data (optional) with App Devs (Self-Hosted), It will never be used/shared/sold to any third party service.",
style = SpotiFlyerTypography.subtitle2,
)
}
}
} }
} }
) )

View File

@ -102,7 +102,7 @@ fun SpotiFlyerMainContent(component: SpotiFlyerMain) {
) )
when (model.selectedCategory) { when (model.selectedCategory) {
HomeCategory.About -> AboutColumn() HomeCategory.About -> AboutColumn { component.analytics.donationDialogVisit() }
HomeCategory.History -> HistoryColumn( HomeCategory.History -> HistoryColumn(
model.records.sortedByDescending { it.id }, model.records.sortedByDescending { it.id },
component::loadImage, component::loadImage,
@ -221,7 +221,10 @@ fun SearchPanel(
} }
@Composable @Composable
fun AboutColumn(modifier: Modifier = Modifier) { fun AboutColumn(
modifier: Modifier = Modifier,
donationDialogOpenEvent:() -> Unit
) {
Box { Box {
val stateVertical = rememberScrollState(0) val stateVertical = rememberScrollState(0)
@ -331,14 +334,18 @@ fun AboutColumn(modifier: Modifier = Modifier) {
var isDonationDialogVisible by remember { mutableStateOf(false) } var isDonationDialogVisible by remember { mutableStateOf(false) }
DonationDialog( DonationDialog(
isDonationDialogVisible isDonationDialogVisible,
) { onDismiss = {
isDonationDialogVisible = false isDonationDialogVisible = false
} }
)
Row( Row(
modifier = modifier.fillMaxWidth().padding(vertical = 6.dp) modifier = modifier.fillMaxWidth().padding(vertical = 6.dp)
.clickable(onClick = { isDonationDialogVisible = true }), .clickable(onClick = {
isDonationDialogVisible = true
donationDialogOpenEvent()
}),
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
Icon(Icons.Rounded.CardGiftcard, "Support Developer") Icon(Icons.Rounded.CardGiftcard, "Support Developer")

View File

@ -41,7 +41,7 @@ import java.net.URL
actual class Dir actual constructor( actual class Dir actual constructor(
private val logger: Kermit, private val logger: Kermit,
private val settings: Settings, private val settings: Settings,
private val spotiFlyerDatabase: SpotiFlyerDatabase, spotiFlyerDatabase: SpotiFlyerDatabase,
) { ) {
companion object { companion object {
const val DirKey = "downloadDir" const val DirKey = "downloadDir"
@ -91,7 +91,7 @@ actual class Dir actual constructor(
* */ * */
if(!songFile.exists()) { if(!songFile.exists()) {
/*Make intermediate Dirs if they don't exist yet*/ /*Make intermediate Dirs if they don't exist yet*/
songFile.parentFile.mkdirs() songFile.parentFile?.mkdirs()
} }
if(mp3ByteArray.isNotEmpty()) songFile.writeBytes(mp3ByteArray) if(mp3ByteArray.isNotEmpty()) songFile.writeBytes(mp3ByteArray)

View File

@ -17,7 +17,6 @@
package com.shabinder.common.di package com.shabinder.common.di
import co.touchlab.kermit.Kermit import co.touchlab.kermit.Kermit
import co.touchlab.stately.ensureNeverFrozen
import com.russhwolf.settings.Settings import com.russhwolf.settings.Settings
import com.shabinder.common.database.databaseModule import com.shabinder.common.database.databaseModule
import com.shabinder.common.database.getLogger import com.shabinder.common.database.getLogger
@ -25,15 +24,10 @@ import com.shabinder.common.di.providers.GaanaProvider
import com.shabinder.common.di.providers.SpotifyProvider import com.shabinder.common.di.providers.SpotifyProvider
import com.shabinder.common.di.providers.YoutubeMp3 import com.shabinder.common.di.providers.YoutubeMp3
import com.shabinder.common.di.providers.YoutubeMusic import com.shabinder.common.di.providers.YoutubeMusic
import com.shabinder.common.models.NativeAtomicReference import io.ktor.client.*
import io.ktor.client.HttpClient import io.ktor.client.features.json.*
import io.ktor.client.features.HttpTimeout import io.ktor.client.features.json.serializer.*
import io.ktor.client.features.json.JsonFeature import io.ktor.client.features.logging.*
import io.ktor.client.features.json.serializer.KotlinxSerializer
import io.ktor.client.features.logging.DEFAULT
import io.ktor.client.features.logging.LogLevel
import io.ktor.client.features.logging.Logger
import io.ktor.client.features.logging.Logging
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import org.koin.core.context.startKoin import org.koin.core.context.startKoin
import org.koin.dsl.KoinAppDeclaration import org.koin.dsl.KoinAppDeclaration
@ -87,12 +81,6 @@ fun createHttpClient(enableNetworkLogs: Boolean = false) = HttpClient {
} }
) )
}*/ }*/
// Timeout
install(HttpTimeout) {
// requestTimeoutMillis = 20000L
connectTimeoutMillis = 15000L
socketTimeoutMillis = 15000L
}
if (enableNetworkLogs) { if (enableNetworkLogs) {
install(Logging) { install(Logging) {
logger = Logger.DEFAULT logger = Logger.DEFAULT

View File

@ -18,7 +18,6 @@ package com.shabinder.common.di
import co.touchlab.kermit.Kermit import co.touchlab.kermit.Kermit
import com.russhwolf.settings.Settings import com.russhwolf.settings.Settings
import com.russhwolf.settings.SettingsListener
import com.shabinder.common.database.SpotiFlyerDatabase import com.shabinder.common.database.SpotiFlyerDatabase
import com.shabinder.common.di.utils.removeIllegalChars import com.shabinder.common.di.utils.removeIllegalChars
import com.shabinder.common.models.DownloadResult import com.shabinder.common.models.DownloadResult

View File

@ -45,6 +45,13 @@ actual class Dir actual constructor(
) { ) {
companion object { companion object {
const val DirKey = "downloadDir" const val DirKey = "downloadDir"
const val AnalyticsKey = "analytics"
}
actual val isAnalyticsEnabled get() = settings.getBooleanOrNull(AnalyticsKey) ?: false
actual fun enableAnalytics() {
settings.putBoolean(AnalyticsKey,true)
} }
init { init {

View File

@ -21,7 +21,6 @@ import platform.Foundation.sendSynchronousRequest
import platform.Foundation.writeToFile import platform.Foundation.writeToFile
import platform.UIKit.UIImage import platform.UIKit.UIImage
import platform.UIKit.UIImageJPEGRepresentation import platform.UIKit.UIImageJPEGRepresentation
import java.lang.System
actual class Dir actual constructor( actual class Dir actual constructor(
val logger: Kermit, val logger: Kermit,
@ -30,6 +29,13 @@ actual class Dir actual constructor(
) { ) {
companion object { companion object {
const val DirKey = "downloadDir" const val DirKey = "downloadDir"
const val AnalyticsKey = "analytics"
}
actual val isAnalyticsEnabled get() = settings.getBooleanOrNull(AnalyticsKey) ?: false
actual fun enableAnalytics() {
settings.putBoolean(AnalyticsKey,true)
} }
actual fun isPresent(path: String): Boolean = NSFileManager.defaultManager.fileExistsAtPath(path) actual fun isPresent(path: String): Boolean = NSFileManager.defaultManager.fileExistsAtPath(path)

View File

@ -39,8 +39,17 @@ actual class Dir actual constructor(
) { ) {
companion object { companion object {
const val DirKey = "downloadDir" const val DirKey = "downloadDir"
const val AnalyticsKey = "analytics"
} }
actual val isAnalyticsEnabled get() = settings.getBooleanOrNull(AnalyticsKey) ?: false
actual fun enableAnalytics() {
settings.putBoolean(AnalyticsKey,true)
}
actual fun setDownloadDirectory(newBasePath:String) = settings.putString(DirKey,newBasePath)
/*init { /*init {
createDirectories() createDirectories()
}*/ }*/

View File

@ -65,6 +65,11 @@ interface SpotiFlyerList {
val link: String val link: String
val listOutput: Consumer<Output> val listOutput: Consumer<Output>
val downloadProgressFlow: MutableSharedFlow<HashMap<String, DownloadStatus>> val downloadProgressFlow: MutableSharedFlow<HashMap<String, DownloadStatus>>
val listAnalytics: Analytics
}
interface Analytics {
} }
sealed class Output { sealed class Output {

View File

@ -31,6 +31,8 @@ interface SpotiFlyerMain {
val models: Value<State> val models: Value<State>
val analytics: Analytics
/* /*
* We Intend to Move to List Screen * We Intend to Move to List Screen
* Note: Implementation in Root * Note: Implementation in Root
@ -57,6 +59,11 @@ interface SpotiFlyerMain {
val storeFactory: StoreFactory val storeFactory: StoreFactory
val database: Database? val database: Database?
val dir: Dir val dir: Dir
val mainAnalytics: Analytics
}
interface Analytics {
fun donationDialogVisit()
} }
sealed class Output { sealed class Output {

View File

@ -40,9 +40,6 @@ internal class SpotiFlyerMainImpl(
init { init {
instanceKeeper.ensureNeverFrozen() instanceKeeper.ensureNeverFrozen()
lifecycle.doOnDestroy {
cache.invalidateAll()
}
} }
private val store = private val store =
@ -55,11 +52,13 @@ internal class SpotiFlyerMainImpl(
private val cache = Cache.Builder private val cache = Cache.Builder
.newBuilder() .newBuilder()
.maximumCacheSize(20) .maximumCacheSize(25)
.build<String, Picture>() .build<String, Picture>()
override val models: Value<State> = store.asValue() override val models: Value<State> = store.asValue()
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 (methods.value.isInternetAvailable) mainOutput.callback(Output.Search(link = link))
else methods.value.showPopUpMessage("Check Network Connection Please") else methods.value.showPopUpMessage("Check Network Connection Please")

View File

@ -54,6 +54,14 @@ interface SpotiFlyerRoot {
val directories: Dir val directories: Dir
val downloadProgressReport: MutableSharedFlow<HashMap<String, DownloadStatus>> val downloadProgressReport: MutableSharedFlow<HashMap<String, DownloadStatus>>
val actions:Actions val actions:Actions
val analytics: Analytics
}
interface Analytics {
fun appLaunchEvent()
fun homeScreenVisit()
fun listScreenVisit()
fun donationDialogVisit()
} }
} }

View File

@ -37,6 +37,7 @@ import com.shabinder.common.models.AllPlatforms
import com.shabinder.common.models.Consumer import com.shabinder.common.models.Consumer
import com.shabinder.common.models.methods import com.shabinder.common.models.methods
import com.shabinder.common.root.SpotiFlyerRoot import com.shabinder.common.root.SpotiFlyerRoot
import com.shabinder.common.root.SpotiFlyerRoot.Analytics
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
@ -49,7 +50,8 @@ internal class SpotiFlyerRootImpl(
componentContext: ComponentContext, componentContext: ComponentContext,
private val main: (ComponentContext, output:Consumer<SpotiFlyerMain.Output>)->SpotiFlyerMain, private val main: (ComponentContext, output:Consumer<SpotiFlyerMain.Output>)->SpotiFlyerMain,
private val list: (ComponentContext, link:String, output:Consumer<SpotiFlyerList.Output>)->SpotiFlyerList, private val list: (ComponentContext, link:String, output:Consumer<SpotiFlyerList.Output>)->SpotiFlyerList,
private val actions: Actions private val actions: Actions,
private val analytics: Analytics
) : SpotiFlyerRoot, ComponentContext by componentContext { ) : SpotiFlyerRoot, ComponentContext by componentContext {
constructor( constructor(
@ -72,7 +74,8 @@ internal class SpotiFlyerRootImpl(
dependencies dependencies
) )
}, },
actions = dependencies.actions.freeze() actions = dependencies.actions.freeze(),
analytics = dependencies.analytics
) { ) {
instanceKeeper.ensureNeverFrozen() instanceKeeper.ensureNeverFrozen()
methods.value = dependencies.actions.freeze() methods.value = dependencies.actions.freeze()
@ -113,16 +116,23 @@ internal class SpotiFlyerRootImpl(
private fun onMainOutput(output: SpotiFlyerMain.Output) = private fun onMainOutput(output: SpotiFlyerMain.Output) =
when (output) { when (output) {
is SpotiFlyerMain.Output.Search -> router.push(Configuration.List(link = output.link)) is SpotiFlyerMain.Output.Search -> {
router.push(Configuration.List(link = output.link))
analytics.listScreenVisit()
}
} }
private fun onListOutput(output: SpotiFlyerList.Output): Unit = private fun onListOutput(output: SpotiFlyerList.Output): Unit =
when (output) { when (output) {
is SpotiFlyerList.Output.Finished -> router.pop() is SpotiFlyerList.Output.Finished -> {
router.pop()
analytics.homeScreenVisit()
}
} }
private fun authenticateSpotify(spotifyProvider: SpotifyProvider, override:Boolean){ private fun authenticateSpotify(spotifyProvider: SpotifyProvider, override:Boolean){
GlobalScope.launch(Dispatchers.Default) { GlobalScope.launch(Dispatchers.Default) {
analytics.appLaunchEvent()
/*Authenticate Spotify Client*/ /*Authenticate Spotify Client*/
spotifyProvider.authenticateSpotifyClient(override) spotifyProvider.authenticateSpotifyClient(override)
} }
@ -143,6 +153,9 @@ private fun spotiFlyerMain(componentContext: ComponentContext, output: Consumer<
dependencies = object : SpotiFlyerMain.Dependencies, Dependencies by dependencies { dependencies = object : SpotiFlyerMain.Dependencies, Dependencies by dependencies {
override val mainOutput: Consumer<SpotiFlyerMain.Output> = output override val mainOutput: Consumer<SpotiFlyerMain.Output> = output
override val dir: Dir = directories override val dir: Dir = directories
override val mainAnalytics = object : SpotiFlyerMain.Analytics {
override fun donationDialogVisit() = analytics.donationDialogVisit()
}
} }
) )
@ -155,5 +168,6 @@ private fun spotiFlyerList(componentContext: ComponentContext, link: String, out
override val link: String = link override val link: String = link
override val listOutput: Consumer<SpotiFlyerList.Output> = output override val listOutput: Consumer<SpotiFlyerList.Output> = output
override val downloadProgressFlow = downloadProgressReport override val downloadProgressFlow = downloadProgressReport
override val listAnalytics = object : SpotiFlyerList.Analytics {}
} }
) )