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

View File

@ -17,6 +17,8 @@
package com.shabinder.spotiflyer package com.shabinder.spotiflyer
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.ClipData
import android.content.ClipboardManager
import android.content.ComponentName import android.content.ComponentName
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
@ -31,11 +33,20 @@ import android.os.PowerManager
import android.util.Log import android.util.Log
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.compose.animation.* import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.Box
import androidx.compose.material.* import androidx.compose.foundation.layout.Spacer
import androidx.compose.runtime.* 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.Modifier
import androidx.compose.ui.platform.LocalView import androidx.compose.ui.platform.LocalView
import androidx.core.content.ContextCompat 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.navigationBarsPadding
import com.google.accompanist.insets.statusBarsHeight import com.google.accompanist.insets.statusBarsHeight
import com.google.accompanist.insets.statusBarsPadding 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.di.preference.PreferenceManager
import com.shabinder.common.models.Actions import com.shabinder.common.models.Actions
import com.shabinder.common.models.DownloadStatus 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.SpotiFlyerRoot.Analytics
import com.shabinder.common.root.callbacks.SpotiFlyerRootCallBacks import com.shabinder.common.root.callbacks.SpotiFlyerRootCallBacks
import com.shabinder.common.translations.Strings 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.service.ForegroundService
import com.shabinder.spotiflyer.ui.AnalyticsDialog import com.shabinder.spotiflyer.ui.AnalyticsDialog
import com.shabinder.spotiflyer.ui.NetworkDialog import com.shabinder.spotiflyer.ui.NetworkDialog
import com.shabinder.spotiflyer.ui.PermissionDialog import com.shabinder.spotiflyer.ui.PermissionDialog
import com.shabinder.spotiflyer.utils.* import com.shabinder.spotiflyer.utils.checkAppSignature
import kotlinx.coroutines.* import com.shabinder.spotiflyer.utils.checkIfLatestVersion
import kotlinx.coroutines.flow.* 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.koin.android.ext.android.inject
import org.matomo.sdk.extra.TrackHelper import org.matomo.sdk.extra.TrackHelper
import java.io.File import java.io.File
@ExperimentalAnimationApi @ExperimentalAnimationApi
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
@ -295,6 +319,14 @@ class MainActivity : ComponentActivity() {
startActivity(shareIntent) 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) { override fun openPlatform(packageID: String, platformLink: String) {
val manager: PackageManager = applicationContext.packageManager val manager: PackageManager = applicationContext.packageManager
try { try {

View File

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

View File

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

View File

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

View File

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

View File

@ -19,17 +19,21 @@
object Versions { object Versions {
// App's Version (To be bumped at each update) // App's Version (To be bumped at each update)
const val versionName = "3.1.0" const val versionName = "3.1.0"
const val versionCode = 20
const val versionCode = 20
// Kotlin // Kotlin
const val kotlinVersion = "1.5.10" const val kotlinVersion = "1.5.10"
const val coroutinesVersion = "1.5.0" const val coroutinesVersion = "1.5.0"
// Code Formatting // Code Formatting
const val ktLint = "10.1.0" const val ktLint = "10.1.0"
// Console-App UI
const val mosaic = "0.1.0"
// DI // DI
const val koin = "3.1.0" const val koin = "3.1.2"
// Logger // Logger
const val kermit = "0.1.9" const val kermit = "0.1.9"
@ -45,6 +49,9 @@ object Versions {
const val sqliteJdbcDriver = "3.34.0" const val sqliteJdbcDriver = "3.34.0"
const val slf4j = "1.7.31" const val slf4j = "1.7.31"
// Internationalisation
const val i18n4k = "0.1.2"
// Android // Android
const val minSdkVersion = 21 const val minSdkVersion = 21
const val compileSdkVersion = 29 const val compileSdkVersion = 29
@ -80,15 +87,11 @@ object Androidx {
const val junit = "androidx.test.ext:junit:1.1.2" const val junit = "androidx.test.ext:junit:1.1.2"
const val expresso = "androidx.test.espresso:espresso-core:3.3.0" const val expresso = "androidx.test.espresso:espresso-core:3.3.0"
/*object Compose{ const val gradlePlugin = "com.android.tools.build:gradle:4.1.1"
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}" object KTLint {
const val uiTooling = "androidx.compose.ui:ui-tooling:${Versions.compose}" const val gradlePlugin = "org.jlleitschuh.gradle:ktlint-gradle:${Versions.ktLint}"
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}"
}*/
} }
object JetBrains { object JetBrains {
@ -106,7 +109,9 @@ object JetBrains {
const val gradlePlugin = "org.jetbrains.compose:compose-gradle-plugin:$VERSION" 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 { object Decompose {
private const val VERSION = "0.2.6" private const val VERSION = "0.2.6"
const val decompose = "com.arkivanov.decompose:decompose:$VERSION" const val decompose = "com.arkivanov.decompose:decompose:$VERSION"
@ -146,7 +151,8 @@ object Ktor {
} }
object Internationalization { 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 { 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, link: String,
loader: suspend () -> Picture, loader: suspend () -> Picture,
desc: String, desc: String,
modifier: Modifier, modifier: Modifier
// placeholder: ImageVector // placeholder: ImageVector
) { ) {
var pic by remember(link) { mutableStateOf<ImageBitmap?>(null) } 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.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource 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.database.R
import com.shabinder.common.translations.Strings import com.shabinder.common.translations.Strings
import kotlinx.coroutines.flow.MutableStateFlow 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 @Composable
actual fun DownloadImageTick() { actual fun DownloadImageTick() {
Image( Image(
@ -49,10 +35,11 @@ actual fun DownloadImageTick() {
} }
@Composable @Composable
actual fun DownloadImageError() { actual fun DownloadImageError(modifier: Modifier) {
Image( Image(
painterResource(R.drawable.ic_error), 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( expect fun ImageLoad(
link: String, link: String,
loader: suspend () -> Picture, loader: suspend () -> Picture,
desc: String = "Album Art", desc: String,
modifier: Modifier = Modifier, modifier: Modifier
// placeholder:Painter = PlaceHolderImage() // placeholder:Painter = PlaceHolderImage()
) )

View File

@ -67,14 +67,7 @@ expect fun RazorPay(): Painter
expect fun HeartIcon(): Painter expect fun HeartIcon(): Painter
@Composable @Composable
expect fun DownloadImageError() expect fun DownloadImageError(modifier: Modifier)
@Composable @Composable
expect fun DownloadImageArrow(modifier: Modifier) 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/>. * 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.material.darkColors
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color

View File

@ -14,7 +14,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * 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.foundation.shape.RoundedCornerShape
import androidx.compose.material.Shapes import androidx.compose.material.Shapes

View File

@ -14,7 +14,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * 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.material.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable

View File

@ -14,7 +14,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * 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.material.Typography
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color

View File

@ -1,17 +1,46 @@
package com.shabinder.common.uikit.dialogs 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.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue 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> typealias DonationDialogCallBacks = Triple<openAction,dismissAction,snoozeAction>
private typealias openAction = () -> Unit internal typealias openAction = () -> Unit
private typealias dismissAction = () -> Unit internal typealias dismissAction = () -> Unit
private typealias snoozeAction = () -> Unit private typealias snoozeAction = () -> Unit
@OptIn(ExperimentalAnimationApi::class)
@Composable @Composable
fun DonationDialogComponent(onDismissExtra: () -> Unit): DonationDialogCallBacks { fun DonationDialogComponent(onDismissExtra: () -> Unit): DonationDialogCallBacks {
var isDonationDialogVisible by remember { mutableStateOf(false) } var isDonationDialogVisible by remember { mutableStateOf(false) }
@ -31,3 +60,111 @@ fun DonationDialogComponent(onDismissExtra: () -> Unit): DonationDialogCallBacks
} }
return DonationDialogCallBacks(openDonationDialog,dismissDonationDialog,snoozeDonationDialog) 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/>. * * 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.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box 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.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.itemsIndexed
@ -37,6 +39,8 @@ import androidx.compose.material.ExtendedFloatingActionButton
import androidx.compose.material.Icon import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text 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.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue 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.TrackDetails
import com.shabinder.common.models.methods import com.shabinder.common.models.methods
import com.shabinder.common.translations.Strings 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.DonationDialogComponent
import com.shabinder.common.uikit.dialogs.ErrorInfoDialog
import com.shabinder.common.uikit.rememberScrollbarAdapter
@OptIn(ExperimentalMaterialApi::class) @OptIn(ExperimentalMaterialApi::class)
@Composable @Composable
@ -133,6 +150,7 @@ fun SpotiFlyerListContent(
} }
} }
@OptIn(ExperimentalAnimationApi::class)
@Composable @Composable
fun TrackCard( fun TrackCard(
track: TrackDetails, track: TrackDetails,
@ -168,7 +186,19 @@ fun TrackCard(
CircularProgressIndicator() CircularProgressIndicator()
} }
is DownloadStatus.Failed -> { 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 -> { is DownloadStatus.Downloading -> {
CircularProgressIndicator(progress = (track.downloaded as DownloadStatus.Downloading).progress.toFloat() / 100f) 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/>. * * 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.animation.Crossfade
import androidx.compose.foundation.BorderStroke 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.DownloadRecord
import com.shabinder.common.models.methods import com.shabinder.common.models.methods
import com.shabinder.common.translations.Strings 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.dialogs.DonationDialogComponent
import com.shabinder.common.uikit.rememberScrollbarAdapter
@Composable @Composable
fun SpotiFlyerMainContent(component: SpotiFlyerMain) { fun SpotiFlyerMainContent(component: SpotiFlyerMain) {

View File

@ -16,7 +16,7 @@
@file:Suppress("EXPERIMENTAL_API_USAGE") @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.AnimatedVisibility
import androidx.compose.animation.ExperimentalAnimationApi 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
import com.shabinder.common.root.SpotiFlyerRoot.Child import com.shabinder.common.root.SpotiFlyerRoot.Child
import com.shabinder.common.translations.Strings import com.shabinder.common.translations.Strings
import com.shabinder.common.uikit.splash.Splash import com.shabinder.common.uikit.SpotiFlyerLogo
import com.shabinder.common.uikit.splash.SplashState 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 import com.shabinder.common.uikit.utils.verticalGradientScrim
// To Not Show Splash Again After Configuration Change in Android // 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/>. * 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.Image
import androidx.compose.foundation.layout.Arrangement 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.translations.Strings
import com.shabinder.common.uikit.HeartIcon import com.shabinder.common.uikit.HeartIcon
import com.shabinder.common.uikit.SpotiFlyerLogo import com.shabinder.common.uikit.SpotiFlyerLogo
import com.shabinder.common.uikit.SpotiFlyerTypography import com.shabinder.common.uikit.configurations.SpotiFlyerTypography
import com.shabinder.common.uikit.colorAccent import com.shabinder.common.uikit.configurations.colorAccent
import com.shabinder.common.uikit.colorPrimary import com.shabinder.common.uikit.configurations.colorPrimary
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
private const val SplashWaitTime: Long = 2000 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, link: String,
loader: suspend () -> Picture, loader: suspend () -> Picture,
desc: String, desc: String,
modifier: Modifier, modifier: Modifier
// placeholder: ImageVector // placeholder: ImageVector
) { ) {
var pic by remember(link) { mutableStateOf<ImageBitmap?>(null) } 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.foundation.Image
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.graphics.vector.rememberVectorPainter import androidx.compose.ui.graphics.vector.rememberVectorPainter
import androidx.compose.ui.res.vectorXmlResource 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 @Composable
actual fun DownloadImageTick() { 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 @Composable
actual fun DownloadImageError() { actual fun DownloadImageError(modifier: Modifier) {
Image( Image(
vectorXmlResource("drawable/ic_error.xml"), 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.TextAlign
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp 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.GlobalScope
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow 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 { i18n4k {
inputDirectory = "../../translations" inputDirectory = "../../translations"
packageName = "com.shabinder.common.translations" packageName = "com.shabinder.common.translations"
// sourceCodeLocales = listOf("en", "de") // sourceCodeLocales = listOf("en", "de", "es", "fr", "id", "pt", "ru", "uk")
} }
kotlin { kotlin {

View File

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

View File

@ -32,8 +32,8 @@ sealed class SpotiFlyerException(override val message: String): Exception(messag
val jioSaavnError: Throwable, val jioSaavnError: Throwable,
val ytMusicError: Throwable, val ytMusicError: Throwable,
override val message: String = "${Strings.noLinkFound()}: $trackName," + 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) ): SpotiFlyerException(message)
data class LinkInvalid( data class LinkInvalid(

View File

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

View File

@ -101,6 +101,7 @@ class FetchPlatformQueryResult(
} }
Source.YouTube -> { Source.YouTube -> {
youtubeMp3.getMp3DownloadLink(track.videoID.requireNotNull()).flatMapError { 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 -> youtubeProvider.ytDownloader.getVideo(track.videoID!!).get()?.url?.let { m4aLink ->
audioToMp3.convertToMp3(m4aLink) audioToMp3.convertToMp3(m4aLink)
} ?: throw SpotiFlyerException.YoutubeLinkNotFound(track.videoID) } ?: throw SpotiFlyerException.YoutubeLinkNotFound(track.videoID)
@ -130,8 +131,8 @@ class FetchPlatformQueryResult(
SuspendableEvent.error( SuspendableEvent.error(
SpotiFlyerException.DownloadLinkFetchFailed( SpotiFlyerException.DownloadLinkFetchFailed(
trackName = track.title, 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"} // logger.d("YT Api Result"){"$trackName - $linksWithMatchValue"}
return linksWithMatchValue.toList().sortedByDescending { it.second }.toMap().also { 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 // Skip this Result if No Word is Common in Name
if (!hasCommonWord) { if (!hasCommonWord) {
logger.i("Saavn Removing Common Word") { result.toString() } // logger.i("Saavn Removing Common Word") { result.toString() }
continue continue
} }
@ -264,7 +264,7 @@ interface JioSaavnRequests {
} }
if (artistMatchNumber == 0) { if (artistMatchNumber == 0) {
logger.i("Artist Match Saavn Removing") { result.toString() } // logger.i("Artist Match Saavn Removing") { result.toString() }
continue continue
} }
val artistMatch: Float = (artistMatchNumber.toFloat() / trackArtists.size) * 100 val artistMatch: Float = (artistMatchNumber.toFloat() / trackArtists.size) * 100
@ -274,11 +274,12 @@ interface JioSaavnRequests {
linksWithMatchValue[result.id] = avgMatch linksWithMatchValue[result.id] = avgMatch
} }
return linksWithMatchValue.toList().sortedByDescending { it.second }.toMap().also { 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 { companion object {
const val TAG = "Saavn Request"
// EndPoints // 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 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=" 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() instanceKeeper.ensureNeverFrozen()
methods.value = dependencies.actions.freeze() methods.value = dependencies.actions.freeze()
/*Init App Launch & Authenticate Spotify Client*/ /*Init App Launch & Authenticate Spotify Client*/
initAppLaunchAndAuthenticateSpotify(dependencies.fetchQuery::authenticateSpotifyClient) initAppLaunchAndAuthenticateSpotify(dependencies.fetchQuery::authenticateSpotifyClient)
} }

View File

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

View File

@ -5,7 +5,7 @@ import com.jakewharton.mosaic.Text
import com.jakewharton.mosaic.runMosaic import com.jakewharton.mosaic.runMosaic
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
fun main(/*args: Array<String>*/) = runMosaic { fun main() = runMosaic {
// TODO https://github.com/JakeWharton/mosaic/issues/3 // TODO https://github.com/JakeWharton/mosaic/issues/3
var count by mutableStateOf(0) 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.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.uikit.SpotiFlyerColors import com.shabinder.common.uikit.configurations.SpotiFlyerColors
import com.shabinder.common.uikit.SpotiFlyerRootContent import com.shabinder.common.uikit.configurations.SpotiFlyerShapes
import com.shabinder.common.uikit.SpotiFlyerShapes import com.shabinder.common.uikit.configurations.SpotiFlyerTypography
import com.shabinder.common.uikit.SpotiFlyerTypography import com.shabinder.common.uikit.configurations.colorOffWhite
import com.shabinder.common.uikit.colorOffWhite import com.shabinder.common.uikit.screens.SpotiFlyerRootContent
import com.shabinder.database.Database import com.shabinder.database.Database
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.piwik.java.tracking.PiwikTracker import org.piwik.java.tracking.PiwikTracker
import utils.trackAsync import utils.trackAsync
import utils.trackScreenAsync import utils.trackScreenAsync
import java.awt.Desktop import java.awt.Desktop
import java.awt.Toolkit
import java.awt.datatransfer.Clipboard
import java.awt.datatransfer.StringSelection
import java.net.URI import java.net.URI
import javax.swing.JFileChooser import javax.swing.JFileChooser
import javax.swing.JFileChooser.APPROVE_OPTION import javax.swing.JFileChooser.APPROVE_OPTION
private val koin = initKoin(enableNetworkLogs = true).koin private val koin = initKoin(enableNetworkLogs = true).koin
private lateinit var showToast: (String)->Unit private lateinit var showToast: (String)->Unit
private val tracker: PiwikTracker by lazy { 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 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) override fun openPlatform(packageID: String, platformLink: String) = openLink(platformLink)

View File

@ -31,11 +31,3 @@ include(
":console-app", ":console-app",
":maintenance-tasks" ":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. 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 share = Teilen
shareDescription = Teile diese App mit deiner Familie und deinen Freunden. 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 status = Status
analytics = Analyse analytics = Analyse
analyticsDescription = Deine Daten sind Anonym und werden nie mit Drittanbietern geteilt. 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. donateDescription = If you think I deserve to get paid for my work, you can support me here.
share = Share share = Share
shareDescription = Share this app with your friends and family. 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 status = Status
analytics = Analytics analytics = Analytics
analyticsDescription = Your Data is Anonymized and never shared with 3rd party service. 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. donateDescription = Si crees que merezco una paga por mi trabajo, aquí puedes apoyarme.
share = Comparte share = Comparte
shareDescription = Comparte esta aplicación con tus familiares y amigos. 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 status = Estatus
analytics = Analiticos analytics = Analiticos
analyticsDescription = Tus datos son anonimizados y nunca se compartiran con servicios de terceros. 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. donateDescription = Si vous pensez que je mérite d'être payé pour mon travail, vous pouvez me soutenir ici.
share = Partager share = Partager
shareDescription = Partagez cette application avec vos amis et votre famille. 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 status = Statut
analytics = Analyses analytics = Analyses
analyticsDescription = Vos données sont anonymes et ne sont jamais partagées avec des services tiers. 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. donateDescription = Jika Anda ingin memberi donasi ke developer apl. ini, Anda bisa donasi disini.
share = Bagikan share = Bagikan
shareDescription = Bagikan aplikasi ini kepada teman / keluarga anda. 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 status = Status
analytics = Analytics analytics = Analytics
analyticsDescription = Data Anda tidak dapat dilihat oleh orang lain dan TIDAK akan dibagikan ke pihak ketiga. 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. donateDescription = Se você acha que mereço ser pago pelo meu trabalho, você pode me ajudar aqui.
share = Compartilhar share = Compartilhar
shareDescription = Compartilhe este app com seus amigos e família. 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 status = Status
analytics = Estatísticas analytics = Estatísticas
analyticsDescription = Seus dados serão tornados anônimos e nunca serão compartilhados com serviços de terceiros. 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 = Если считаешь, что я заслуживаю этого, ты можешь поддержать меня здесь. donateDescription = Если считаешь, что я заслуживаю этого, ты можешь поддержать меня здесь.
share = Поделиться share = Поделиться
shareDescription = Расскажи об этом приложении знакомым - поделись полезным инструментом. shareDescription = Расскажи об этом приложении знакомым - поделись полезным инструментом.
whatWentWrong = Что пошло не так...
copyToClipboard = Скопировать в буфер обмена
copyCodeInGithubIssue = Скопируйте вставьте ниже код при создании проблемы Github / Сообщите об этой проблеме для лучшей помощи.
status = Статус status = Статус
analytics = Аналитика analytics = Аналитика
analyticsDescription = Предоставить анонимные данные для улучшения приложения. Они не будут переданы третьим лицам. analyticsDescription = Предоставить анонимные данные для улучшения приложения. Они не будут переданы третьим лицам.

View File

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

View File

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