Some more translations, ErrorInfoDialog and Refactoring

This commit is contained in:
shabinder 2021-07-10 10:57:18 +05:30
parent fc9355269a
commit 9881cc55aa
53 changed files with 521 additions and 371 deletions

View File

@ -49,7 +49,7 @@
android:theme="@style/Theme.AppCompat.Light.NoActionBar"
android:icon="@mipmap/ic_launcher"
android:largeHeap="true"
android:label="@string/app_name"
android:label="SpotiFlyer"
android:roundIcon="@mipmap/ic_launcher_round"
android:configChanges="orientation|screenSize"
android:forceDarkAllowed="true"
@ -57,7 +57,7 @@
tools:targetApi="q">
<activity android:name=".MainActivity"
android:launchMode="singleTask"
android:label="@string/app_name"
android:label="SpotiFlyer"
android:hardwareAccelerated="true"
android:theme="@style/Theme.SpotiFlyer"
>

View File

@ -17,6 +17,8 @@
package com.shabinder.spotiflyer
import android.annotation.SuppressLint
import android.content.ClipData
import android.content.ClipboardManager
import android.content.ComponentName
import android.content.Context
import android.content.Intent
@ -31,11 +33,20 @@ import android.os.PowerManager
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.animation.*
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalView
import androidx.core.content.ContextCompat
@ -51,7 +62,10 @@ import com.google.accompanist.insets.ProvideWindowInsets
import com.google.accompanist.insets.navigationBarsPadding
import com.google.accompanist.insets.statusBarsHeight
import com.google.accompanist.insets.statusBarsPadding
import com.shabinder.common.di.*
import com.shabinder.common.di.ConnectionLiveData
import com.shabinder.common.di.Dir
import com.shabinder.common.di.FetchPlatformQueryResult
import com.shabinder.common.di.observeAsState
import com.shabinder.common.di.preference.PreferenceManager
import com.shabinder.common.models.Actions
import com.shabinder.common.models.DownloadStatus
@ -63,18 +77,28 @@ import com.shabinder.common.root.SpotiFlyerRoot
import com.shabinder.common.root.SpotiFlyerRoot.Analytics
import com.shabinder.common.root.callbacks.SpotiFlyerRootCallBacks
import com.shabinder.common.translations.Strings
import com.shabinder.common.uikit.*
import com.shabinder.common.uikit.configurations.SpotiFlyerTheme
import com.shabinder.common.uikit.configurations.colorOffWhite
import com.shabinder.common.uikit.screens.SpotiFlyerRootContent
import com.shabinder.spotiflyer.service.ForegroundService
import com.shabinder.spotiflyer.ui.AnalyticsDialog
import com.shabinder.spotiflyer.ui.NetworkDialog
import com.shabinder.spotiflyer.ui.PermissionDialog
import com.shabinder.spotiflyer.utils.*
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import com.shabinder.spotiflyer.utils.checkAppSignature
import com.shabinder.spotiflyer.utils.checkIfLatestVersion
import com.shabinder.spotiflyer.utils.checkPermissions
import com.shabinder.spotiflyer.utils.disableDozeMode
import com.shabinder.spotiflyer.utils.requestStoragePermission
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.launch
import org.koin.android.ext.android.inject
import org.matomo.sdk.extra.TrackHelper
import java.io.File
@ExperimentalAnimationApi
class MainActivity : ComponentActivity() {
@ -295,6 +319,14 @@ class MainActivity : ComponentActivity() {
startActivity(shareIntent)
}
override fun copyToClipboard(text: String) {
val clipboard = getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText("SpotiFlyer Selection", text)
clipboard.setPrimaryClip(clip)
showPopUpMessage("StackTrace Copied to Clipboard.")
}
override fun openPlatform(packageID: String, platformLink: String) {
val manager: PackageManager = applicationContext.packageManager
try {

View File

@ -27,9 +27,9 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.DialogProperties
import com.shabinder.common.translations.Strings
import com.shabinder.common.uikit.SpotiFlyerShapes
import com.shabinder.common.uikit.SpotiFlyerTypography
import com.shabinder.common.uikit.colorPrimary
import com.shabinder.common.uikit.configurations.SpotiFlyerShapes
import com.shabinder.common.uikit.configurations.SpotiFlyerTypography
import com.shabinder.common.uikit.configurations.colorPrimary
@ExperimentalAnimationApi
@Composable

View File

@ -44,9 +44,9 @@ import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.shabinder.common.translations.Strings
import com.shabinder.common.uikit.SpotiFlyerShapes
import com.shabinder.common.uikit.SpotiFlyerTypography
import com.shabinder.common.uikit.colorOffWhite
import com.shabinder.common.uikit.configurations.SpotiFlyerShapes
import com.shabinder.common.uikit.configurations.SpotiFlyerTypography
import com.shabinder.common.uikit.configurations.colorOffWhite
import kotlinx.coroutines.delay
@ExperimentalAnimationApi
@ -88,7 +88,8 @@ fun NetworkDialog(
) {
Image(Icons.Rounded.CloudOff,
Strings.noInternetConnection(),Modifier.size(42.dp),colorFilter = ColorFilter.tint(
colorOffWhite))
colorOffWhite
))
Spacer(modifier = Modifier.padding(start = 16.dp))
Text(
text = Strings.checkInternetConnection(),

View File

@ -29,9 +29,9 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.shabinder.common.translations.Strings
import com.shabinder.common.uikit.SpotiFlyerShapes
import com.shabinder.common.uikit.SpotiFlyerTypography
import com.shabinder.common.uikit.colorPrimary
import com.shabinder.common.uikit.configurations.SpotiFlyerShapes
import com.shabinder.common.uikit.configurations.SpotiFlyerTypography
import com.shabinder.common.uikit.configurations.colorPrimary
import kotlinx.coroutines.delay
@ExperimentalAnimationApi

View File

@ -33,14 +33,14 @@ allprojects {
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
kotlinOptions {
jvmTarget = "1.8"
freeCompilerArgs = freeCompilerArgs + "-Xopt-in=kotlin.RequiresOptIn"
}
}
afterEvaluate {
project.extensions.findByType<org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension>()?.let { kmpExt ->
kmpExt.sourceSets.run {
all {
languageSettings.useExperimentalAnnotation("kotlinx.serialization.ExperimentalSerializationApi")
languageSettings.useExperimentalAnnotation("kotlin.RequiresOptIn")
languageSettings.useExperimentalAnnotation("kotlinx.serialization.ExperimentalSerializationApi")
}
removeAll { it.name == "androidAndroidTestRelease" }
}

View File

@ -30,13 +30,14 @@ repositories {
}
dependencies {
implementation("com.android.tools.build:gradle:4.1.1")
implementation(Androidx.gradlePlugin)
implementation(JetBrains.Compose.gradlePlugin)
implementation(JetBrains.Kotlin.gradlePlugin)
implementation(JetBrains.Kotlin.serialization)
implementation(SqlDelight.gradlePlugin)
implementation("org.jlleitschuh.gradle:ktlint-gradle:${Versions.ktLint}")
implementation("de.comahe.i18n4k:i18n4k-gradle-plugin:0.1.1")
implementation(KTLint.gradlePlugin)
implementation(Internationalization.gradlePlugin)
implementation(Mosaic.gradlePlugin)
}
kotlin {

View File

@ -19,17 +19,21 @@
object Versions {
// App's Version (To be bumped at each update)
const val versionName = "3.1.0"
const val versionCode = 20
const val versionCode = 20
// Kotlin
const val kotlinVersion = "1.5.10"
const val coroutinesVersion = "1.5.0"
// Code Formatting
const val ktLint = "10.1.0"
// Console-App UI
const val mosaic = "0.1.0"
// DI
const val koin = "3.1.0"
const val koin = "3.1.2"
// Logger
const val kermit = "0.1.9"
@ -45,6 +49,9 @@ object Versions {
const val sqliteJdbcDriver = "3.34.0"
const val slf4j = "1.7.31"
// Internationalisation
const val i18n4k = "0.1.2"
// Android
const val minSdkVersion = 21
const val compileSdkVersion = 29
@ -80,15 +87,11 @@ object Androidx {
const val junit = "androidx.test.ext:junit:1.1.2"
const val expresso = "androidx.test.espresso:espresso-core:3.3.0"
/*object Compose{
const val materialIcon = "androidx.compose.material:material-icons-extended:${Versions.compose}"
const val ui = "androidx.compose.ui:ui:${Versions.compose}"
const val uiGraphics = "androidx.compose.ui:ui-graphics:${Versions.compose}"
const val uiTooling = "androidx.compose.ui:ui-tooling:${Versions.compose}"
const val foundationLayout = "androidx.compose.foundation:foundation-layout:${Versions.compose}"
const val material = "androidx.compose.material:material:${Versions.compose}"
const val runtimeLiveData = "androidx.compose.runtime:runtime-livedata:${Versions.compose}"
}*/
const val gradlePlugin = "com.android.tools.build:gradle:4.1.1"
}
object KTLint {
const val gradlePlugin = "org.jlleitschuh.gradle:ktlint-gradle:${Versions.ktLint}"
}
object JetBrains {
@ -106,7 +109,9 @@ object JetBrains {
const val gradlePlugin = "org.jetbrains.compose:compose-gradle-plugin:$VERSION"
}
}
object Mosaic {
const val gradlePlugin = "com.jakewharton.mosaic:mosaic-gradle-plugin:${Versions.mosaic}"
}
object Decompose {
private const val VERSION = "0.2.6"
const val decompose = "com.arkivanov.decompose:decompose:$VERSION"
@ -146,7 +151,8 @@ object Ktor {
}
object Internationalization {
const val dep = "de.comahe.i18n4k:i18n4k-core:0.1.1"
const val dep = "de.comahe.i18n4k:i18n4k-core:${Versions.i18n4k}"
const val gradlePlugin = "de.comahe.i18n4k:i18n4k-gradle-plugin:${Versions.i18n4k}"
}
object Extras {

View File

@ -0,0 +1,19 @@
package com.shabinder.common.uikit
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.runtime.Composable
@OptIn(ExperimentalAnimationApi::class)
@Composable
actual fun Dialog(
isVisible: Boolean,
onDismiss: () -> Unit,
content: @Composable () -> Unit
) {
AnimatedVisibility(isVisible) {
androidx.compose.ui.window.Dialog(onDismiss) {
content()
}
}
}

View File

@ -20,7 +20,7 @@ actual fun ImageLoad(
link: String,
loader: suspend () -> Picture,
desc: String,
modifier: Modifier,
modifier: Modifier
// placeholder: ImageVector
) {
var pic by remember(link) { mutableStateOf<ImageBitmap?>(null) }

View File

@ -22,24 +22,10 @@ import androidx.compose.foundation.Image
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import com.shabinder.common.database.R
import com.shabinder.common.translations.Strings
import kotlinx.coroutines.flow.MutableStateFlow
actual fun montserratFont() = FontFamily(
Font(R.font.montserrat_light, FontWeight.Light),
Font(R.font.montserrat_regular, FontWeight.Normal),
Font(R.font.montserrat_medium, FontWeight.Medium),
Font(R.font.montserrat_semibold, FontWeight.SemiBold),
)
actual fun pristineFont() = FontFamily(
Font(R.font.pristine_script, FontWeight.Bold)
)
@Composable
actual fun DownloadImageTick() {
Image(
@ -49,10 +35,11 @@ actual fun DownloadImageTick() {
}
@Composable
actual fun DownloadImageError() {
actual fun DownloadImageError(modifier: Modifier) {
Image(
painterResource(R.drawable.ic_error),
Strings.downloadError()
Strings.downloadError(),
modifier = modifier
)
}

View File

@ -1,141 +0,0 @@
package com.shabinder.common.uikit
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.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Card
import androidx.compose.material.Icon
import androidx.compose.material.OutlinedButton
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import com.shabinder.common.models.methods
import com.shabinder.common.translations.Strings
@OptIn(ExperimentalAnimationApi::class)
@Composable
actual fun DonationDialog(
isVisible: Boolean,
onDismiss: () -> Unit,
onSnooze: () -> Unit
) {
AnimatedVisibility(
isVisible
) {
Dialog(onDismiss) {
Card(
modifier = Modifier.fillMaxWidth(),
border = BorderStroke(1.dp, Color.Gray) // Gray
) {
Column(Modifier.padding(16.dp)) {
Text(
Strings.supportUs(),
style = SpotiFlyerTypography.h5,
textAlign = TextAlign.Center,
color = colorAccent,
modifier = Modifier
)
Spacer(modifier = Modifier.padding(vertical = 4.dp))
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth().clickable(
onClick = {
onDismiss()
methods.value.openPlatform("", "https://opencollective.com/spotiflyer/donate")
}
)
.padding(vertical = 6.dp)
) {
Icon(OpenCollectiveLogo(), "Open Collective Logo", Modifier.size(24.dp), tint = Color(0xFFCCCCCC))
Spacer(modifier = Modifier.padding(start = 16.dp))
Column {
Text(
text = "Open Collective",
style = SpotiFlyerTypography.h6
)
Text(
text = Strings.worldWideDonations(),
style = SpotiFlyerTypography.subtitle2
)
}
}
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth().clickable(
onClick = {
onDismiss()
methods.value.openPlatform("", "https://www.paypal.com/paypalme/shabinder")
}
)
.padding(vertical = 6.dp)
) {
Icon(PaypalLogo(), "Paypal Logo", Modifier.size(24.dp), tint = Color(0xFFCCCCCC))
Spacer(modifier = Modifier.padding(start = 16.dp))
Column {
Text(
text = "Paypal",
style = SpotiFlyerTypography.h6
)
Text(
text = Strings.worldWideDonations(),
style = SpotiFlyerTypography.subtitle2
)
}
}
Row(
modifier = Modifier.fillMaxWidth().padding(top = 6.dp)
.clickable(
onClick = {
onDismiss()
methods.value.giveDonation()
}
),
verticalAlignment = Alignment.CenterVertically
) {
Icon(RazorPay(), "Indian Rupee Logo", Modifier.size(24.dp), tint = Color(0xFFCCCCCC))
Spacer(modifier = Modifier.padding(start = 16.dp))
Column {
Text(
text = "RazorPay",
style = SpotiFlyerTypography.h6
)
Text(
text = "${Strings.indianDonations()} (UPI / PayTM / PhonePe / Cards).",
style = SpotiFlyerTypography.subtitle2
)
}
}
Spacer(modifier = Modifier.padding(vertical = 16.dp))
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceEvenly,
modifier = Modifier.padding(horizontal = 4.dp).fillMaxWidth()
) {
OutlinedButton(onClick = onDismiss) {
Text(Strings.dismiss())
}
TextButton(onClick = onSnooze, colors = ButtonDefaults.buttonColors()) {
Text(Strings.remindLater())
}
}
}
}
}
}
}

View File

@ -0,0 +1,17 @@
package com.shabinder.common.uikit.configurations
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import com.shabinder.common.database.R
actual fun montserratFont() = FontFamily(
Font(R.font.montserrat_light, FontWeight.Light),
Font(R.font.montserrat_regular, FontWeight.Normal),
Font(R.font.montserrat_medium, FontWeight.Medium),
Font(R.font.montserrat_semibold, FontWeight.SemiBold),
)
actual fun pristineFont() = FontFamily(
Font(R.font.pristine_script, FontWeight.Bold)
)

View File

@ -0,0 +1,10 @@
package com.shabinder.common.uikit
import androidx.compose.runtime.Composable
@Composable
expect fun Dialog(
isVisible: Boolean,
onDismiss: () -> Unit,
content: @Composable () -> Unit
)

View File

@ -8,7 +8,7 @@ import com.shabinder.common.di.Picture
expect fun ImageLoad(
link: String,
loader: suspend () -> Picture,
desc: String = "Album Art",
modifier: Modifier = Modifier,
desc: String,
modifier: Modifier
// placeholder:Painter = PlaceHolderImage()
)

View File

@ -67,14 +67,7 @@ expect fun RazorPay(): Painter
expect fun HeartIcon(): Painter
@Composable
expect fun DownloadImageError()
expect fun DownloadImageError(modifier: Modifier)
@Composable
expect fun DownloadImageArrow(modifier: Modifier)
@Composable
expect fun DonationDialog(
isVisible: Boolean,
onDismiss: () -> Unit,
onSnooze: () -> Unit
)

View File

@ -14,7 +14,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.shabinder.common.uikit
package com.shabinder.common.uikit.configurations
import androidx.compose.material.darkColors
import androidx.compose.ui.graphics.Color

View File

@ -14,7 +14,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.shabinder.common.uikit
package com.shabinder.common.uikit.configurations
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Shapes

View File

@ -14,7 +14,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.shabinder.common.uikit
package com.shabinder.common.uikit.configurations
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable

View File

@ -14,7 +14,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.shabinder.common.uikit
package com.shabinder.common.uikit.configurations
import androidx.compose.material.Typography
import androidx.compose.ui.graphics.Color

View File

@ -1,17 +1,46 @@
package com.shabinder.common.uikit.dialogs
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Card
import androidx.compose.material.Icon
import androidx.compose.material.OutlinedButton
import androidx.compose.material.Text
import androidx.compose.material.TextButton
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 com.shabinder.common.uikit.DonationDialog
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.shabinder.common.models.methods
import com.shabinder.common.translations.Strings
import com.shabinder.common.uikit.Dialog
import com.shabinder.common.uikit.OpenCollectiveLogo
import com.shabinder.common.uikit.PaypalLogo
import com.shabinder.common.uikit.RazorPay
import com.shabinder.common.uikit.configurations.SpotiFlyerTypography
import com.shabinder.common.uikit.configurations.colorAccent
typealias DonationDialogCallBacks = Triple<openAction,dismissAction,snoozeAction>
private typealias openAction = () -> Unit
private typealias dismissAction = () -> Unit
internal typealias openAction = () -> Unit
internal typealias dismissAction = () -> Unit
private typealias snoozeAction = () -> Unit
@OptIn(ExperimentalAnimationApi::class)
@Composable
fun DonationDialogComponent(onDismissExtra: () -> Unit): DonationDialogCallBacks {
var isDonationDialogVisible by remember { mutableStateOf(false) }
@ -31,3 +60,111 @@ fun DonationDialogComponent(onDismissExtra: () -> Unit): DonationDialogCallBacks
}
return DonationDialogCallBacks(openDonationDialog,dismissDonationDialog,snoozeDonationDialog)
}
@ExperimentalAnimationApi
@Composable
fun DonationDialog(
isVisible: Boolean,
onDismiss: () -> Unit,
onSnooze: () -> Unit
) {
Dialog(isVisible,onDismiss) {
Card(
modifier = Modifier.fillMaxWidth(),
border = BorderStroke(1.dp, Color.Gray) // Gray
) {
Column(Modifier.padding(16.dp)) {
Text(
Strings.supportUs(),
style = SpotiFlyerTypography.h5,
textAlign = TextAlign.Center,
color = colorAccent,
modifier = Modifier
)
Spacer(modifier = Modifier.padding(vertical = 4.dp))
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth().clickable(
onClick = {
onDismiss()
methods.value.openPlatform("", "https://opencollective.com/spotiflyer/donate")
}
)
.padding(vertical = 6.dp)
) {
Icon(OpenCollectiveLogo(), "Open Collective Logo", Modifier.size(24.dp), tint = Color(0xFFCCCCCC))
Spacer(modifier = Modifier.padding(start = 16.dp))
Column {
Text(
text = "Open Collective",
style = SpotiFlyerTypography.h6
)
Text(
text = Strings.worldWideDonations(),
style = SpotiFlyerTypography.subtitle2
)
}
}
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth().clickable(
onClick = {
onDismiss()
methods.value.openPlatform("", "https://www.paypal.com/paypalme/shabinder")
}
)
.padding(vertical = 6.dp)
) {
Icon(PaypalLogo(), "Paypal Logo", Modifier.size(24.dp), tint = Color(0xFFCCCCCC))
Spacer(modifier = Modifier.padding(start = 16.dp))
Column {
Text(
text = "Paypal",
style = SpotiFlyerTypography.h6
)
Text(
text = Strings.worldWideDonations(),
style = SpotiFlyerTypography.subtitle2
)
}
}
Row(
modifier = Modifier.fillMaxWidth().padding(top = 6.dp)
.clickable(
onClick = {
onDismiss()
methods.value.giveDonation()
}
),
verticalAlignment = Alignment.CenterVertically
) {
Icon(RazorPay(), "Indian Rupee Logo", Modifier.size(24.dp), tint = Color(0xFFCCCCCC))
Spacer(modifier = Modifier.padding(start = 16.dp))
Column {
Text(
text = "RazorPay",
style = SpotiFlyerTypography.h6
)
Text(
text = "${Strings.indianDonations()} (UPI / PayTM / PhonePe / Cards).",
style = SpotiFlyerTypography.subtitle2
)
}
}
Spacer(modifier = Modifier.padding(vertical = 16.dp))
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceEvenly,
modifier = Modifier.padding(horizontal = 4.dp).fillMaxWidth()
) {
OutlinedButton(onClick = onDismiss) {
Text(Strings.dismiss())
}
TextButton(onClick = onSnooze, colors = ButtonDefaults.buttonColors()) {
Text(Strings.remindLater())
}
}
}
}
}
}

View File

@ -0,0 +1,79 @@
package com.shabinder.common.uikit.dialogs
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Card
import androidx.compose.material.Text
import androidx.compose.material.TextButton
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.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.shabinder.common.models.methods
import com.shabinder.common.translations.Strings
import com.shabinder.common.uikit.Dialog
import com.shabinder.common.uikit.configurations.SpotiFlyerTypography
import com.shabinder.common.uikit.configurations.colorAccent
typealias ErrorInfoDialogCallBacks = Pair<openAction,dismissAction>
@Composable
fun ErrorInfoDialog(error: Throwable): ErrorInfoDialogCallBacks {
var isErrorDialogVisible by remember { mutableStateOf(false) }
val onDismissDialog = { isErrorDialogVisible = false }
val openErrorDialog = { isErrorDialogVisible = true }
Dialog(isErrorDialogVisible, onDismissDialog) {
Card(
modifier = Modifier.fillMaxWidth(),
border = BorderStroke(1.dp, Color.Gray) // Gray
) {
Column(Modifier.padding(16.dp)) {
Text(
Strings.whatWentWrong(),
style = SpotiFlyerTypography.h5,
textAlign = TextAlign.Center,
color = colorAccent,
modifier = Modifier.padding(vertical = 4.dp).fillMaxWidth()
)
Spacer(Modifier.padding(top = 4.dp))
Text(Strings.copyCodeInGithubIssue(), fontWeight = FontWeight.SemiBold)
SelectionContainer(Modifier.padding(vertical = 8.dp).verticalScroll(rememberScrollState()).weight(1f)) {
Text(error.stackTraceToString(), fontWeight = FontWeight.Light)
}
Row(
Modifier.padding(top = 8.dp).fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceEvenly
) {
TextButton(onClick = onDismissDialog, colors = ButtonDefaults.buttonColors()) {
Text(Strings.dismiss())
}
TextButton(onClick = { methods.value.copyToClipboard(error.stackTraceToString()) }, colors = ButtonDefaults.buttonColors()) {
Text(Strings.copyToClipboard())
}
}
}
}
}
return ErrorInfoDialogCallBacks(openErrorDialog,onDismissDialog)
}

View File

@ -14,8 +14,9 @@
* * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.shabinder.common.uikit
package com.shabinder.common.uikit.screens
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
@ -27,6 +28,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
@ -37,6 +39,8 @@ import androidx.compose.material.ExtendedFloatingActionButton
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Info
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
@ -55,7 +59,20 @@ import com.shabinder.common.models.DownloadStatus
import com.shabinder.common.models.TrackDetails
import com.shabinder.common.models.methods
import com.shabinder.common.translations.Strings
import com.shabinder.common.uikit.DownloadAllImage
import com.shabinder.common.uikit.DownloadImageArrow
import com.shabinder.common.uikit.DownloadImageError
import com.shabinder.common.uikit.DownloadImageTick
import com.shabinder.common.uikit.ImageLoad
import com.shabinder.common.uikit.VerticalScrollbar
import com.shabinder.common.uikit.configurations.SpotiFlyerTypography
import com.shabinder.common.uikit.configurations.appNameStyle
import com.shabinder.common.uikit.configurations.colorAccent
import com.shabinder.common.uikit.configurations.colorPrimary
import com.shabinder.common.uikit.configurations.lightGray
import com.shabinder.common.uikit.dialogs.DonationDialogComponent
import com.shabinder.common.uikit.dialogs.ErrorInfoDialog
import com.shabinder.common.uikit.rememberScrollbarAdapter
@OptIn(ExperimentalMaterialApi::class)
@Composable
@ -133,6 +150,7 @@ fun SpotiFlyerListContent(
}
}
@OptIn(ExperimentalAnimationApi::class)
@Composable
fun TrackCard(
track: TrackDetails,
@ -168,7 +186,19 @@ fun TrackCard(
CircularProgressIndicator()
}
is DownloadStatus.Failed -> {
DownloadImageError()
val (openErrorDialog,dismissErrorDialog) = ErrorInfoDialog((track.downloaded as DownloadStatus.Failed).error)
Icon(Icons.Rounded.Info,Strings.downloadError(),tint = lightGray,modifier = Modifier.size(42.dp).clickable {
openErrorDialog()
}.padding(start = 4.dp,end = 12.dp))
DownloadImageError(
Modifier.clickable(
onClick = {
downloadTrack()
}
)
)
}
is DownloadStatus.Downloading -> {
CircularProgressIndicator(progress = (track.downloaded as DownloadStatus.Downloading).progress.toFloat() / 100f)

View File

@ -14,7 +14,7 @@
* * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.shabinder.common.uikit
package com.shabinder.common.uikit.screens
import androidx.compose.animation.Crossfade
import androidx.compose.foundation.BorderStroke
@ -84,7 +84,23 @@ import com.shabinder.common.main.SpotiFlyerMain.HomeCategory
import com.shabinder.common.models.DownloadRecord
import com.shabinder.common.models.methods
import com.shabinder.common.translations.Strings
import com.shabinder.common.uikit.GaanaLogo
import com.shabinder.common.uikit.GithubLogo
import com.shabinder.common.uikit.ImageLoad
import com.shabinder.common.uikit.SaavnLogo
import com.shabinder.common.uikit.ShareImage
import com.shabinder.common.uikit.SpotifyLogo
import com.shabinder.common.uikit.VerticalScrollbar
import com.shabinder.common.uikit.YoutubeLogo
import com.shabinder.common.uikit.YoutubeMusicLogo
import com.shabinder.common.uikit.configurations.SpotiFlyerShapes
import com.shabinder.common.uikit.configurations.SpotiFlyerTypography
import com.shabinder.common.uikit.configurations.colorAccent
import com.shabinder.common.uikit.configurations.colorOffWhite
import com.shabinder.common.uikit.configurations.colorPrimary
import com.shabinder.common.uikit.configurations.transparent
import com.shabinder.common.uikit.dialogs.DonationDialogComponent
import com.shabinder.common.uikit.rememberScrollbarAdapter
@Composable
fun SpotiFlyerMainContent(component: SpotiFlyerMain) {

View File

@ -16,7 +16,7 @@
@file:Suppress("EXPERIMENTAL_API_USAGE")
package com.shabinder.common.uikit
package com.shabinder.common.uikit.screens
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.ExperimentalAnimationApi
@ -60,8 +60,13 @@ import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState
import com.shabinder.common.root.SpotiFlyerRoot
import com.shabinder.common.root.SpotiFlyerRoot.Child
import com.shabinder.common.translations.Strings
import com.shabinder.common.uikit.splash.Splash
import com.shabinder.common.uikit.splash.SplashState
import com.shabinder.common.uikit.SpotiFlyerLogo
import com.shabinder.common.uikit.Toast
import com.shabinder.common.uikit.ToastDuration
import com.shabinder.common.uikit.configurations.appNameStyle
import com.shabinder.common.uikit.configurations.colorPrimaryDark
import com.shabinder.common.uikit.screens.splash.Splash
import com.shabinder.common.uikit.screens.splash.SplashState
import com.shabinder.common.uikit.utils.verticalGradientScrim
// To Not Show Splash Again After Configuration Change in Android

View File

@ -14,7 +14,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.shabinder.common.uikit.splash
package com.shabinder.common.uikit.screens.splash
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
@ -38,9 +38,9 @@ import androidx.compose.ui.unit.sp
import com.shabinder.common.translations.Strings
import com.shabinder.common.uikit.HeartIcon
import com.shabinder.common.uikit.SpotiFlyerLogo
import com.shabinder.common.uikit.SpotiFlyerTypography
import com.shabinder.common.uikit.colorAccent
import com.shabinder.common.uikit.colorPrimary
import com.shabinder.common.uikit.configurations.SpotiFlyerTypography
import com.shabinder.common.uikit.configurations.colorAccent
import com.shabinder.common.uikit.configurations.colorPrimary
import kotlinx.coroutines.delay
private const val SplashWaitTime: Long = 2000

View File

@ -0,0 +1,19 @@
package com.shabinder.common.uikit
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.runtime.Composable
@OptIn(ExperimentalAnimationApi::class)
@Composable
actual fun Dialog(
isVisible: Boolean,
onDismiss: () -> Unit,
content: @Composable () -> Unit
) {
AnimatedVisibility(isVisible) {
androidx.compose.ui.window.v1.Dialog(onDismiss) {
content()
}
}
}

View File

@ -20,7 +20,7 @@ actual fun ImageLoad(
link: String,
loader: suspend () -> Picture,
desc: String,
modifier: Modifier,
modifier: Modifier
// placeholder: ImageVector
) {
var pic by remember(link) { mutableStateOf<ImageBitmap?>(null) }

View File

@ -19,15 +19,10 @@ package com.shabinder.common.uikit
import androidx.compose.foundation.Image
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.graphics.vector.rememberVectorPainter
import androidx.compose.ui.res.vectorXmlResource
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.platform.Font
@Composable
actual fun DownloadImageTick() {
@ -37,22 +32,12 @@ actual fun DownloadImageTick() {
)
}
actual fun montserratFont() = FontFamily(
Font("font/montserrat_light.ttf", FontWeight.Light),
Font("font/montserrat_regular.ttf", FontWeight.Normal),
Font("font/montserrat_medium.ttf", FontWeight.Medium),
Font("font/montserrat_semibold.ttf", FontWeight.SemiBold),
)
actual fun pristineFont() = FontFamily(
Font("font/pristine_script.ttf", FontWeight.Bold)
)
@Composable
actual fun DownloadImageError() {
actual fun DownloadImageError(modifier: Modifier) {
Image(
vectorXmlResource("drawable/ic_error.xml"),
"Can't Download"
"Can't Download",
modifier = modifier
)
}

View File

@ -39,6 +39,8 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.shabinder.common.uikit.configurations.SpotiFlyerTypography
import com.shabinder.common.uikit.configurations.colorOffWhite
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow

View File

@ -1,102 +0,0 @@
package com.shabinder.common.uikit
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.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.Card
import androidx.compose.material.Icon
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.v1.Dialog
import com.shabinder.common.models.methods
import com.shabinder.common.translations.Strings
@OptIn(ExperimentalAnimationApi::class, androidx.compose.ui.ExperimentalComposeUiApi::class)
@Composable
actual fun DonationDialog(
isVisible: Boolean,
onDismiss: () -> Unit,
onSnooze: () -> Unit
) {
AnimatedVisibility(
isVisible
) {
Dialog(onDismiss) {
Card(
modifier = Modifier.fillMaxSize(),
border = BorderStroke(1.dp, Color.Gray) // Gray
) {
Column(Modifier.padding(16.dp)) {
Text(
Strings.supportUs(),
style = SpotiFlyerTypography.h5,
textAlign = TextAlign.Center,
color = colorAccent,
modifier = Modifier
)
Spacer(modifier = Modifier.padding(vertical = 4.dp))
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth().clickable(
onClick = {
onDismiss()
methods.value.openPlatform("", "https://www.paypal.com/paypalme/shabinder")
}
)
.padding(vertical = 6.dp)
) {
Icon(PaypalLogo(), "Paypal Logo", tint = Color(0xFFCCCCCC))
Spacer(modifier = Modifier.padding(start = 16.dp))
Column {
Text(
text = "Paypal",
style = SpotiFlyerTypography.h6
)
Text(
text = Strings.worldWideDonations(),
style = SpotiFlyerTypography.subtitle2
)
}
}
Row(
modifier = Modifier.fillMaxWidth().padding(top = 6.dp)
.clickable(
onClick = {
onDismiss()
methods.value.giveDonation()
}
),
verticalAlignment = Alignment.CenterVertically
) {
Icon(RazorPay(), "Indian Rupee Logo", Modifier.size(32.dp), tint = Color(0xFFCCCCCC))
Spacer(modifier = Modifier.padding(start = 16.dp))
Column {
Text(
text = "RazorPay",
style = SpotiFlyerTypography.h6
)
Text(
text = "${Strings.indianDonations()} (UPI / PayTM / PhonePe / Cards).",
style = SpotiFlyerTypography.subtitle2
)
}
}
}
}
}
}
}

View File

@ -0,0 +1,17 @@
package com.shabinder.common.uikit.configurations
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.platform.Font
actual fun montserratFont() = FontFamily(
Font("font/montserrat_light.ttf", FontWeight.Light),
Font("font/montserrat_regular.ttf", FontWeight.Normal),
Font("font/montserrat_medium.ttf", FontWeight.Medium),
Font("font/montserrat_semibold.ttf", FontWeight.SemiBold),
)
actual fun pristineFont() = FontFamily(
Font("font/pristine_script.ttf", FontWeight.Bold)
)

View File

@ -31,7 +31,7 @@ val statelyIsoVersion = "1.1.7-a1"
i18n4k {
inputDirectory = "../../translations"
packageName = "com.shabinder.common.translations"
// sourceCodeLocales = listOf("en", "de")
// sourceCodeLocales = listOf("en", "de", "es", "fr", "id", "pt", "ru", "uk")
}
kotlin {

View File

@ -36,6 +36,9 @@ interface Actions {
// Share SpotiFlyer App
fun shareApp()
// Copy to Clipboard
fun copyToClipboard(text: String)
// Open / Redirect to another Platform
fun openPlatform(packageID: String, platformLink: String)
fun writeMp3Tags(trackDetails: TrackDetails)
@ -48,6 +51,8 @@ private fun stubActions(): Actions = object : Actions {
override fun queryActiveTracks() {}
override fun giveDonation() {}
override fun shareApp() {}
override fun copyToClipboard(text: String) {}
override fun openPlatform(packageID: String, platformLink: String) {}
override fun writeMp3Tags(trackDetails: TrackDetails) {}

View File

@ -32,8 +32,8 @@ sealed class SpotiFlyerException(override val message: String): Exception(messag
val jioSaavnError: Throwable,
val ytMusicError: Throwable,
override val message: String = "${Strings.noLinkFound()}: $trackName," +
" \n JioSaavn Error's StackTrace: ${jioSaavnError.stackTraceToString()} \n " +
" \n YtMusic Error's StackTrace: ${ytMusicError.stackTraceToString()} \n "
" \n YtMusic Error's StackTrace: ${ytMusicError.stackTraceToString()} \n " +
" \n JioSaavn Error's StackTrace: ${jioSaavnError.stackTraceToString()} \n "
): SpotiFlyerException(message)
data class LinkInvalid(

View File

@ -1,3 +1,5 @@
@file:Suppress("UNCHECKED_CAST")
package com.shabinder.common.models.event.coroutines
import kotlin.properties.ReadOnlyProperty

View File

@ -101,6 +101,7 @@ class FetchPlatformQueryResult(
}
Source.YouTube -> {
youtubeMp3.getMp3DownloadLink(track.videoID.requireNotNull()).flatMapError {
logger.e("Yt1sMp3 Failed") { it.message ?: "couldn't fetch link for ${track.videoID} ,trying manual extraction" }
youtubeProvider.ytDownloader.getVideo(track.videoID!!).get()?.url?.let { m4aLink ->
audioToMp3.convertToMp3(m4aLink)
} ?: throw SpotiFlyerException.YoutubeLinkNotFound(track.videoID)
@ -130,8 +131,8 @@ class FetchPlatformQueryResult(
SuspendableEvent.error(
SpotiFlyerException.DownloadLinkFetchFailed(
trackName = track.title,
jioSaavnError = saavnError,
ytMusicError = ytMusicError
ytMusicError = ytMusicError,
jioSaavnError = saavnError
)
)
}

View File

@ -303,7 +303,7 @@ class YoutubeMusic constructor(
}
// logger.d("YT Api Result"){"$trackName - $linksWithMatchValue"}
return linksWithMatchValue.toList().sortedByDescending { it.second }.toMap().also {
logger.d(tag) { "Match Found for $trackName - ${!it.isNullOrEmpty()}" }
logger.d(tag) { "Match Found for $trackName - ${!it.isNullOrEmpty()} ${it.keys.firstOrNull() ?: ""}" }
}
}

View File

@ -243,7 +243,7 @@ interface JioSaavnRequests {
// Skip this Result if No Word is Common in Name
if (!hasCommonWord) {
logger.i("Saavn Removing Common Word") { result.toString() }
// logger.i("Saavn Removing Common Word") { result.toString() }
continue
}
@ -264,7 +264,7 @@ interface JioSaavnRequests {
}
if (artistMatchNumber == 0) {
logger.i("Artist Match Saavn Removing") { result.toString() }
// logger.i("Artist Match Saavn Removing") { result.toString() }
continue
}
val artistMatch: Float = (artistMatchNumber.toFloat() / trackArtists.size) * 100
@ -274,11 +274,12 @@ interface JioSaavnRequests {
linksWithMatchValue[result.id] = avgMatch
}
return linksWithMatchValue.toList().sortedByDescending { it.second }.toMap().also {
logger.i { "Match Found for $trackName - ${!it.isNullOrEmpty()}" }
logger.i(TAG) { "Match Found for $trackName - ${!it.isNullOrEmpty()} ${it.keys.firstOrNull() ?: ""}" }
}
}
companion object {
const val TAG = "Saavn Request"
// EndPoints
val search_base_url = "${corsApi}https://www.jiosaavn.com/api.php?__call=autocomplete.get&_format=json&_marker=0&cc=in&includeMetaTags=1&query="
val song_details_base_url = "${corsApi}https://www.jiosaavn.com/api.php?__call=song.getDetails&cc=in&_marker=0%3F_marker%3D0&_format=json&pids="

View File

@ -76,6 +76,7 @@ internal class SpotiFlyerRootImpl(
) {
instanceKeeper.ensureNeverFrozen()
methods.value = dependencies.actions.freeze()
/*Init App Launch & Authenticate Spotify Client*/
initAppLaunchAndAuthenticateSpotify(dependencies.fetchQuery::authenticateSpotifyClient)
}

View File

@ -1,5 +1,5 @@
plugins {
kotlin("jvm")// version "1.4.32"
kotlin("jvm")
kotlin("plugin.serialization")
id("ktlint-setup")
id("com.jakewharton.mosaic")

View File

@ -5,7 +5,7 @@ import com.jakewharton.mosaic.Text
import com.jakewharton.mosaic.runMosaic
import kotlinx.coroutines.delay
fun main(/*args: Array<String>*/) = runMosaic {
fun main() = runMosaic {
// TODO https://github.com/JakeWharton/mosaic/issues/3
var count by mutableStateOf(0)

View File

@ -37,21 +37,25 @@ import com.shabinder.common.models.Actions
import com.shabinder.common.models.PlatformActions
import com.shabinder.common.models.TrackDetails
import com.shabinder.common.root.SpotiFlyerRoot
import com.shabinder.common.uikit.SpotiFlyerColors
import com.shabinder.common.uikit.SpotiFlyerRootContent
import com.shabinder.common.uikit.SpotiFlyerShapes
import com.shabinder.common.uikit.SpotiFlyerTypography
import com.shabinder.common.uikit.colorOffWhite
import com.shabinder.common.uikit.configurations.SpotiFlyerColors
import com.shabinder.common.uikit.configurations.SpotiFlyerShapes
import com.shabinder.common.uikit.configurations.SpotiFlyerTypography
import com.shabinder.common.uikit.configurations.colorOffWhite
import com.shabinder.common.uikit.screens.SpotiFlyerRootContent
import com.shabinder.database.Database
import kotlinx.coroutines.runBlocking
import org.piwik.java.tracking.PiwikTracker
import utils.trackAsync
import utils.trackScreenAsync
import java.awt.Desktop
import java.awt.Toolkit
import java.awt.datatransfer.Clipboard
import java.awt.datatransfer.StringSelection
import java.net.URI
import javax.swing.JFileChooser
import javax.swing.JFileChooser.APPROVE_OPTION
private val koin = initKoin(enableNetworkLogs = true).koin
private lateinit var showToast: (String)->Unit
private val tracker: PiwikTracker by lazy {
@ -129,6 +133,11 @@ private fun spotiFlyerRoot(componentContext: ComponentContext): SpotiFlyerRoot =
}
override fun shareApp() = openLink("https://github.com/Shabinder/SpotiFlyer")
override fun copyToClipboard(text: String) {
val data = StringSelection(text)
val cb: Clipboard = Toolkit.getDefaultToolkit().systemClipboard
cb.setContents(data, data)
}
override fun openPlatform(packageID: String, platformLink: String) = openLink(platformLink)

View File

@ -31,11 +31,3 @@ include(
":console-app",
":maintenance-tasks"
)
includeBuild("mosaic/mosaic") {
dependencySubstitution {
substitute(module("com.jakewharton.mosaic:mosaic-gradle-plugin")).with(project(":mosaic-gradle-plugin"))
substitute(module("com.jakewharton.mosaic:mosaic-runtime")).with(project(":mosaic-runtime"))
substitute(module("com.jakewharton.mosaic:compose-compiler")).with(project(":compose:compiler"))
}
}

View File

@ -15,6 +15,9 @@ supportDeveloper = Entwickler untestützen
donateDescription = Wenn du denkst, dass ich verdiene für meine Arbeit bezahlt zu werden, können sie mich hier unterstützen.If you think I deserve to get paid for my work, you can support me here.
share = Teilen
shareDescription = Teile diese App mit deiner Familie und deinen Freunden.
whatWentWrong = Was schief gelaufen ist...
copyToClipboard = In die Zwischenablage kopieren
copyCodeInGithubIssue = Kopieren Sie Einfügen unterhalb des Codes, während Sie ein Github-Problem erstellen / Dieses Problem melden, um bessere Hilfe zu erhalten.
status = Status
analytics = Analyse
analyticsDescription = Deine Daten sind Anonym und werden nie mit Drittanbietern geteilt.

View File

@ -15,6 +15,9 @@ supportDeveloper = Support Developer
donateDescription = If you think I deserve to get paid for my work, you can support me here.
share = Share
shareDescription = Share this app with your friends and family.
whatWentWrong = What Went Wrong...
copyToClipboard = Copy to Clipboard
copyCodeInGithubIssue = Copy Paste Below Code while creating Github Issue / Reporting this issue for better help.
status = Status
analytics = Analytics
analyticsDescription = Your Data is Anonymized and never shared with 3rd party service.

View File

@ -15,6 +15,9 @@ supportDeveloper = Apoya al desarollador
donateDescription = Si crees que merezco una paga por mi trabajo, aquí puedes apoyarme.
share = Comparte
shareDescription = Comparte esta aplicación con tus familiares y amigos.
whatWentWrong = Qué salió mal...
copyToClipboard = Copiar al portapapeles
copyCodeInGithubIssue = Copie Pegar debajo del código mientras crea Github Issue / Reportando este problema para obtener una mejor ayuda.
status = Estatus
analytics = Analiticos
analyticsDescription = Tus datos son anonimizados y nunca se compartiran con servicios de terceros.

View File

@ -15,6 +15,9 @@ supportDeveloper = Soutenir le Développeur
donateDescription = Si vous pensez que je mérite d'être payé pour mon travail, vous pouvez me soutenir ici.
share = Partager
shareDescription = Partagez cette application avec vos amis et votre famille.
whatWentWrong = Qu'est ce qui ne s'est pas bien passé...
copyToClipboard = Copier dans le presse-papier
copyCodeInGithubIssue = Copiez et collez le code ci-dessous lors de la création d'un problème Github / Signalement de ce problème pour une meilleure aide.
status = Statut
analytics = Analyses
analyticsDescription = Vos données sont anonymes et ne sont jamais partagées avec des services tiers.

View File

@ -15,6 +15,9 @@ supportDeveloper = Dukung Developer
donateDescription = Jika Anda ingin memberi donasi ke developer apl. ini, Anda bisa donasi disini.
share = Bagikan
shareDescription = Bagikan aplikasi ini kepada teman / keluarga anda.
whatWentWrong = Apa yang salah...
copyToClipboard = Menyalin ke clipboard
copyCodeInGithubIssue = Salin Tempel Kode Di Bawah Saat membuat Masalah Github / Melaporkan masalah ini untuk bantuan yang lebih baik.
status = Status
analytics = Analytics
analyticsDescription = Data Anda tidak dapat dilihat oleh orang lain dan TIDAK akan dibagikan ke pihak ketiga.

View File

@ -15,6 +15,9 @@ supportDeveloper = Ajude o Desenvolvedor
donateDescription = Se você acha que mereço ser pago pelo meu trabalho, você pode me ajudar aqui.
share = Compartilhar
shareDescription = Compartilhe este app com seus amigos e família.
whatWentWrong = O que deu errado...
copyToClipboard = Copiar para área de transferência
copyCodeInGithubIssue = Copiar e colar abaixo do código ao criar o problema no Github / relatar esse problema para obter ajuda melhor.
status = Status
analytics = Estatísticas
analyticsDescription = Seus dados serão tornados anônimos e nunca serão compartilhados com serviços de terceiros.

View File

@ -15,6 +15,9 @@ supportDeveloper = Поддержи разработчика
donateDescription = Если считаешь, что я заслуживаю этого, ты можешь поддержать меня здесь.
share = Поделиться
shareDescription = Расскажи об этом приложении знакомым - поделись полезным инструментом.
whatWentWrong = Что пошло не так...
copyToClipboard = Скопировать в буфер обмена
copyCodeInGithubIssue = Скопируйте вставьте ниже код при создании проблемы Github / Сообщите об этой проблеме для лучшей помощи.
status = Статус
analytics = Аналитика
analyticsDescription = Предоставить анонимные данные для улучшения приложения. Они не будут переданы третьим лицам.

View File

@ -15,6 +15,9 @@ supportDeveloper = Підтримати розробника
donateDescription = Якщо ви вважаєте, що я заслуговую на отримання грошей за свою роботу, ви можете підтримати мене тут.
share = Поділитися
shareDescription = Поділися цією програмою зі своїми друзями та родиною.
whatWentWrong = Що пішло не так...
copyToClipboard = Копіювати в буфер обміну
copyCodeInGithubIssue = Скопіюйте вставку нижче коду під час створення випуску Github / звітування про цю проблему, щоб отримати кращу допомогу.
status = Статус
analytics = Аналіз
analyticsDescription = Ваші дані анонімні та ніколи не передаються стороннім сервісам.

View File

@ -71,6 +71,8 @@ class App(props: AppProps): RComponent<AppProps, RState>(props) {
/*TODO("Not yet implemented")*/
}
override fun copyToClipboard(text: String) {}
override fun setDownloadDirectoryAction() {}
override fun queryActiveTracks() {}