mirror of
https://github.com/IllusionMan1212/lyrics-grabbr.git
synced 2026-10-24 18:12:27 +00:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
37b5ca8917
|
||
|
|
6b64ead713
|
@@ -118,7 +118,7 @@ dependencies {
|
|||||||
implementation("androidx.compose.ui:ui")
|
implementation("androidx.compose.ui:ui")
|
||||||
implementation("androidx.compose.ui:ui-graphics")
|
implementation("androidx.compose.ui:ui-graphics")
|
||||||
implementation("androidx.compose.ui:ui-tooling-preview")
|
implementation("androidx.compose.ui:ui-tooling-preview")
|
||||||
implementation("androidx.compose.material3:material3:1.3.0-beta04")
|
implementation("androidx.compose.material3:material3:1.2.1")
|
||||||
implementation("com.google.android.material:material:1.12.0")
|
implementation("com.google.android.material:material:1.12.0")
|
||||||
implementation("androidx.compose.material:material-icons-extended:1.6.8")
|
implementation("androidx.compose.material:material-icons-extended:1.6.8")
|
||||||
implementation("androidx.core:core-splashscreen:1.0.1")
|
implementation("androidx.core:core-splashscreen:1.0.1")
|
||||||
|
|||||||
@@ -6,8 +6,6 @@
|
|||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
|
||||||
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
|
|
||||||
tools:ignore="QueryAllPackagesPermission" />
|
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".LGApp"
|
android:name=".LGApp"
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ import com.illusionman1212.lyricsgrabbr.ui.screens.HomePage
|
|||||||
import com.illusionman1212.lyricsgrabbr.ui.screens.LyricsPage
|
import com.illusionman1212.lyricsgrabbr.ui.screens.LyricsPage
|
||||||
import com.illusionman1212.lyricsgrabbr.ui.theme.LyricsGrabbrTheme
|
import com.illusionman1212.lyricsgrabbr.ui.theme.LyricsGrabbrTheme
|
||||||
import com.illusionman1212.lyricsgrabbr.ui.screens.SettingsPage
|
import com.illusionman1212.lyricsgrabbr.ui.screens.SettingsPage
|
||||||
import com.illusionman1212.lyricsgrabbr.ui.screens.WhitelistPage
|
|
||||||
import com.illusionman1212.lyricsgrabbr.viewmodels.LyricsViewModel
|
import com.illusionman1212.lyricsgrabbr.viewmodels.LyricsViewModel
|
||||||
import com.illusionman1212.lyricsgrabbr.viewmodels.SettingsViewModel
|
import com.illusionman1212.lyricsgrabbr.viewmodels.SettingsViewModel
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
@@ -131,17 +130,6 @@ class MainActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
composable(Routes.Settings.route) {
|
composable(Routes.Settings.route) {
|
||||||
SettingsPage(
|
SettingsPage(
|
||||||
goBack = {
|
|
||||||
navController.popBackStack()
|
|
||||||
},
|
|
||||||
navigateToWhitelist = {
|
|
||||||
navController.navigate(Routes.Whitelist.route)
|
|
||||||
},
|
|
||||||
viewModel = settingsViewModel,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
composable(Routes.Whitelist.route) {
|
|
||||||
WhitelistPage(
|
|
||||||
goBack = {
|
goBack = {
|
||||||
navController.popBackStack()
|
navController.popBackStack()
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -20,29 +20,15 @@ import androidx.core.app.NotificationManagerCompat
|
|||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.os.BundleCompat
|
import androidx.core.os.BundleCompat
|
||||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||||
import com.illusionman1212.lyricsgrabbr.data.SettingsPreferencesRepository
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.cancel
|
|
||||||
import kotlinx.coroutines.flow.first
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
class NotificationListener: NotificationListenerService() {
|
class NotificationListener: NotificationListenerService() {
|
||||||
private var notificationManager : NotificationManagerCompat? = null
|
private var notificationManager : NotificationManagerCompat? = null
|
||||||
private lateinit var settingsPrefsRepo: SettingsPreferencesRepository
|
|
||||||
private val serviceScope = CoroutineScope(Dispatchers.IO)
|
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
|
|
||||||
initNotificationChannels(this)
|
initNotificationChannels(this)
|
||||||
notificationManager = NotificationManagerCompat.from(applicationContext)
|
notificationManager = NotificationManagerCompat.from(applicationContext)
|
||||||
settingsPrefsRepo = (application as LGApp).settingsPreferencesRepository
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroy() {
|
|
||||||
super.onDestroy()
|
|
||||||
serviceScope.cancel()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||||
@@ -67,11 +53,7 @@ class NotificationListener: NotificationListenerService() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onNotificationPosted(sbn: StatusBarNotification) {
|
override fun onNotificationPosted(sbn: StatusBarNotification) {
|
||||||
serviceScope.launch {
|
sendNotificationBroadcast(sbn, applicationContext)
|
||||||
val whitelist = settingsPrefsRepo.preferences.first().whitelist
|
|
||||||
if (sbn.packageName !in whitelist) return@launch
|
|
||||||
sendNotificationBroadcast(sbn, applicationContext)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getStopServiceNotificationAction(): PendingIntent {
|
private fun getStopServiceNotificationAction(): PendingIntent {
|
||||||
@@ -109,13 +91,8 @@ class NotificationListener: NotificationListenerService() {
|
|||||||
|
|
||||||
private fun getCurrentNotifications(context: Context) {
|
private fun getCurrentNotifications(context: Context) {
|
||||||
val sbns = activeNotifications
|
val sbns = activeNotifications
|
||||||
serviceScope.launch {
|
for (sbn in sbns) {
|
||||||
val whitelist = settingsPrefsRepo.preferences.first().whitelist
|
sendNotificationBroadcast(sbn, context)
|
||||||
|
|
||||||
for (sbn in sbns) {
|
|
||||||
if (sbn.packageName !in whitelist) continue
|
|
||||||
sendNotificationBroadcast(sbn, context)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
package com.illusionman1212.lyricsgrabbr
|
package com.illusionman1212.lyricsgrabbr
|
||||||
|
|
||||||
sealed class Routes(val route: String) {
|
sealed class Routes(val route: String) {
|
||||||
data object Home : Routes("home")
|
object Home : Routes("home")
|
||||||
data object Lyrics : Routes("lyrics")
|
object Lyrics : Routes("lyrics")
|
||||||
data object Settings : Routes("settings")
|
object Settings : Routes("settings")
|
||||||
data object Whitelist : Routes("settings/whitelist")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
-16
@@ -4,7 +4,6 @@ import androidx.datastore.core.DataStore
|
|||||||
import androidx.datastore.preferences.core.Preferences
|
import androidx.datastore.preferences.core.Preferences
|
||||||
import androidx.datastore.preferences.core.edit
|
import androidx.datastore.preferences.core.edit
|
||||||
import androidx.datastore.preferences.core.intPreferencesKey
|
import androidx.datastore.preferences.core.intPreferencesKey
|
||||||
import androidx.datastore.preferences.core.stringSetPreferencesKey
|
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
|
|
||||||
enum class Theme {
|
enum class Theme {
|
||||||
@@ -15,7 +14,6 @@ enum class Theme {
|
|||||||
|
|
||||||
data class SettingsPreferences(
|
data class SettingsPreferences(
|
||||||
val appTheme: Int = Theme.SYSTEM.ordinal,
|
val appTheme: Int = Theme.SYSTEM.ordinal,
|
||||||
val whitelist: Set<String> = emptySet(),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
class SettingsPreferencesRepository(
|
class SettingsPreferencesRepository(
|
||||||
@@ -23,7 +21,6 @@ class SettingsPreferencesRepository(
|
|||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
private val APP_THEME = intPreferencesKey("app_theme")
|
private val APP_THEME = intPreferencesKey("app_theme")
|
||||||
private val WHITELIST = stringSetPreferencesKey("apps_whitelist")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val preferences = dataStore.data.map { mapSettingsPreferences(it) }
|
val preferences = dataStore.data.map { mapSettingsPreferences(it) }
|
||||||
@@ -34,22 +31,9 @@ class SettingsPreferencesRepository(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun addToWhitelist(packageName: String) {
|
|
||||||
dataStore.edit { prefs ->
|
|
||||||
prefs[WHITELIST] = (prefs[WHITELIST]?.toMutableSet() ?: emptySet()) + packageName
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun removeFromWhitelist(packageName: String) {
|
|
||||||
dataStore.edit { prefs ->
|
|
||||||
prefs[WHITELIST] = (prefs[WHITELIST]?.toMutableSet() ?: emptySet()) - packageName
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun mapSettingsPreferences(prefs: Preferences): SettingsPreferences {
|
private fun mapSettingsPreferences(prefs: Preferences): SettingsPreferences {
|
||||||
return SettingsPreferences(
|
return SettingsPreferences(
|
||||||
prefs[APP_THEME] ?: Theme.SYSTEM.ordinal,
|
prefs[APP_THEME] ?: Theme.SYSTEM.ordinal,
|
||||||
prefs[WHITELIST] ?: emptySet(),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -433,6 +433,7 @@ fun LongClickBottomSheet(
|
|||||||
dragHandle = {},
|
dragHandle = {},
|
||||||
onDismissRequest = onDismiss,
|
onDismissRequest = onDismiss,
|
||||||
sheetState = sheetState,
|
sheetState = sheetState,
|
||||||
|
windowInsets = WindowInsets(top = 0.dp),
|
||||||
) {
|
) {
|
||||||
Column {
|
Column {
|
||||||
if (artistUri != Uri.EMPTY) {
|
if (artistUri != Uri.EMPTY) {
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import androidx.compose.foundation.layout.width
|
|||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||||
import androidx.compose.material.icons.filled.Apps
|
|
||||||
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.material.icons.outlined.Translate
|
||||||
@@ -62,7 +61,6 @@ private val Themes = hashMapOf(
|
|||||||
@Composable
|
@Composable
|
||||||
fun SettingsPage(
|
fun SettingsPage(
|
||||||
goBack: () -> Unit,
|
goBack: () -> Unit,
|
||||||
navigateToWhitelist: () -> Unit,
|
|
||||||
viewModel: SettingsViewModel,
|
viewModel: SettingsViewModel,
|
||||||
) {
|
) {
|
||||||
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
||||||
@@ -96,7 +94,6 @@ fun SettingsPage(
|
|||||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
) {
|
) {
|
||||||
Appearance(viewModel, uiState)
|
Appearance(viewModel, uiState)
|
||||||
Listening(navigateToWhitelist)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -141,35 +138,6 @@ fun Appearance(viewModel: SettingsViewModel, uiState: SettingsState) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun Listening(navigateToWhitelist: () -> Unit) {
|
|
||||||
SettingGroup(title = stringResource(id = R.string.listening)) {
|
|
||||||
Box(
|
|
||||||
Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.clickable { navigateToWhitelist() }
|
|
||||||
) {
|
|
||||||
Row(
|
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
|
||||||
modifier = Modifier.padding(vertical = 8.dp)
|
|
||||||
) {
|
|
||||||
Icon(imageVector = Icons.Default.Apps, contentDescription = null, modifier = Modifier.width(56.dp))
|
|
||||||
Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
|
|
||||||
Text(text = stringResource(id = R.string.whitelist_apps), color = MaterialTheme.colorScheme.onBackground)
|
|
||||||
Text(
|
|
||||||
text = stringResource(id = R.string.whitelist_desc),
|
|
||||||
color = ColorSecondaryLight,
|
|
||||||
fontSize = 14.sp,
|
|
||||||
maxLines = 2,
|
|
||||||
overflow = TextOverflow.Ellipsis,
|
|
||||||
modifier = Modifier.padding(end = 24.dp)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ThemeDialog(
|
fun ThemeDialog(
|
||||||
uiState: SettingsState,
|
uiState: SettingsState,
|
||||||
|
|||||||
@@ -1,162 +0,0 @@
|
|||||||
package com.illusionman1212.lyricsgrabbr.ui.screens
|
|
||||||
|
|
||||||
import androidx.compose.foundation.clickable
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
|
||||||
import androidx.compose.foundation.layout.Box
|
|
||||||
import androidx.compose.foundation.layout.Row
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.foundation.layout.size
|
|
||||||
import androidx.compose.foundation.layout.width
|
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
|
||||||
import androidx.compose.material.icons.Icons
|
|
||||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
|
||||||
import androidx.compose.material.icons.filled.Clear
|
|
||||||
import androidx.compose.material3.Checkbox
|
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
|
||||||
import androidx.compose.material3.HorizontalDivider
|
|
||||||
import androidx.compose.material3.Icon
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.material3.Scaffold
|
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.material3.TextField
|
|
||||||
import androidx.compose.material3.TextFieldDefaults
|
|
||||||
import androidx.compose.material3.TopAppBar
|
|
||||||
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
|
|
||||||
import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState
|
|
||||||
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.platform.LocalContext
|
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import androidx.compose.ui.semantics.Role
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|
||||||
import coil.compose.AsyncImage
|
|
||||||
import com.illusionman1212.lyricsgrabbr.R
|
|
||||||
import com.illusionman1212.lyricsgrabbr.ui.components.LGIconButton
|
|
||||||
import com.illusionman1212.lyricsgrabbr.ui.theme.Typography
|
|
||||||
import com.illusionman1212.lyricsgrabbr.viewmodels.AppInfo
|
|
||||||
import com.illusionman1212.lyricsgrabbr.viewmodels.SettingsViewModel
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
@Composable
|
|
||||||
fun WhitelistPage(goBack: () -> Unit, viewModel: SettingsViewModel) {
|
|
||||||
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
|
||||||
|
|
||||||
Scaffold(topBar = {
|
|
||||||
TopAppBar(
|
|
||||||
title = {
|
|
||||||
TextField(
|
|
||||||
value = uiState.whitelistSearchQuery,
|
|
||||||
onValueChange = {
|
|
||||||
viewModel.filterPackages(it)
|
|
||||||
},
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
placeholder = {
|
|
||||||
Text(text = stringResource(id = R.string.search), style = Typography.titleLarge)
|
|
||||||
},
|
|
||||||
trailingIcon = {
|
|
||||||
LGIconButton(tooltip = stringResource(id = R.string.clear), onClick = { viewModel.clearWhitelistSearch() }) {
|
|
||||||
Icon(Icons.Default.Clear, contentDescription = stringResource(id = R.string.clear))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
singleLine = true,
|
|
||||||
colors = TextFieldDefaults.colors(
|
|
||||||
focusedContainerColor = Color.Transparent,
|
|
||||||
unfocusedContainerColor = Color.Transparent,
|
|
||||||
disabledContainerColor = Color.Transparent,
|
|
||||||
cursorColor = MaterialTheme.colorScheme.primary,
|
|
||||||
focusedIndicatorColor = Color.Transparent,
|
|
||||||
unfocusedIndicatorColor = Color.Transparent,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
navigationIcon = {
|
|
||||||
LGIconButton(tooltip = stringResource(id = R.string.go_back), onClick = goBack) {
|
|
||||||
Icon(imageVector = Icons.AutoMirrored.Filled.ArrowBack, contentDescription = stringResource(
|
|
||||||
id = R.string.go_back
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}) { padding ->
|
|
||||||
val context = LocalContext.current
|
|
||||||
|
|
||||||
val refreshState = rememberPullToRefreshState()
|
|
||||||
var isRefreshing by remember { mutableStateOf(false) }
|
|
||||||
|
|
||||||
if (uiState.applications.isNotEmpty()) {
|
|
||||||
isRefreshing = false
|
|
||||||
}
|
|
||||||
|
|
||||||
PullToRefreshBox(
|
|
||||||
state = refreshState,
|
|
||||||
isRefreshing = isRefreshing,
|
|
||||||
onRefresh = {
|
|
||||||
isRefreshing = true
|
|
||||||
viewModel.refreshPackages(context)
|
|
||||||
},
|
|
||||||
modifier = Modifier.padding(padding)
|
|
||||||
) {
|
|
||||||
LazyColumn(Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally) {
|
|
||||||
if (uiState.applications.any { it.value.isWhitelisted }) {
|
|
||||||
for ((packageName, appInfo) in uiState.applications.filter { it.value.isWhitelisted }) {
|
|
||||||
item {
|
|
||||||
Application(packageName, appInfo, viewModel)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
item {
|
|
||||||
HorizontalDivider(
|
|
||||||
Modifier
|
|
||||||
.width(84.dp)
|
|
||||||
.padding(vertical = 12.dp))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for ((packageName, appInfo) in uiState.applications.filter { !it.value.isWhitelisted }) {
|
|
||||||
item {
|
|
||||||
Application(packageName, appInfo, viewModel)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun Application(packageName: String, app: AppInfo, viewModel: SettingsViewModel) {
|
|
||||||
Box(
|
|
||||||
Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.clickable(
|
|
||||||
role = Role.Checkbox,
|
|
||||||
onClick = {
|
|
||||||
if (app.isWhitelisted) {
|
|
||||||
viewModel.removeFromWhitelist(packageName)
|
|
||||||
} else {
|
|
||||||
viewModel.addToWhitelist(packageName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)) {
|
|
||||||
Row(
|
|
||||||
Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(horizontal = 24.dp, vertical = 5.dp),
|
|
||||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
|
||||||
verticalAlignment = Alignment.CenterVertically
|
|
||||||
) {
|
|
||||||
AsyncImage(modifier = Modifier.size(32.dp), model = app.icon, contentDescription = null)
|
|
||||||
Text(app.displayName, Modifier.weight(1f))
|
|
||||||
Checkbox(
|
|
||||||
checked = app.isWhitelisted,
|
|
||||||
onCheckedChange = null,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+2
-112
@@ -1,8 +1,5 @@
|
|||||||
package com.illusionman1212.lyricsgrabbr.viewmodels
|
package com.illusionman1212.lyricsgrabbr.viewmodels
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.pm.PackageManager
|
|
||||||
import android.graphics.drawable.Drawable
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY
|
import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY
|
||||||
@@ -17,23 +14,13 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
|||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.flow.update
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
data class AppInfo(
|
|
||||||
val displayName: String,
|
|
||||||
val icon: Drawable,
|
|
||||||
var isWhitelisted: Boolean,
|
|
||||||
)
|
|
||||||
|
|
||||||
data class SettingsState(
|
data class SettingsState(
|
||||||
val appTheme: Int = Theme.SYSTEM.ordinal,
|
val appTheme: Int = Theme.SYSTEM.ordinal,
|
||||||
val applications: Map<String, AppInfo> = emptyMap(),
|
|
||||||
val whitelistSearchQuery: String = "",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
class SettingsViewModel(private val settingsPrefsRepo: SettingsPreferencesRepository, context: Context): ViewModel() {
|
class SettingsViewModel(private val settingsPrefsRepo: SettingsPreferencesRepository): ViewModel() {
|
||||||
private var immutablePackages: Map<String, AppInfo> = emptyMap()
|
|
||||||
private val _uiState = MutableStateFlow(SettingsState())
|
private val _uiState = MutableStateFlow(SettingsState())
|
||||||
val uiState = _uiState.asStateFlow()
|
val uiState = _uiState.asStateFlow()
|
||||||
|
|
||||||
@@ -47,104 +34,7 @@ class SettingsViewModel(private val settingsPrefsRepo: SettingsPreferencesReposi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun getWhitelistedApps(): Set<String> {
|
|
||||||
return settingsPrefsRepo.preferences.first().whitelist
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getPackages(context: Context) {
|
|
||||||
if (immutablePackages.isNotEmpty()) return
|
|
||||||
|
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
|
||||||
val pm = context.packageManager
|
|
||||||
val packages = pm.getInstalledApplications(PackageManager.GET_META_DATA)
|
|
||||||
.filter { it.packageName != context.packageName && pm.getLaunchIntentForPackage(it.packageName) != null }
|
|
||||||
.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER) { it.loadLabel(pm).toString() })
|
|
||||||
|
|
||||||
val apps: MutableMap<String, AppInfo> = mutableMapOf()
|
|
||||||
val whitelistedApps = getWhitelistedApps()
|
|
||||||
|
|
||||||
packages.forEach { pkg ->
|
|
||||||
val label = pkg.loadLabel(pm)
|
|
||||||
val icon = pkg.loadIcon(pm)
|
|
||||||
apps[pkg.packageName] = AppInfo(
|
|
||||||
displayName = label.toString(),
|
|
||||||
icon = icon,
|
|
||||||
isWhitelisted = pkg.packageName in whitelistedApps
|
|
||||||
)
|
|
||||||
}
|
|
||||||
immutablePackages = apps
|
|
||||||
_uiState.update {
|
|
||||||
SettingsState(
|
|
||||||
appTheme = uiState.value.appTheme,
|
|
||||||
applications = apps,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun refreshPackages(context: Context) {
|
|
||||||
_uiState.update {
|
|
||||||
val apps = uiState.value.applications.toMutableMap()
|
|
||||||
apps.clear()
|
|
||||||
it.copy(whitelistSearchQuery = "", applications = apps)
|
|
||||||
}
|
|
||||||
val apps = immutablePackages.toMutableMap()
|
|
||||||
apps.clear()
|
|
||||||
immutablePackages = apps
|
|
||||||
getPackages(context)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun filterPackages(query: String) {
|
|
||||||
_uiState.update { state ->
|
|
||||||
state.copy(
|
|
||||||
applications = immutablePackages.filter {
|
|
||||||
it.value.displayName.lowercase().contains(query.lowercase())
|
|
||||||
}.toMutableMap(),
|
|
||||||
whitelistSearchQuery = query,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun clearWhitelistSearch() {
|
|
||||||
_uiState.update {
|
|
||||||
it.copy(applications = immutablePackages, whitelistSearchQuery = "")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun addToWhitelist(packageName: String) {
|
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
|
||||||
_uiState.update { state ->
|
|
||||||
val apps = immutablePackages.toMutableMap()
|
|
||||||
apps[packageName] = apps[packageName]!!.copy(isWhitelisted = true)
|
|
||||||
immutablePackages = apps
|
|
||||||
state.copy(
|
|
||||||
applications = immutablePackages.filter {
|
|
||||||
it.value.displayName.lowercase().contains(uiState.value.whitelistSearchQuery.lowercase())
|
|
||||||
}.toMutableMap(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
settingsPrefsRepo.addToWhitelist(packageName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun removeFromWhitelist(packageName: String) {
|
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
|
||||||
_uiState.update { state ->
|
|
||||||
val apps = immutablePackages.toMutableMap()
|
|
||||||
apps[packageName] = apps[packageName]!!.copy(isWhitelisted = false)
|
|
||||||
immutablePackages = apps
|
|
||||||
state.copy(
|
|
||||||
applications = immutablePackages.filter {
|
|
||||||
it.value.displayName.lowercase().contains(uiState.value.whitelistSearchQuery.lowercase())
|
|
||||||
}.toMutableMap(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
settingsPrefsRepo.removeFromWhitelist(packageName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
getPackages(context)
|
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
settingsPrefsRepo.preferences.collectLatest {
|
settingsPrefsRepo.preferences.collectLatest {
|
||||||
_uiState.value = _uiState.value.copy(
|
_uiState.value = _uiState.value.copy(
|
||||||
@@ -158,7 +48,7 @@ class SettingsViewModel(private val settingsPrefsRepo: SettingsPreferencesReposi
|
|||||||
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)
|
||||||
SettingsViewModel(application.settingsPreferencesRepository, application.applicationContext)
|
SettingsViewModel(application.settingsPreferencesRepository)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,12 +34,6 @@
|
|||||||
<string name="theme_system">استخدم مظهر النظام</string>
|
<string name="theme_system">استخدم مظهر النظام</string>
|
||||||
<string name="language">اللغة</string>
|
<string name="language">اللغة</string>
|
||||||
<string name="_default">الإفتراضي</string>
|
<string name="_default">الإفتراضي</string>
|
||||||
<string name="listening">الإستماع</string>
|
|
||||||
<string name="whitelist">القائمة البيضاء</string>
|
|
||||||
<string name="whitelist_apps">تطبيقات القائمة البيضاء</string>
|
|
||||||
<string name="whitelist_desc">اختر التطبيقات التي سيستمع التطبيق إلى إشعاراتها</string>
|
|
||||||
<string name="search">بحث</string>
|
|
||||||
<string name="clear">مسح</string>
|
|
||||||
|
|
||||||
<!-- Lyrics -->
|
<!-- Lyrics -->
|
||||||
<string name="lyrics">كلمات الأغنية</string>
|
<string name="lyrics">كلمات الأغنية</string>
|
||||||
|
|||||||
@@ -33,12 +33,6 @@
|
|||||||
<string name="theme_system">Use System Theme</string>
|
<string name="theme_system">Use System Theme</string>
|
||||||
<string name="language">Language</string>
|
<string name="language">Language</string>
|
||||||
<string name="_default">Default</string>
|
<string name="_default">Default</string>
|
||||||
<string name="listening">Listening</string>
|
|
||||||
<string name="whitelist">Whitelist</string>
|
|
||||||
<string name="whitelist_apps">Whitelist Apps</string>
|
|
||||||
<string name="whitelist_desc">Choose which apps Lyrics Grabbr will listen to</string>
|
|
||||||
<string name="search">Search</string>
|
|
||||||
<string name="clear">Clear</string>
|
|
||||||
|
|
||||||
<!-- Lyrics -->
|
<!-- Lyrics -->
|
||||||
<string name="lyrics">Lyrics</string>
|
<string name="lyrics">Lyrics</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user