mirror of
https://github.com/IllusionMan1212/lyrics-grabbr.git
synced 2026-10-24 02:12:27 +00:00
feat: settings option to change the app language
This commit is contained in:
@@ -9,8 +9,8 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val keystoreProperties = Properties()
|
val keystoreProperties = Properties()
|
||||||
val keystorePropertiesFile: File? = rootProject.file("keystore.properties")
|
val keystorePropertiesFile: File = rootProject.file("keystore.properties")
|
||||||
if (keystorePropertiesFile?.exists() == true) {
|
if (keystorePropertiesFile.exists()) {
|
||||||
keystoreProperties.load(FileInputStream(keystorePropertiesFile))
|
keystoreProperties.load(FileInputStream(keystorePropertiesFile))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,6 +30,9 @@ android {
|
|||||||
useSupportLibrary = true
|
useSupportLibrary = true
|
||||||
}
|
}
|
||||||
signingConfig = signingConfigs.getByName("debug")
|
signingConfig = signingConfigs.getByName("debug")
|
||||||
|
|
||||||
|
// https://developer.android.com/guide/topics/resources/app-languages#gradle-config
|
||||||
|
resourceConfigurations.plus(listOf("en", "ar"))
|
||||||
}
|
}
|
||||||
|
|
||||||
signingConfigs {
|
signingConfigs {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||||
android:fullBackupContent="@xml/backup_rules"
|
android:fullBackupContent="@xml/backup_rules"
|
||||||
|
android:localeConfig="@xml/locales_config"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
@@ -21,7 +22,7 @@
|
|||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:configChanges="screenSize|screenLayout|keyboardHidden|orientation"
|
android:configChanges="screenSize|screenLayout|keyboardHidden|orientation|locale"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:theme="@style/Theme.LyricsGrabbr.Splash"
|
android:theme="@style/Theme.LyricsGrabbr.Splash"
|
||||||
>
|
>
|
||||||
@@ -45,5 +46,15 @@
|
|||||||
android:name="android.app.NOTIFICATION_LISTENER"
|
android:name="android.app.NOTIFICATION_LISTENER"
|
||||||
android:value="Media playback notification listener service" />
|
android:value="Media playback notification listener service" />
|
||||||
</service>
|
</service>
|
||||||
|
<service
|
||||||
|
android:name="androidx.appcompat.app.AppLocalesMetadataHolderService"
|
||||||
|
android:enabled="false"
|
||||||
|
android:exported="false"
|
||||||
|
>
|
||||||
|
<meta-data
|
||||||
|
android:name="autoStoreLocales"
|
||||||
|
android:value="true"
|
||||||
|
/>
|
||||||
|
</service>
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
@@ -8,6 +8,7 @@ import android.os.Bundle
|
|||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.activity.enableEdgeToEdge
|
import androidx.activity.enableEdgeToEdge
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.compose.animation.core.tween
|
import androidx.compose.animation.core.tween
|
||||||
import androidx.compose.animation.fadeIn
|
import androidx.compose.animation.fadeIn
|
||||||
import androidx.compose.animation.fadeOut
|
import androidx.compose.animation.fadeOut
|
||||||
@@ -45,10 +46,9 @@ data class NotificationEvent(
|
|||||||
val artist: String,
|
val artist: String,
|
||||||
val title: String,
|
val title: String,
|
||||||
val playbackState: Int,
|
val playbackState: Int,
|
||||||
val duration: Long
|
|
||||||
)
|
)
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : AppCompatActivity() {
|
||||||
private var lastNotification: MutableStateFlow<NotificationEvent?> = MutableStateFlow(null)
|
private var lastNotification: MutableStateFlow<NotificationEvent?> = MutableStateFlow(null)
|
||||||
private var isReceiverRunning = false
|
private var isReceiverRunning = false
|
||||||
|
|
||||||
@@ -175,14 +175,12 @@ class MainActivity : ComponentActivity() {
|
|||||||
val artist = intent.getStringExtra(NotificationListener.NOTIFICATION_PACKAGE_ARTIST)
|
val artist = intent.getStringExtra(NotificationListener.NOTIFICATION_PACKAGE_ARTIST)
|
||||||
val songTitle = intent.getStringExtra(NotificationListener.NOTIFICATION_PACKAGE_SONG_TITLE)
|
val songTitle = intent.getStringExtra(NotificationListener.NOTIFICATION_PACKAGE_SONG_TITLE)
|
||||||
val playbackState = intent.getIntExtra(NotificationListener.NOTIFICATION_PACKAGE_PLAYBACK_STATE, -1)
|
val playbackState = intent.getIntExtra(NotificationListener.NOTIFICATION_PACKAGE_PLAYBACK_STATE, -1)
|
||||||
val duration = intent.getLongExtra(NotificationListener.NOTIFICATION_PACKAGE_DURATION, -1)
|
|
||||||
val notifPackageName = intent.getStringExtra(NotificationListener.NOTIFICATION_PACKAGE_NAME)
|
val notifPackageName = intent.getStringExtra(NotificationListener.NOTIFICATION_PACKAGE_NAME)
|
||||||
|
|
||||||
lastNotification.update { NotificationEvent(
|
lastNotification.update { NotificationEvent(
|
||||||
title = songTitle!!,
|
title = songTitle!!,
|
||||||
artist = artist!!,
|
artist = artist!!,
|
||||||
playbackState = playbackState,
|
playbackState = playbackState,
|
||||||
duration = duration,
|
|
||||||
packageName = notifPackageName!!,
|
packageName = notifPackageName!!,
|
||||||
) }
|
) }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import android.service.notification.StatusBarNotification
|
|||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import androidx.core.app.NotificationManagerCompat
|
import androidx.core.app.NotificationManagerCompat
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.core.os.BundleCompat
|
||||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||||
|
|
||||||
class NotificationListener: NotificationListenerService() {
|
class NotificationListener: NotificationListenerService() {
|
||||||
@@ -113,14 +114,11 @@ class NotificationListener: NotificationListenerService() {
|
|||||||
if (ContextCompat.checkSelfPermission(this, POST_NOTIFICATIONS) != PERMISSION_GRANTED) {
|
if (ContextCompat.checkSelfPermission(this, POST_NOTIFICATIONS) != PERMISSION_GRANTED) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Apparently the new getParcelable API is buggy and throws an exception if used.
|
|
||||||
// so we continue to use the deprecated API even on newer versions.
|
val mediaSessionToken = BundleCompat.getParcelable(sbn.notification.extras, Notification.EXTRA_MEDIA_SESSION, MediaSession.Token::class.java)
|
||||||
// https://issuetracker.google.com/issues/240585930
|
|
||||||
val mediaSessionToken = sbn.notification.extras.getParcelable<MediaSession.Token>(Notification.EXTRA_MEDIA_SESSION)
|
|
||||||
?: return
|
?: return
|
||||||
|
|
||||||
val mediaCtrl = MediaController(context, mediaSessionToken)
|
val mediaCtrl = MediaController(context, mediaSessionToken)
|
||||||
val duration = mediaCtrl.metadata?.getLong(MediaMetadata.METADATA_KEY_DURATION)?.div(1000)
|
|
||||||
val playbackState = mediaCtrl.playbackState?.state
|
val playbackState = mediaCtrl.playbackState?.state
|
||||||
val albumArt = mediaCtrl.metadata?.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART)
|
val albumArt = mediaCtrl.metadata?.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART)
|
||||||
|
|
||||||
@@ -133,7 +131,6 @@ class NotificationListener: NotificationListenerService() {
|
|||||||
intent.putExtra(NOTIFICATION_PACKAGE_NAME, sbn.packageName)
|
intent.putExtra(NOTIFICATION_PACKAGE_NAME, sbn.packageName)
|
||||||
intent.putExtra(NOTIFICATION_PACKAGE_ARTIST, packageArtist)
|
intent.putExtra(NOTIFICATION_PACKAGE_ARTIST, packageArtist)
|
||||||
intent.putExtra(NOTIFICATION_PACKAGE_SONG_TITLE, packageSongTitle)
|
intent.putExtra(NOTIFICATION_PACKAGE_SONG_TITLE, packageSongTitle)
|
||||||
intent.putExtra(NOTIFICATION_PACKAGE_DURATION, duration)
|
|
||||||
intent.putExtra(NOTIFICATION_PACKAGE_PLAYBACK_STATE, playbackState)
|
intent.putExtra(NOTIFICATION_PACKAGE_PLAYBACK_STATE, playbackState)
|
||||||
|
|
||||||
if (playbackState == PlaybackState.STATE_PAUSED) {
|
if (playbackState == PlaybackState.STATE_PAUSED) {
|
||||||
@@ -166,9 +163,8 @@ class NotificationListener: NotificationListenerService() {
|
|||||||
const val NOTIFICATION_PACKAGE_ARTIST = "package_artist"
|
const val NOTIFICATION_PACKAGE_ARTIST = "package_artist"
|
||||||
const val NOTIFICATION_PACKAGE_SONG_TITLE = "package_song_title"
|
const val NOTIFICATION_PACKAGE_SONG_TITLE = "package_song_title"
|
||||||
const val NOTIFICATION_PACKAGE_PLAYBACK_STATE = "package_playback_state"
|
const val NOTIFICATION_PACKAGE_PLAYBACK_STATE = "package_playback_state"
|
||||||
const val NOTIFICATION_PACKAGE_DURATION = "package_duration"
|
|
||||||
const val NOTIFICATION_ID = 1
|
const val NOTIFICATION_ID = 1
|
||||||
const val NOTIFICATION_CHANNEL_ID = "Lyrics"
|
const val NOTIFICATION_CHANNEL_ID = "Lyrics"
|
||||||
const val START_FOREGROUND_SERVICE_ACTION = "START_SERVICE"
|
const val START_FOREGROUND_SERVICE_ACTION = "START_SERVICE"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,9 +30,9 @@ import androidx.compose.material.icons.filled.MusicNote
|
|||||||
import androidx.compose.material.icons.filled.Settings
|
import androidx.compose.material.icons.filled.Settings
|
||||||
import androidx.compose.material3.CenterAlignedTopAppBar
|
import androidx.compose.material3.CenterAlignedTopAppBar
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
import androidx.compose.material3.Divider
|
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.FilledTonalButton
|
import androidx.compose.material3.FilledTonalButton
|
||||||
|
import androidx.compose.material3.HorizontalDivider
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.LocalContentColor
|
import androidx.compose.material3.LocalContentColor
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
@@ -48,7 +48,6 @@ 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.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
@@ -86,7 +85,6 @@ import com.illusionman1212.lyricsgrabbr.viewmodels.HomeViewModel
|
|||||||
import com.illusionman1212.lyricsgrabbr.viewmodels.SearchResult
|
import com.illusionman1212.lyricsgrabbr.viewmodels.SearchResult
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import coil.compose.AsyncImage
|
import coil.compose.AsyncImage
|
||||||
import com.illusionman1212.lyricsgrabbr.LGApp
|
|
||||||
import com.illusionman1212.lyricsgrabbr.ui.components.forwardingPainter
|
import com.illusionman1212.lyricsgrabbr.ui.components.forwardingPainter
|
||||||
import com.illusionman1212.lyricsgrabbr.utils.annotatedStringResource
|
import com.illusionman1212.lyricsgrabbr.utils.annotatedStringResource
|
||||||
import com.illusionman1212.lyricsgrabbr.viewmodels.LyricsViewModel
|
import com.illusionman1212.lyricsgrabbr.viewmodels.LyricsViewModel
|
||||||
@@ -106,7 +104,6 @@ fun HomePage(
|
|||||||
lastNotification: MutableStateFlow<NotificationEvent?>
|
lastNotification: MutableStateFlow<NotificationEvent?>
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val application = (context as Activity).application as LGApp
|
|
||||||
val lifecycleOwner = LocalLifecycleOwner.current
|
val lifecycleOwner = LocalLifecycleOwner.current
|
||||||
|
|
||||||
val lifecycleState by lifecycleOwner.lifecycle.currentStateAsState()
|
val lifecycleState by lifecycleOwner.lifecycle.currentStateAsState()
|
||||||
@@ -124,7 +121,7 @@ fun HomePage(
|
|||||||
) }
|
) }
|
||||||
var shouldShowNotificationPermissionRationale by remember { mutableStateOf(
|
var shouldShowNotificationPermissionRationale by remember { mutableStateOf(
|
||||||
ActivityCompat.shouldShowRequestPermissionRationale(
|
ActivityCompat.shouldShowRequestPermissionRationale(
|
||||||
context,
|
context as Activity,
|
||||||
POST_NOTIFICATIONS
|
POST_NOTIFICATIONS
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
@@ -415,7 +412,7 @@ fun Results(
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (it != results.last())
|
if (it != results.last())
|
||||||
Divider()
|
HorizontalDivider()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.illusionman1212.lyricsgrabbr.ui.screens
|
package com.illusionman1212.lyricsgrabbr.ui.screens
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
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
|
||||||
@@ -12,10 +13,12 @@ import androidx.compose.foundation.layout.padding
|
|||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.statusBarsPadding
|
import androidx.compose.foundation.layout.statusBarsPadding
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.ArrowBack
|
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||||
import androidx.compose.material.icons.filled.Check
|
import androidx.compose.material.icons.filled.Check
|
||||||
import androidx.compose.material.icons.outlined.Palette
|
import androidx.compose.material.icons.outlined.Palette
|
||||||
|
import androidx.compose.material.icons.outlined.Translate
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
@@ -32,10 +35,12 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.draw.alpha
|
import androidx.compose.ui.draw.alpha
|
||||||
import androidx.compose.ui.graphics.painter.Painter
|
import androidx.compose.ui.graphics.painter.Painter
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
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 androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
|
import androidx.core.os.LocaleListCompat
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import com.illusionman1212.lyricsgrabbr.R
|
import com.illusionman1212.lyricsgrabbr.R
|
||||||
import com.illusionman1212.lyricsgrabbr.ui.components.LGAlertDialog
|
import com.illusionman1212.lyricsgrabbr.ui.components.LGAlertDialog
|
||||||
@@ -43,7 +48,7 @@ import com.illusionman1212.lyricsgrabbr.ui.components.LGRadioButton
|
|||||||
import com.illusionman1212.lyricsgrabbr.ui.theme.ColorSecondaryLight
|
import com.illusionman1212.lyricsgrabbr.ui.theme.ColorSecondaryLight
|
||||||
import com.illusionman1212.lyricsgrabbr.data.Theme
|
import com.illusionman1212.lyricsgrabbr.data.Theme
|
||||||
import com.illusionman1212.lyricsgrabbr.ui.components.LGIconButton
|
import com.illusionman1212.lyricsgrabbr.ui.components.LGIconButton
|
||||||
import com.illusionman1212.lyricsgrabbr.utils.mirror
|
import com.illusionman1212.lyricsgrabbr.utils.getLangPreferenceDropdownEntries
|
||||||
import com.illusionman1212.lyricsgrabbr.viewmodels.SettingsState
|
import com.illusionman1212.lyricsgrabbr.viewmodels.SettingsState
|
||||||
import com.illusionman1212.lyricsgrabbr.viewmodels.SettingsViewModel
|
import com.illusionman1212.lyricsgrabbr.viewmodels.SettingsViewModel
|
||||||
|
|
||||||
@@ -72,8 +77,7 @@ fun SettingsPage(
|
|||||||
) {
|
) {
|
||||||
LGIconButton(tooltip = stringResource(id = R.string.go_back), onClick = goBack) {
|
LGIconButton(tooltip = stringResource(id = R.string.go_back), onClick = goBack) {
|
||||||
Icon(
|
Icon(
|
||||||
modifier = Modifier.mirror(),
|
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
|
||||||
imageVector = Icons.Filled.ArrowBack,
|
|
||||||
contentDescription = stringResource(
|
contentDescription = stringResource(
|
||||||
id = R.string.go_back
|
id = R.string.go_back
|
||||||
) )
|
) )
|
||||||
@@ -97,6 +101,7 @@ fun SettingsPage(
|
|||||||
@Composable
|
@Composable
|
||||||
fun Appearance(viewModel: SettingsViewModel, uiState: SettingsState) {
|
fun Appearance(viewModel: SettingsViewModel, uiState: SettingsState) {
|
||||||
var themeOpenDialog by remember { mutableStateOf(false) }
|
var themeOpenDialog by remember { mutableStateOf(false) }
|
||||||
|
var languageOpenDialog by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
SettingGroup(title = stringResource(id = R.string.appearance)) {
|
SettingGroup(title = stringResource(id = R.string.appearance)) {
|
||||||
DialogSetting(
|
DialogSetting(
|
||||||
@@ -105,6 +110,17 @@ fun Appearance(viewModel: SettingsViewModel, uiState: SettingsState) {
|
|||||||
icon = Icons.Outlined.Palette,
|
icon = Icons.Outlined.Palette,
|
||||||
onClick = { themeOpenDialog = true }
|
onClick = { themeOpenDialog = true }
|
||||||
)
|
)
|
||||||
|
DialogSetting(
|
||||||
|
title = stringResource(id = R.string.language),
|
||||||
|
value = AppCompatDelegate.getApplicationLocales()[0]?.let {
|
||||||
|
AppCompatDelegate.getApplicationLocales()[0]?.getDisplayLanguage(
|
||||||
|
it
|
||||||
|
)
|
||||||
|
}
|
||||||
|
?: stringResource(id = R.string._default),
|
||||||
|
icon = Icons.Outlined.Translate,
|
||||||
|
onClick = { languageOpenDialog = true }
|
||||||
|
)
|
||||||
}
|
}
|
||||||
if (themeOpenDialog) {
|
if (themeOpenDialog) {
|
||||||
ThemeDialog(
|
ThemeDialog(
|
||||||
@@ -115,6 +131,11 @@ fun Appearance(viewModel: SettingsViewModel, uiState: SettingsState) {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
if (languageOpenDialog) {
|
||||||
|
LanguageDialog(
|
||||||
|
onDismiss = { languageOpenDialog = false },
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -159,6 +180,47 @@ fun ThemeDialog(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun LanguageDialog(
|
||||||
|
onDismiss: () -> Unit,
|
||||||
|
) {
|
||||||
|
val langs = LocalContext.current.getLangPreferenceDropdownEntries()
|
||||||
|
|
||||||
|
LGAlertDialog(
|
||||||
|
onDismiss = onDismiss,
|
||||||
|
title = stringResource(id = R.string.language),
|
||||||
|
buttons = {
|
||||||
|
TextButton(onClick = onDismiss) {
|
||||||
|
Text(stringResource(id = R.string.cancel))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
LazyColumn {
|
||||||
|
item {
|
||||||
|
LGRadioButton(
|
||||||
|
text = stringResource(id = R.string._default),
|
||||||
|
onClick = {
|
||||||
|
AppCompatDelegate.setApplicationLocales(LocaleListCompat.getEmptyLocaleList())
|
||||||
|
},
|
||||||
|
selected = AppCompatDelegate.getApplicationLocales().isEmpty,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
for ((key, value) in langs) {
|
||||||
|
item {
|
||||||
|
LGRadioButton(
|
||||||
|
text = key,
|
||||||
|
onClick = {
|
||||||
|
AppCompatDelegate.setApplicationLocales(LocaleListCompat.forLanguageTags(value))
|
||||||
|
},
|
||||||
|
selected = AppCompatDelegate.getApplicationLocales()[0]?.language == value,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SettingGroup(title: String, content: @Composable () -> Unit) {
|
fun SettingGroup(title: String, content: @Composable () -> Unit) {
|
||||||
Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
|
Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
|
||||||
@@ -169,7 +231,7 @@ fun SettingGroup(title: String, content: @Composable () -> Unit) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun Header(title: String) {
|
private fun Header(title: String) {
|
||||||
Row(Modifier.padding(start = 56.dp)) {
|
Row(Modifier.padding(start = 56.dp)) {
|
||||||
Text(text = title, color = MaterialTheme.colorScheme.primary)
|
Text(text = title, color = MaterialTheme.colorScheme.primary)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.illusionman1212.lyricsgrabbr.utils
|
package com.illusionman1212.lyricsgrabbr.utils
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import android.text.Annotation
|
import android.text.Annotation
|
||||||
import android.text.SpannedString
|
import android.text.SpannedString
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
@@ -7,6 +8,11 @@ import androidx.compose.runtime.Composable
|
|||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.text.AnnotatedString
|
import androidx.compose.ui.text.AnnotatedString
|
||||||
import androidx.compose.ui.text.SpanStyle
|
import androidx.compose.ui.text.SpanStyle
|
||||||
|
import androidx.core.os.LocaleListCompat
|
||||||
|
import com.illusionman1212.lyricsgrabbr.R
|
||||||
|
import org.xmlpull.v1.XmlPullParser
|
||||||
|
import org.xmlpull.v1.XmlPullParserException
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
// Native stylization is coming soon
|
// Native stylization is coming soon
|
||||||
// https://issuetracker.google.com/issues/139320238
|
// https://issuetracker.google.com/issues/139320238
|
||||||
@@ -31,4 +37,38 @@ fun annotatedStringResource(
|
|||||||
spanStyles(annotation)?.let { resultBuilder.addStyle(it, spanStart, spanEnd) }
|
spanStyles(annotation)?.let { resultBuilder.addStyle(it, spanStart, spanEnd) }
|
||||||
}
|
}
|
||||||
return resultBuilder.toAnnotatedString()
|
return resultBuilder.toAnnotatedString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Context.getLocaleListFromXml(): LocaleListCompat {
|
||||||
|
val tagsList = mutableListOf<CharSequence>()
|
||||||
|
try {
|
||||||
|
val xpp: XmlPullParser = resources.getXml(R.xml.locales_config)
|
||||||
|
while (xpp.eventType != XmlPullParser.END_DOCUMENT) {
|
||||||
|
if (xpp.eventType == XmlPullParser.START_TAG) {
|
||||||
|
if (xpp.name == "locale") {
|
||||||
|
tagsList.add(xpp.getAttributeValue(0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xpp.next()
|
||||||
|
}
|
||||||
|
} catch (e: XmlPullParserException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
} catch (e: IOException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
|
||||||
|
return LocaleListCompat.forLanguageTags(tagsList.joinToString(","))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun Context.getLangPreferenceDropdownEntries(): Map<String, String> {
|
||||||
|
val localeList = getLocaleListFromXml()
|
||||||
|
val map = mutableMapOf<String, String>()
|
||||||
|
|
||||||
|
for (a in 0 until localeList.size()) {
|
||||||
|
localeList[a].let {
|
||||||
|
it?.let { it1 -> map.put(it1.getDisplayName(it), it.toLanguageTag()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
package com.illusionman1212.lyricsgrabbr.viewmodels
|
package com.illusionman1212.lyricsgrabbr.viewmodels
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
@@ -85,7 +84,7 @@ data class HomeState(
|
|||||||
val error: String? = null,
|
val error: String? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
class HomeViewModel(private val context: Context, private val homeRepository: HomeRepository): ViewModel() {
|
class HomeViewModel(private val homeRepository: HomeRepository): ViewModel() {
|
||||||
private val _uiState = MutableStateFlow(HomeState())
|
private val _uiState = MutableStateFlow(HomeState())
|
||||||
val uiState = _uiState.asStateFlow()
|
val uiState = _uiState.asStateFlow()
|
||||||
private val json = Json {
|
private val json = Json {
|
||||||
@@ -134,7 +133,7 @@ class HomeViewModel(private val context: Context, private val homeRepository: Ho
|
|||||||
val Factory: ViewModelProvider.Factory = viewModelFactory {
|
val Factory: ViewModelProvider.Factory = viewModelFactory {
|
||||||
initializer {
|
initializer {
|
||||||
val application = (this[APPLICATION_KEY] as LGApp)
|
val application = (this[APPLICATION_KEY] as LGApp)
|
||||||
HomeViewModel(application.applicationContext, application.homeRepository)
|
HomeViewModel(application.homeRepository)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,8 @@
|
|||||||
<string name="theme_light">نهار</string>
|
<string name="theme_light">نهار</string>
|
||||||
<string name="theme_dark">ليلي</string>
|
<string name="theme_dark">ليلي</string>
|
||||||
<string name="theme_system">استخدم مظهر النظام</string>
|
<string name="theme_system">استخدم مظهر النظام</string>
|
||||||
|
<string name="language">اللغة</string>
|
||||||
|
<string name="_default">الإفتراضي</string>
|
||||||
|
|
||||||
<!-- Lyrics -->
|
<!-- Lyrics -->
|
||||||
<string name="lyrics">كلمات الأغنية</string>
|
<string name="lyrics">كلمات الأغنية</string>
|
||||||
|
|||||||
@@ -31,6 +31,8 @@
|
|||||||
<string name="theme_light">Light</string>
|
<string name="theme_light">Light</string>
|
||||||
<string name="theme_dark">Dark</string>
|
<string name="theme_dark">Dark</string>
|
||||||
<string name="theme_system">Use System Theme</string>
|
<string name="theme_system">Use System Theme</string>
|
||||||
|
<string name="language">Language</string>
|
||||||
|
<string name="_default">Default</string>
|
||||||
|
|
||||||
<!-- Lyrics -->
|
<!-- Lyrics -->
|
||||||
<string name="lyrics">Lyrics</string>
|
<string name="lyrics">Lyrics</string>
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<locale-config xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<locale android:name="en" />
|
||||||
|
<locale android:name="ar" />
|
||||||
|
</locale-config>
|
||||||
Reference in New Issue
Block a user