mirror of
https://github.com/Shabinder/SpotiFlyer.git
synced 2024-11-22 09:04:32 +01:00
Preference Screen Added
This commit is contained in:
parent
34fcfe4d88
commit
a44f4cc061
@ -300,7 +300,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
|
|
||||||
override fun showPopUpMessage(string: String, long: Boolean) = this@MainActivity.showPopUpMessage(string,long)
|
override fun showPopUpMessage(string: String, long: Boolean) = this@MainActivity.showPopUpMessage(string,long)
|
||||||
|
|
||||||
override fun setDownloadDirectoryAction() = setUpOnPrefClickListener()
|
override fun setDownloadDirectoryAction(callBack: (String) -> Unit) = setUpOnPrefClickListener(callBack)
|
||||||
|
|
||||||
override fun queryActiveTracks() = this@MainActivity.queryActiveTracks()
|
override fun queryActiveTracks() = this@MainActivity.queryActiveTracks()
|
||||||
|
|
||||||
@ -400,7 +400,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
private fun setUpOnPrefClickListener() {
|
private fun setUpOnPrefClickListener(callBack : (String) -> Unit) {
|
||||||
// Initialize Builder
|
// Initialize Builder
|
||||||
val chooser = StorageChooser.Builder()
|
val chooser = StorageChooser.Builder()
|
||||||
.withActivity(this)
|
.withActivity(this)
|
||||||
@ -421,6 +421,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
if (f.canWrite()) {
|
if (f.canWrite()) {
|
||||||
// hell yeah :)
|
// hell yeah :)
|
||||||
preferenceManager.setDownloadDirectory(path)
|
preferenceManager.setDownloadDirectory(path)
|
||||||
|
callBack(dir.defaultDir())
|
||||||
showPopUpMessage(Strings.downloadDirectorySetTo("\n${dir.defaultDir()}"))
|
showPopUpMessage(Strings.downloadDirectorySetTo("\n${dir.defaultDir()}"))
|
||||||
}else{
|
}else{
|
||||||
showPopUpMessage(Strings.noWriteAccess("\n$path "))
|
showPopUpMessage(Strings.noWriteAccess("\n$path "))
|
||||||
|
@ -33,6 +33,7 @@ kotlin {
|
|||||||
implementation(project(":common:root"))
|
implementation(project(":common:root"))
|
||||||
implementation(project(":common:main"))
|
implementation(project(":common:main"))
|
||||||
implementation(project(":common:list"))
|
implementation(project(":common:list"))
|
||||||
|
implementation(project(":common:preference"))
|
||||||
implementation(project(":common:database"))
|
implementation(project(":common:database"))
|
||||||
implementation(project(":common:data-models"))
|
implementation(project(":common:data-models"))
|
||||||
implementation(project(":common:dependency-injection"))
|
implementation(project(":common:dependency-injection"))
|
||||||
|
@ -0,0 +1,201 @@
|
|||||||
|
package com.shabinder.common.uikit.screens
|
||||||
|
|
||||||
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
|
import androidx.compose.animation.ExperimentalAnimationApi
|
||||||
|
import androidx.compose.foundation.BorderStroke
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.ColumnScope
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.selection.selectable
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.material.Card
|
||||||
|
import androidx.compose.material.Icon
|
||||||
|
import androidx.compose.material.RadioButton
|
||||||
|
import androidx.compose.material.Switch
|
||||||
|
import androidx.compose.material.SwitchDefaults
|
||||||
|
import androidx.compose.material.Text
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.rounded.Insights
|
||||||
|
import androidx.compose.material.icons.rounded.MusicNote
|
||||||
|
import androidx.compose.material.icons.rounded.SnippetFolder
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.painter.Painter
|
||||||
|
import androidx.compose.ui.graphics.vector.rememberVectorPainter
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState
|
||||||
|
import com.shabinder.common.models.AudioQuality
|
||||||
|
import com.shabinder.common.preference.SpotiFlyerPreference
|
||||||
|
import com.shabinder.common.translations.Strings
|
||||||
|
import com.shabinder.common.uikit.configurations.SpotiFlyerTypography
|
||||||
|
import com.shabinder.common.uikit.configurations.colorAccent
|
||||||
|
import com.shabinder.common.uikit.configurations.colorOffWhite
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SpotiFlyerPreferenceContent(component: SpotiFlyerPreference) {
|
||||||
|
val model by component.model.subscribeAsState()
|
||||||
|
|
||||||
|
val stateVertical = rememberScrollState(0)
|
||||||
|
|
||||||
|
Column(Modifier.fillMaxSize().padding(8.dp).verticalScroll(stateVertical)) {
|
||||||
|
Spacer(Modifier.padding(top = 16.dp))
|
||||||
|
Card(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
border = BorderStroke(1.dp, Color.Gray)
|
||||||
|
) {
|
||||||
|
Column(Modifier.padding(12.dp)) {
|
||||||
|
Text(
|
||||||
|
text = Strings.preferences(),
|
||||||
|
style = SpotiFlyerTypography.body1,
|
||||||
|
color = colorAccent
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.padding(top = 12.dp))
|
||||||
|
|
||||||
|
SettingsRow(
|
||||||
|
icon = rememberVectorPainter(Icons.Rounded.MusicNote),
|
||||||
|
title = "Preferred Audio Quality",
|
||||||
|
value = model.preferredQuality.kbps + "KBPS"
|
||||||
|
) { save ->
|
||||||
|
val audioQualities = AudioQuality.values()
|
||||||
|
|
||||||
|
audioQualities.forEach { quality ->
|
||||||
|
Row(
|
||||||
|
Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.selectable(
|
||||||
|
selected = (quality == model.preferredQuality),
|
||||||
|
onClick = {
|
||||||
|
component.setPreferredQuality(quality)
|
||||||
|
save()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.padding(horizontal = 16.dp,vertical = 2.dp)
|
||||||
|
) {
|
||||||
|
RadioButton(
|
||||||
|
selected = (quality == model.preferredQuality),
|
||||||
|
onClick = {
|
||||||
|
component.setPreferredQuality(quality)
|
||||||
|
save()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = quality.kbps + " KBPS",
|
||||||
|
style = SpotiFlyerTypography.h6,
|
||||||
|
modifier = Modifier.padding(start = 16.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(Modifier.padding(top = 12.dp))
|
||||||
|
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = Modifier.fillMaxWidth().clickable(
|
||||||
|
onClick = { component.selectNewDownloadDirectory() }
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Icon(Icons.Rounded.SnippetFolder, Strings.setDownloadDirectory(), Modifier.size(32.dp), tint = Color(0xFFCCCCCC))
|
||||||
|
Spacer(modifier = Modifier.padding(start = 16.dp))
|
||||||
|
Column {
|
||||||
|
Text(
|
||||||
|
text = Strings.setDownloadDirectory(),
|
||||||
|
style = SpotiFlyerTypography.h6
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = model.downloadPath,
|
||||||
|
style = SpotiFlyerTypography.subtitle2
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(Modifier.padding(top = 12.dp))
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
.clickable(
|
||||||
|
onClick = { component.toggleAnalytics(!model.isAnalyticsEnabled) }
|
||||||
|
),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
@Suppress("DuplicatedCode")
|
||||||
|
Icon(Icons.Rounded.Insights, Strings.analytics() + Strings.status(), Modifier.size(32.dp))
|
||||||
|
Spacer(modifier = Modifier.padding(start = 16.dp))
|
||||||
|
Column(
|
||||||
|
Modifier.weight(1f)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = Strings.analytics(),
|
||||||
|
style = SpotiFlyerTypography.h6
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = Strings.analyticsDescription(),
|
||||||
|
style = SpotiFlyerTypography.subtitle2
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Switch(
|
||||||
|
checked = model.isAnalyticsEnabled,
|
||||||
|
onCheckedChange = null,
|
||||||
|
colors = SwitchDefaults.colors(uncheckedThumbColor = colorOffWhite)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Spacer(modifier = Modifier.padding(top = 8.dp))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalAnimationApi::class)
|
||||||
|
@Composable
|
||||||
|
fun SettingsRow(
|
||||||
|
icon: Painter,
|
||||||
|
title: String,
|
||||||
|
value:String,
|
||||||
|
editContent: @Composable ColumnScope.(() -> Unit) -> Unit
|
||||||
|
) {
|
||||||
|
|
||||||
|
var isEditMode by remember { mutableStateOf(false) }
|
||||||
|
Column {
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = Modifier.fillMaxWidth().clickable(
|
||||||
|
onClick = { isEditMode = !isEditMode }
|
||||||
|
).padding(vertical = 6.dp)
|
||||||
|
) {
|
||||||
|
Icon(icon, title, Modifier.size(32.dp), tint = Color(0xFFCCCCCC))
|
||||||
|
Spacer(modifier = Modifier.padding(start = 16.dp))
|
||||||
|
Column {
|
||||||
|
Text(
|
||||||
|
text = title,
|
||||||
|
style = SpotiFlyerTypography.h6
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = value,
|
||||||
|
style = SpotiFlyerTypography.subtitle2
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AnimatedVisibility(isEditMode) {
|
||||||
|
Column {
|
||||||
|
editContent {
|
||||||
|
isEditMode = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -136,8 +136,8 @@ fun MainScreen(modifier: Modifier = Modifier, alpha: Float, topPadding: Dp = 0.d
|
|||||||
AppBar(
|
AppBar(
|
||||||
backgroundColor = appBarColor,
|
backgroundColor = appBarColor,
|
||||||
onBackPressed = callBacks::popBackToHomeScreen,
|
onBackPressed = callBacks::popBackToHomeScreen,
|
||||||
setDownloadDirectory = callBacks::setDownloadDirectory,
|
openPreferenceScreen = callBacks::openPreferenceScreen,
|
||||||
isBackButtonVisible = activeComponent.value.activeChild.instance is Child.List,
|
isBackButtonVisible = activeComponent.value.activeChild.instance !is Child.Main,
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
)
|
)
|
||||||
Spacer(Modifier.padding(top = topPadding))
|
Spacer(Modifier.padding(top = topPadding))
|
||||||
@ -148,6 +148,7 @@ fun MainScreen(modifier: Modifier = Modifier, alpha: Float, topPadding: Dp = 0.d
|
|||||||
when (val child = it.instance) {
|
when (val child = it.instance) {
|
||||||
is Child.Main -> SpotiFlyerMainContent(component = child.component)
|
is Child.Main -> SpotiFlyerMainContent(component = child.component)
|
||||||
is Child.List -> SpotiFlyerListContent(component = child.component)
|
is Child.List -> SpotiFlyerListContent(component = child.component)
|
||||||
|
is Child.Preference -> SpotiFlyerPreferenceContent(component = child.component)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -158,7 +159,7 @@ fun MainScreen(modifier: Modifier = Modifier, alpha: Float, topPadding: Dp = 0.d
|
|||||||
fun AppBar(
|
fun AppBar(
|
||||||
backgroundColor: Color,
|
backgroundColor: Color,
|
||||||
onBackPressed: () -> Unit,
|
onBackPressed: () -> Unit,
|
||||||
setDownloadDirectory: () -> Unit,
|
openPreferenceScreen: () -> Unit,
|
||||||
isBackButtonVisible: Boolean,
|
isBackButtonVisible: Boolean,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
@ -189,7 +190,7 @@ fun AppBar(
|
|||||||
},
|
},
|
||||||
actions = {
|
actions = {
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = { setDownloadDirectory() }
|
onClick = { openPreferenceScreen() }
|
||||||
) {
|
) {
|
||||||
Icon(Icons.Filled.Settings, Strings.preferences(), tint = Color.Gray)
|
Icon(Icons.Filled.Settings, Strings.preferences(), tint = Color.Gray)
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ interface Actions {
|
|||||||
fun showPopUpMessage(string: String, long: Boolean = false)
|
fun showPopUpMessage(string: String, long: Boolean = false)
|
||||||
|
|
||||||
// Change Download Directory
|
// Change Download Directory
|
||||||
fun setDownloadDirectoryAction()
|
fun setDownloadDirectoryAction(callBack: (String) -> Unit)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Query Downloading Tracks
|
* Query Downloading Tracks
|
||||||
@ -47,7 +47,7 @@ interface Actions {
|
|||||||
private fun stubActions(): Actions = object : Actions {
|
private fun stubActions(): Actions = object : Actions {
|
||||||
override val platformActions = StubPlatformActions
|
override val platformActions = StubPlatformActions
|
||||||
override fun showPopUpMessage(string: String, long: Boolean) {}
|
override fun showPopUpMessage(string: String, long: Boolean) {}
|
||||||
override fun setDownloadDirectoryAction() {}
|
override fun setDownloadDirectoryAction(callBack: (String) -> Unit) {}
|
||||||
override fun queryActiveTracks() {}
|
override fun queryActiveTracks() {}
|
||||||
override fun giveDonation() {}
|
override fun giveDonation() {}
|
||||||
override fun shareApp() {}
|
override fun shareApp() {}
|
||||||
|
@ -4,7 +4,6 @@ enum class AudioQuality(val kbps: String) {
|
|||||||
KBPS128("128"),
|
KBPS128("128"),
|
||||||
KBPS160("160"),
|
KBPS160("160"),
|
||||||
KBPS192("192"),
|
KBPS192("192"),
|
||||||
KBPS224("224"),
|
|
||||||
KBPS256("256"),
|
KBPS256("256"),
|
||||||
KBPS320("320");
|
KBPS320("320");
|
||||||
|
|
||||||
@ -14,7 +13,6 @@ enum class AudioQuality(val kbps: String) {
|
|||||||
"128" -> KBPS128
|
"128" -> KBPS128
|
||||||
"160" -> KBPS160
|
"160" -> KBPS160
|
||||||
"192" -> KBPS192
|
"192" -> KBPS192
|
||||||
"224" -> KBPS224
|
|
||||||
"256" -> KBPS256
|
"256" -> KBPS256
|
||||||
"320" -> KBPS320
|
"320" -> KBPS320
|
||||||
else -> KBPS160 // Use 160 as baseline
|
else -> KBPS160 // Use 160 as baseline
|
||||||
|
@ -1,35 +1,40 @@
|
|||||||
package com.shabinder.common.di.preference
|
package com.shabinder.common.di.preference
|
||||||
|
|
||||||
import com.russhwolf.settings.Settings
|
import com.russhwolf.settings.Settings
|
||||||
|
import com.shabinder.common.models.AudioQuality
|
||||||
|
|
||||||
class PreferenceManager(settings: Settings): Settings by settings {
|
class PreferenceManager(settings: Settings): Settings by settings {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val DirKey = "downloadDir"
|
const val DIR_KEY = "downloadDir"
|
||||||
const val AnalyticsKey = "analytics"
|
const val ANALYTICS_KEY = "analytics"
|
||||||
const val FirstLaunch = "firstLaunch"
|
const val FIRST_LAUNCH = "firstLaunch"
|
||||||
const val DonationInterval = "donationInterval"
|
const val DONATION_INTERVAL = "donationInterval"
|
||||||
|
const val PREFERRED_AUDIO_QUALITY = "preferredAudioQuality"
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ANALYTICS */
|
/* ANALYTICS */
|
||||||
val isAnalyticsEnabled get() = getBooleanOrNull(AnalyticsKey) ?: false
|
val isAnalyticsEnabled get() = getBooleanOrNull(ANALYTICS_KEY) ?: false
|
||||||
fun toggleAnalytics(enabled: Boolean) = putBoolean(AnalyticsKey, enabled)
|
fun toggleAnalytics(enabled: Boolean) = putBoolean(ANALYTICS_KEY, enabled)
|
||||||
|
|
||||||
|
|
||||||
/* DOWNLOAD DIRECTORY */
|
/* DOWNLOAD DIRECTORY */
|
||||||
val downloadDir get() = getStringOrNull(DirKey)
|
val downloadDir get() = getStringOrNull(DIR_KEY)
|
||||||
fun setDownloadDirectory(newBasePath: String) = putString(DirKey, newBasePath)
|
fun setDownloadDirectory(newBasePath: String) = putString(DIR_KEY, newBasePath)
|
||||||
|
|
||||||
|
/* Preferred Audio Quality */
|
||||||
|
val audioQuality get() = AudioQuality.getQuality(getStringOrNull(PREFERRED_AUDIO_QUALITY) ?: "320")
|
||||||
|
fun setPreferredAudioQuality(quality: AudioQuality) = putString(PREFERRED_AUDIO_QUALITY,quality.kbps)
|
||||||
|
|
||||||
/* OFFSET FOR WHEN TO ASK FOR SUPPORT */
|
/* OFFSET FOR WHEN TO ASK FOR SUPPORT */
|
||||||
val getDonationOffset: Int get() = (getIntOrNull(DonationInterval) ?: 3).also {
|
val getDonationOffset: Int get() = (getIntOrNull(DONATION_INTERVAL) ?: 3).also {
|
||||||
// Min. Donation Asking Interval is `3`
|
// Min. Donation Asking Interval is `3`
|
||||||
if (it < 3) setDonationOffset(3) else setDonationOffset(it - 1)
|
if (it < 3) setDonationOffset(3) else setDonationOffset(it - 1)
|
||||||
}
|
}
|
||||||
fun setDonationOffset(offset: Int = 5) = putInt(DonationInterval, offset)
|
fun setDonationOffset(offset: Int = 5) = putInt(DONATION_INTERVAL, offset)
|
||||||
|
|
||||||
|
|
||||||
/* TO CHECK IF THIS IS APP's FIRST LAUNCH */
|
/* TO CHECK IF THIS IS APP's FIRST LAUNCH */
|
||||||
val isFirstLaunch get() = getBooleanOrNull(FirstLaunch) ?: true
|
val isFirstLaunch get() = getBooleanOrNull(FIRST_LAUNCH) ?: true
|
||||||
fun firstLaunchDone() = putBoolean(FirstLaunch, false)
|
fun firstLaunchDone() = putBoolean(FIRST_LAUNCH, false)
|
||||||
}
|
}
|
@ -18,6 +18,7 @@ package com.shabinder.common.main.integration
|
|||||||
|
|
||||||
import co.touchlab.stately.ensureNeverFrozen
|
import co.touchlab.stately.ensureNeverFrozen
|
||||||
import com.arkivanov.decompose.ComponentContext
|
import com.arkivanov.decompose.ComponentContext
|
||||||
|
import com.arkivanov.decompose.lifecycle.doOnResume
|
||||||
import com.arkivanov.decompose.value.Value
|
import com.arkivanov.decompose.value.Value
|
||||||
import com.shabinder.common.caching.Cache
|
import com.shabinder.common.caching.Cache
|
||||||
import com.shabinder.common.di.Picture
|
import com.shabinder.common.di.Picture
|
||||||
@ -39,6 +40,9 @@ internal class SpotiFlyerMainImpl(
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
instanceKeeper.ensureNeverFrozen()
|
instanceKeeper.ensureNeverFrozen()
|
||||||
|
lifecycle.doOnResume {
|
||||||
|
store.accept(Intent.ToggleAnalytics(preferenceManager.isAnalyticsEnabled))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val store =
|
private val store =
|
||||||
|
@ -71,12 +71,12 @@ internal class SpotiFlyerMainStoreProvider(
|
|||||||
data class ItemsLoaded(val items: List<DownloadRecord>) : Result()
|
data class ItemsLoaded(val items: List<DownloadRecord>) : Result()
|
||||||
data class CategoryChanged(val category: SpotiFlyerMain.HomeCategory) : Result()
|
data class CategoryChanged(val category: SpotiFlyerMain.HomeCategory) : Result()
|
||||||
data class LinkChanged(val link: String) : Result()
|
data class LinkChanged(val link: String) : Result()
|
||||||
data class ToggleAnalytics(val isEnabled: Boolean) : Result()
|
data class AnalyticsToggled(val isEnabled: Boolean) : Result()
|
||||||
}
|
}
|
||||||
|
|
||||||
private inner class ExecutorImpl : SuspendExecutor<Intent, Unit, State, Result, Nothing>() {
|
private inner class ExecutorImpl : SuspendExecutor<Intent, Unit, State, Result, Nothing>() {
|
||||||
override suspend fun executeAction(action: Unit, getState: () -> State) {
|
override suspend fun executeAction(action: Unit, getState: () -> State) {
|
||||||
dispatch(Result.ToggleAnalytics(preferenceManager.isAnalyticsEnabled))
|
dispatch(Result.AnalyticsToggled(preferenceManager.isAnalyticsEnabled))
|
||||||
updates?.collect {
|
updates?.collect {
|
||||||
dispatch(Result.ItemsLoaded(it))
|
dispatch(Result.ItemsLoaded(it))
|
||||||
}
|
}
|
||||||
@ -90,7 +90,7 @@ internal class SpotiFlyerMainStoreProvider(
|
|||||||
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 -> {
|
||||||
dispatch(Result.ToggleAnalytics(intent.enabled))
|
dispatch(Result.AnalyticsToggled(intent.enabled))
|
||||||
preferenceManager.toggleAnalytics(intent.enabled)
|
preferenceManager.toggleAnalytics(intent.enabled)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -103,7 +103,7 @@ internal class SpotiFlyerMainStoreProvider(
|
|||||||
is Result.ItemsLoaded -> copy(records = result.items)
|
is Result.ItemsLoaded -> copy(records = result.items)
|
||||||
is Result.LinkChanged -> copy(link = result.link)
|
is Result.LinkChanged -> copy(link = result.link)
|
||||||
is Result.CategoryChanged -> copy(selectedCategory = result.category)
|
is Result.CategoryChanged -> copy(selectedCategory = result.category)
|
||||||
is Result.ToggleAnalytics -> copy(isAnalyticsEnabled = result.isEnabled)
|
is Result.AnalyticsToggled -> copy(isAnalyticsEnabled = result.isEnabled)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ import com.arkivanov.mvikotlin.core.store.StoreFactory
|
|||||||
import com.shabinder.common.di.Dir
|
import com.shabinder.common.di.Dir
|
||||||
import com.shabinder.common.di.Picture
|
import com.shabinder.common.di.Picture
|
||||||
import com.shabinder.common.di.preference.PreferenceManager
|
import com.shabinder.common.di.preference.PreferenceManager
|
||||||
|
import com.shabinder.common.models.Actions
|
||||||
import com.shabinder.common.models.AudioQuality
|
import com.shabinder.common.models.AudioQuality
|
||||||
import com.shabinder.common.models.Consumer
|
import com.shabinder.common.models.Consumer
|
||||||
import com.shabinder.common.preference.integration.SpotiFlyerPreferenceImpl
|
import com.shabinder.common.preference.integration.SpotiFlyerPreferenceImpl
|
||||||
@ -34,7 +35,9 @@ interface SpotiFlyerPreference {
|
|||||||
|
|
||||||
fun toggleAnalytics(enabled: Boolean)
|
fun toggleAnalytics(enabled: Boolean)
|
||||||
|
|
||||||
fun setDownloadDirectory(newBasePath: String)
|
fun selectNewDownloadDirectory()
|
||||||
|
|
||||||
|
fun setPreferredQuality(quality: AudioQuality)
|
||||||
|
|
||||||
suspend fun loadImage(url: String): Picture
|
suspend fun loadImage(url: String): Picture
|
||||||
|
|
||||||
@ -43,6 +46,7 @@ interface SpotiFlyerPreference {
|
|||||||
val storeFactory: StoreFactory
|
val storeFactory: StoreFactory
|
||||||
val dir: Dir
|
val dir: Dir
|
||||||
val preferenceManager: PreferenceManager
|
val preferenceManager: PreferenceManager
|
||||||
|
val actions: Actions
|
||||||
val preferenceAnalytics: Analytics
|
val preferenceAnalytics: Analytics
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,6 +58,7 @@ interface SpotiFlyerPreference {
|
|||||||
|
|
||||||
data class State(
|
data class State(
|
||||||
val preferredQuality: AudioQuality = AudioQuality.KBPS320,
|
val preferredQuality: AudioQuality = AudioQuality.KBPS320,
|
||||||
|
val downloadPath: String = "",
|
||||||
val isAnalyticsEnabled: Boolean = false
|
val isAnalyticsEnabled: Boolean = false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ import com.arkivanov.decompose.value.Value
|
|||||||
import com.shabinder.common.caching.Cache
|
import com.shabinder.common.caching.Cache
|
||||||
import com.shabinder.common.di.Picture
|
import com.shabinder.common.di.Picture
|
||||||
import com.shabinder.common.di.utils.asValue
|
import com.shabinder.common.di.utils.asValue
|
||||||
|
import com.shabinder.common.models.AudioQuality
|
||||||
import com.shabinder.common.preference.SpotiFlyerPreference
|
import com.shabinder.common.preference.SpotiFlyerPreference
|
||||||
import com.shabinder.common.preference.SpotiFlyerPreference.Dependencies
|
import com.shabinder.common.preference.SpotiFlyerPreference.Dependencies
|
||||||
import com.shabinder.common.preference.SpotiFlyerPreference.State
|
import com.shabinder.common.preference.SpotiFlyerPreference.State
|
||||||
@ -42,7 +43,9 @@ internal class SpotiFlyerPreferenceImpl(
|
|||||||
instanceKeeper.getStore {
|
instanceKeeper.getStore {
|
||||||
SpotiFlyerPreferenceStoreProvider(
|
SpotiFlyerPreferenceStoreProvider(
|
||||||
storeFactory = storeFactory,
|
storeFactory = storeFactory,
|
||||||
preferenceManager = preferenceManager
|
preferenceManager = preferenceManager,
|
||||||
|
dir = dir,
|
||||||
|
actions = actions
|
||||||
).provide()
|
).provide()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,8 +62,14 @@ internal class SpotiFlyerPreferenceImpl(
|
|||||||
store.accept(Intent.ToggleAnalytics(enabled))
|
store.accept(Intent.ToggleAnalytics(enabled))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setDownloadDirectory(newBasePath: String) {
|
override fun selectNewDownloadDirectory() {
|
||||||
preferenceManager.setDownloadDirectory(newBasePath)
|
actions.setDownloadDirectoryAction {
|
||||||
|
store.accept(Intent.SetDownloadDirectory(dir.defaultDir()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setPreferredQuality(quality: AudioQuality) {
|
||||||
|
store.accept(Intent.SetPreferredAudioQuality(quality))
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun loadImage(url: String): Picture {
|
override suspend fun loadImage(url: String): Picture {
|
||||||
|
@ -17,12 +17,15 @@
|
|||||||
package com.shabinder.common.preference.store
|
package com.shabinder.common.preference.store
|
||||||
|
|
||||||
import com.arkivanov.mvikotlin.core.store.Store
|
import com.arkivanov.mvikotlin.core.store.Store
|
||||||
|
import com.shabinder.common.models.AudioQuality
|
||||||
import com.shabinder.common.preference.SpotiFlyerPreference
|
import com.shabinder.common.preference.SpotiFlyerPreference
|
||||||
|
|
||||||
internal interface SpotiFlyerPreferenceStore : Store<SpotiFlyerPreferenceStore.Intent, SpotiFlyerPreference.State, Nothing> {
|
internal interface SpotiFlyerPreferenceStore : Store<SpotiFlyerPreferenceStore.Intent, SpotiFlyerPreference.State, Nothing> {
|
||||||
sealed class Intent {
|
sealed class Intent {
|
||||||
data class OpenPlatform(val platformID: String, val platformLink: String) : Intent()
|
data class OpenPlatform(val platformID: String, val platformLink: String) : Intent()
|
||||||
data class ToggleAnalytics(val enabled: Boolean) : Intent()
|
data class ToggleAnalytics(val enabled: Boolean) : Intent()
|
||||||
|
data class SetDownloadDirectory(val path: String) : Intent()
|
||||||
|
data class SetPreferredAudioQuality(val quality: AudioQuality) : Intent()
|
||||||
object GiveDonation : Intent()
|
object GiveDonation : Intent()
|
||||||
object ShareApp : Intent()
|
object ShareApp : Intent()
|
||||||
}
|
}
|
||||||
|
@ -21,14 +21,19 @@ 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.core.store.StoreFactory
|
import com.arkivanov.mvikotlin.core.store.StoreFactory
|
||||||
import com.arkivanov.mvikotlin.extensions.coroutines.SuspendExecutor
|
import com.arkivanov.mvikotlin.extensions.coroutines.SuspendExecutor
|
||||||
|
import com.shabinder.common.di.Dir
|
||||||
import com.shabinder.common.di.preference.PreferenceManager
|
import com.shabinder.common.di.preference.PreferenceManager
|
||||||
|
import com.shabinder.common.models.Actions
|
||||||
|
import com.shabinder.common.models.AudioQuality
|
||||||
import com.shabinder.common.models.methods
|
import com.shabinder.common.models.methods
|
||||||
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
|
||||||
|
|
||||||
internal class SpotiFlyerPreferenceStoreProvider(
|
internal class SpotiFlyerPreferenceStoreProvider(
|
||||||
private val storeFactory: StoreFactory,
|
private val storeFactory: StoreFactory,
|
||||||
private val preferenceManager: PreferenceManager
|
private val preferenceManager: PreferenceManager,
|
||||||
|
private val dir: Dir,
|
||||||
|
private val actions: Actions
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun provide(): SpotiFlyerPreferenceStore =
|
fun provide(): SpotiFlyerPreferenceStore =
|
||||||
@ -43,12 +48,16 @@ internal class SpotiFlyerPreferenceStoreProvider(
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
private sealed class Result {
|
private sealed class Result {
|
||||||
data class ToggleAnalytics(val isEnabled: Boolean) : Result()
|
data class AnalyticsToggled(val isEnabled: Boolean) : Result()
|
||||||
|
data class DownloadPathSet(val path: String) : Result()
|
||||||
|
data class PreferredAudioQualityChanged(val quality: AudioQuality) : Result()
|
||||||
}
|
}
|
||||||
|
|
||||||
private inner class ExecutorImpl : SuspendExecutor<Intent, Unit, State, Result, Nothing>() {
|
private inner class ExecutorImpl : SuspendExecutor<Intent, Unit, State, Result, Nothing>() {
|
||||||
override suspend fun executeAction(action: Unit, getState: () -> State) {
|
override suspend fun executeAction(action: Unit, getState: () -> State) {
|
||||||
dispatch(Result.ToggleAnalytics(preferenceManager.isAnalyticsEnabled))
|
dispatch(Result.AnalyticsToggled(preferenceManager.isAnalyticsEnabled))
|
||||||
|
dispatch(Result.PreferredAudioQualityChanged(preferenceManager.audioQuality))
|
||||||
|
dispatch(Result.DownloadPathSet(dir.defaultDir()))
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun executeIntent(intent: Intent, getState: () -> State) {
|
override suspend fun executeIntent(intent: Intent, getState: () -> State) {
|
||||||
@ -57,9 +66,17 @@ internal class SpotiFlyerPreferenceStoreProvider(
|
|||||||
is Intent.GiveDonation -> methods.value.giveDonation()
|
is Intent.GiveDonation -> methods.value.giveDonation()
|
||||||
is Intent.ShareApp -> methods.value.shareApp()
|
is Intent.ShareApp -> methods.value.shareApp()
|
||||||
is Intent.ToggleAnalytics -> {
|
is Intent.ToggleAnalytics -> {
|
||||||
dispatch(Result.ToggleAnalytics(intent.enabled))
|
dispatch(Result.AnalyticsToggled(intent.enabled))
|
||||||
preferenceManager.toggleAnalytics(intent.enabled)
|
preferenceManager.toggleAnalytics(intent.enabled)
|
||||||
}
|
}
|
||||||
|
is Intent.SetDownloadDirectory -> {
|
||||||
|
dispatch(Result.DownloadPathSet(intent.path))
|
||||||
|
preferenceManager.setDownloadDirectory(intent.path)
|
||||||
|
}
|
||||||
|
is Intent.SetPreferredAudioQuality -> {
|
||||||
|
dispatch(Result.PreferredAudioQualityChanged(intent.quality))
|
||||||
|
preferenceManager.setPreferredAudioQuality(intent.quality)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -67,7 +84,9 @@ internal class SpotiFlyerPreferenceStoreProvider(
|
|||||||
private object ReducerImpl : Reducer<State, Result> {
|
private object ReducerImpl : Reducer<State, Result> {
|
||||||
override fun State.reduce(result: Result): State =
|
override fun State.reduce(result: Result): State =
|
||||||
when (result) {
|
when (result) {
|
||||||
is Result.ToggleAnalytics -> copy(isAnalyticsEnabled = result.isEnabled)
|
is Result.AnalyticsToggled -> copy(isAnalyticsEnabled = result.isEnabled)
|
||||||
|
is Result.DownloadPathSet -> copy(downloadPath = result.path)
|
||||||
|
is Result.PreferredAudioQualityChanged -> copy(preferredQuality = result.quality)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@ fun org.jetbrains.kotlin.gradle.dsl.KotlinNativeBinaryContainer.generateFramewor
|
|||||||
export(project(":common:database"))
|
export(project(":common:database"))
|
||||||
export(project(":common:main"))
|
export(project(":common:main"))
|
||||||
export(project(":common:list"))
|
export(project(":common:list"))
|
||||||
|
export(project(":common:preference"))
|
||||||
export(Decompose.decompose)
|
export(Decompose.decompose)
|
||||||
export(MVIKotlin.mvikotlinMain)
|
export(MVIKotlin.mvikotlinMain)
|
||||||
export(MVIKotlin.mvikotlinLogging)
|
export(MVIKotlin.mvikotlinLogging)
|
||||||
@ -65,6 +66,7 @@ kotlin {
|
|||||||
implementation(project(":common:database"))
|
implementation(project(":common:database"))
|
||||||
implementation(project(":common:list"))
|
implementation(project(":common:list"))
|
||||||
implementation(project(":common:main"))
|
implementation(project(":common:main"))
|
||||||
|
implementation(project(":common:preference"))
|
||||||
implementation(SqlDelight.coroutineExtensions)
|
implementation(SqlDelight.coroutineExtensions)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -79,6 +81,7 @@ kotlin {
|
|||||||
api(project(":common:database"))
|
api(project(":common:database"))
|
||||||
api(project(":common:list"))
|
api(project(":common:list"))
|
||||||
api(project(":common:main"))
|
api(project(":common:main"))
|
||||||
|
api(project(":common:preference"))
|
||||||
api(Decompose.decompose)
|
api(Decompose.decompose)
|
||||||
api(MVIKotlin.mvikotlinMain)
|
api(MVIKotlin.mvikotlinMain)
|
||||||
api(MVIKotlin.mvikotlinLogging)
|
api(MVIKotlin.mvikotlinLogging)
|
||||||
|
@ -27,6 +27,7 @@ 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
|
||||||
import com.shabinder.common.models.DownloadStatus
|
import com.shabinder.common.models.DownloadStatus
|
||||||
|
import com.shabinder.common.preference.SpotiFlyerPreference
|
||||||
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 com.shabinder.common.root.integration.SpotiFlyerRootImpl
|
import com.shabinder.common.root.integration.SpotiFlyerRootImpl
|
||||||
@ -45,6 +46,7 @@ interface SpotiFlyerRoot {
|
|||||||
sealed class Child {
|
sealed class Child {
|
||||||
data class Main(val component: SpotiFlyerMain) : Child()
|
data class Main(val component: SpotiFlyerMain) : Child()
|
||||||
data class List(val component: SpotiFlyerList) : Child()
|
data class List(val component: SpotiFlyerList) : Child()
|
||||||
|
data class Preference(val component: SpotiFlyerPreference) : Child()
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Dependencies {
|
interface Dependencies {
|
||||||
|
@ -20,5 +20,5 @@ interface SpotiFlyerRootCallBacks {
|
|||||||
fun searchLink(link: String)
|
fun searchLink(link: String)
|
||||||
fun showToast(text: String)
|
fun showToast(text: String)
|
||||||
fun popBackToHomeScreen()
|
fun popBackToHomeScreen()
|
||||||
fun setDownloadDirectory()
|
fun openPreferenceScreen()
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,7 @@ import com.shabinder.common.main.SpotiFlyerMain
|
|||||||
import com.shabinder.common.models.Actions
|
import com.shabinder.common.models.Actions
|
||||||
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.preference.SpotiFlyerPreference
|
||||||
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.Analytics
|
||||||
import com.shabinder.common.root.SpotiFlyerRoot.Child
|
import com.shabinder.common.root.SpotiFlyerRoot.Child
|
||||||
@ -47,6 +48,7 @@ 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 preference: (ComponentContext, output: Consumer<SpotiFlyerPreference.Output>) -> SpotiFlyerPreference,
|
||||||
private val actions: Actions,
|
private val actions: Actions,
|
||||||
private val analytics: Analytics
|
private val analytics: Analytics
|
||||||
) : SpotiFlyerRoot, ComponentContext by componentContext {
|
) : SpotiFlyerRoot, ComponentContext by componentContext {
|
||||||
@ -57,19 +59,13 @@ internal class SpotiFlyerRootImpl(
|
|||||||
) : this(
|
) : this(
|
||||||
componentContext = componentContext,
|
componentContext = componentContext,
|
||||||
main = { childContext, output ->
|
main = { childContext, output ->
|
||||||
spotiFlyerMain(
|
spotiFlyerMain(childContext, output, dependencies)
|
||||||
childContext,
|
|
||||||
output,
|
|
||||||
dependencies
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
list = { childContext, link, output ->
|
list = { childContext, link, output ->
|
||||||
spotiFlyerList(
|
spotiFlyerList(childContext, link, output, dependencies)
|
||||||
childContext,
|
},
|
||||||
link,
|
preference = { childContext, output ->
|
||||||
output,
|
spotiFlyerPreference(childContext, output, dependencies)
|
||||||
dependencies
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
actions = dependencies.actions.freeze(),
|
actions = dependencies.actions.freeze(),
|
||||||
analytics = dependencies.analytics
|
analytics = dependencies.analytics
|
||||||
@ -95,20 +91,25 @@ internal class SpotiFlyerRootImpl(
|
|||||||
override val callBacks = object : SpotiFlyerRootCallBacks {
|
override val callBacks = object : SpotiFlyerRootCallBacks {
|
||||||
override fun searchLink(link: String) = onMainOutput(SpotiFlyerMain.Output.Search(link))
|
override fun searchLink(link: String) = onMainOutput(SpotiFlyerMain.Output.Search(link))
|
||||||
override fun popBackToHomeScreen() {
|
override fun popBackToHomeScreen() {
|
||||||
if (router.state.value.activeChild.instance is Child.List && router.state.value.backStack.isNotEmpty()) {
|
if (router.state.value.activeChild.instance !is Child.Main && router.state.value.backStack.isNotEmpty()) {
|
||||||
router.popWhile {
|
router.popWhile {
|
||||||
it !is Configuration.Main
|
it !is Configuration.Main
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun openPreferenceScreen() {
|
||||||
|
router.push(Configuration.Preference)
|
||||||
|
}
|
||||||
|
|
||||||
override fun showToast(text: String) { toastState.value = text }
|
override fun showToast(text: String) { toastState.value = text }
|
||||||
override fun setDownloadDirectory() { actions.setDownloadDirectoryAction() }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createChild(configuration: Configuration, componentContext: ComponentContext): Child =
|
private fun createChild(configuration: Configuration, componentContext: ComponentContext): Child =
|
||||||
when (configuration) {
|
when (configuration) {
|
||||||
is Configuration.Main -> Child.Main(main(componentContext, Consumer(::onMainOutput)))
|
is Configuration.Main -> Child.Main(main(componentContext, Consumer(::onMainOutput)))
|
||||||
is Configuration.List -> Child.List(list(componentContext, configuration.link, Consumer(::onListOutput)))
|
is Configuration.List -> Child.List(list(componentContext, configuration.link, Consumer(::onListOutput)))
|
||||||
|
is Configuration.Preference -> Child.Preference(preference(componentContext, Consumer(::onPreferenceOutput)),)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onMainOutput(output: SpotiFlyerMain.Output) =
|
private fun onMainOutput(output: SpotiFlyerMain.Output) =
|
||||||
@ -128,6 +129,15 @@ internal class SpotiFlyerRootImpl(
|
|||||||
analytics.homeScreenVisit()
|
analytics.homeScreenVisit()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
private fun onPreferenceOutput(output: SpotiFlyerPreference.Output): Unit =
|
||||||
|
when (output) {
|
||||||
|
is SpotiFlyerPreference.Output.Finished -> {
|
||||||
|
if (router.state.value.activeChild.instance is Child.Preference && router.state.value.backStack.isNotEmpty()) {
|
||||||
|
router.pop()
|
||||||
|
}
|
||||||
|
Unit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@OptIn(DelicateCoroutinesApi::class)
|
@OptIn(DelicateCoroutinesApi::class)
|
||||||
private fun initAppLaunchAndAuthenticateSpotify(authenticator: suspend () -> Unit) {
|
private fun initAppLaunchAndAuthenticateSpotify(authenticator: suspend () -> Unit) {
|
||||||
@ -142,6 +152,9 @@ internal class SpotiFlyerRootImpl(
|
|||||||
@Parcelize
|
@Parcelize
|
||||||
object Main : Configuration()
|
object Main : Configuration()
|
||||||
|
|
||||||
|
@Parcelize
|
||||||
|
object Preference : Configuration()
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class List(val link: String) : Configuration()
|
data class List(val link: String) : Configuration()
|
||||||
}
|
}
|
||||||
@ -165,3 +178,12 @@ private fun spotiFlyerList(componentContext: ComponentContext, link: String, out
|
|||||||
override val listAnalytics = object : SpotiFlyerList.Analytics, Analytics by analytics {}
|
override val listAnalytics = object : SpotiFlyerList.Analytics, Analytics by analytics {}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private fun spotiFlyerPreference(componentContext: ComponentContext, output: Consumer<SpotiFlyerPreference.Output>, dependencies: Dependencies): SpotiFlyerPreference =
|
||||||
|
SpotiFlyerPreference(
|
||||||
|
componentContext = componentContext,
|
||||||
|
dependencies = object : SpotiFlyerPreference.Dependencies, Dependencies by dependencies {
|
||||||
|
override val prefOutput: Consumer<SpotiFlyerPreference.Output> = output
|
||||||
|
override val preferenceAnalytics = object : SpotiFlyerPreference.Analytics, Analytics by analytics {}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
@ -37,6 +37,7 @@ 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.root.SpotiFlyerRoot
|
import com.shabinder.common.root.SpotiFlyerRoot
|
||||||
|
import com.shabinder.common.translations.Strings
|
||||||
import com.shabinder.common.uikit.configurations.SpotiFlyerColors
|
import com.shabinder.common.uikit.configurations.SpotiFlyerColors
|
||||||
import com.shabinder.common.uikit.configurations.SpotiFlyerShapes
|
import com.shabinder.common.uikit.configurations.SpotiFlyerShapes
|
||||||
import com.shabinder.common.uikit.configurations.SpotiFlyerTypography
|
import com.shabinder.common.uikit.configurations.SpotiFlyerTypography
|
||||||
@ -106,7 +107,7 @@ private fun spotiFlyerRoot(componentContext: ComponentContext): SpotiFlyerRoot =
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setDownloadDirectoryAction() {
|
override fun setDownloadDirectoryAction(callBack: (String) -> Unit) {
|
||||||
val fileChooser = JFileChooser().apply {
|
val fileChooser = JFileChooser().apply {
|
||||||
fileSelectionMode = JFileChooser.DIRECTORIES_ONLY
|
fileSelectionMode = JFileChooser.DIRECTORIES_ONLY
|
||||||
}
|
}
|
||||||
@ -115,9 +116,10 @@ private fun spotiFlyerRoot(componentContext: ComponentContext): SpotiFlyerRoot =
|
|||||||
val directory = fileChooser.selectedFile
|
val directory = fileChooser.selectedFile
|
||||||
if(directory.canWrite()){
|
if(directory.canWrite()){
|
||||||
preferenceManager.setDownloadDirectory(directory.absolutePath)
|
preferenceManager.setDownloadDirectory(directory.absolutePath)
|
||||||
showPopUpMessage("Set New Download Directory:\n${directory.absolutePath}")
|
callBack(dir.defaultDir())
|
||||||
|
showPopUpMessage("${Strings.setDownloadDirectory()} \n${dir.defaultDir()}")
|
||||||
} else {
|
} else {
|
||||||
showPopUpMessage("Cant Write to Selected Directory!")
|
showPopUpMessage(Strings.noWriteAccess("\n${directory.absolutePath} "))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
|
7
fastlane/metadata/android/en-US/changelogs/21.txt
Normal file
7
fastlane/metadata/android/en-US/changelogs/21.txt
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
- YT Quality 128 -> 192 KBPS
|
||||||
|
- Bug Fixes
|
||||||
|
- Better Error Handling, and Bubbling upto the caller
|
||||||
|
- Error Dialog with error info added
|
||||||
|
- YT extraction fixed
|
||||||
|
- Retry on Error Logo Added
|
||||||
|
- Translations added for languages: Locales - de, en, es, fr, id, pt, ru, uk
|
@ -1 +1 @@
|
|||||||
Download All your songs from Spotify, Gaana, Youtube Music.
|
Download All your songs from Spotify, Gaana, Jio Saavn, Youtube Music.
|
@ -73,7 +73,7 @@ class App(props: AppProps): RComponent<AppProps, RState>(props) {
|
|||||||
|
|
||||||
override fun copyToClipboard(text: String) {}
|
override fun copyToClipboard(text: String) {}
|
||||||
|
|
||||||
override fun setDownloadDirectoryAction() {}
|
override fun setDownloadDirectoryAction(callBack: (String) -> Unit) {}
|
||||||
|
|
||||||
override fun queryActiveTracks() {}
|
override fun queryActiveTracks() {}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user