mirror of
https://github.com/IllusionMan1212/lyrics-grabbr.git
synced 2026-10-24 02:12:27 +00:00
feat: whitelist for notification listening
This commit is contained in:
@@ -6,6 +6,8 @@
|
|||||||
<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,6 +35,7 @@ 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
|
||||||
@@ -130,6 +131,17 @@ 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,15 +20,29 @@ 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 {
|
||||||
@@ -53,8 +67,12 @@ class NotificationListener: NotificationListenerService() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onNotificationPosted(sbn: StatusBarNotification) {
|
override fun onNotificationPosted(sbn: StatusBarNotification) {
|
||||||
|
serviceScope.launch {
|
||||||
|
val whitelist = settingsPrefsRepo.preferences.first().whitelist
|
||||||
|
if (sbn.packageName !in whitelist) return@launch
|
||||||
sendNotificationBroadcast(sbn, applicationContext)
|
sendNotificationBroadcast(sbn, applicationContext)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun getStopServiceNotificationAction(): PendingIntent {
|
private fun getStopServiceNotificationAction(): PendingIntent {
|
||||||
val intent = Intent(android.provider.Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
val intent = Intent(android.provider.Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
@@ -91,10 +109,15 @@ class NotificationListener: NotificationListenerService() {
|
|||||||
|
|
||||||
private fun getCurrentNotifications(context: Context) {
|
private fun getCurrentNotifications(context: Context) {
|
||||||
val sbns = activeNotifications
|
val sbns = activeNotifications
|
||||||
|
serviceScope.launch {
|
||||||
|
val whitelist = settingsPrefsRepo.preferences.first().whitelist
|
||||||
|
|
||||||
for (sbn in sbns) {
|
for (sbn in sbns) {
|
||||||
|
if (sbn.packageName !in whitelist) continue
|
||||||
sendNotificationBroadcast(sbn, context)
|
sendNotificationBroadcast(sbn, context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun getInitialNotification(): Notification {
|
private fun getInitialNotification(): Notification {
|
||||||
val notification: Notification = NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
|
val notification: Notification = NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
package com.illusionman1212.lyricsgrabbr
|
package com.illusionman1212.lyricsgrabbr
|
||||||
|
|
||||||
sealed class Routes(val route: String) {
|
sealed class Routes(val route: String) {
|
||||||
object Home : Routes("home")
|
data object Home : Routes("home")
|
||||||
object Lyrics : Routes("lyrics")
|
data object Lyrics : Routes("lyrics")
|
||||||
object Settings : Routes("settings")
|
data object Settings : Routes("settings")
|
||||||
|
data object Whitelist : Routes("settings/whitelist")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+16
@@ -4,6 +4,7 @@ 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 {
|
||||||
@@ -14,6 +15,7 @@ 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(
|
||||||
@@ -21,6 +23,7 @@ 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) }
|
||||||
@@ -31,9 +34,22 @@ 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(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -16,6 +16,7 @@ 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
|
||||||
@@ -61,6 +62,7 @@ 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()
|
||||||
@@ -94,6 +96,7 @@ fun SettingsPage(
|
|||||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
) {
|
) {
|
||||||
Appearance(viewModel, uiState)
|
Appearance(viewModel, uiState)
|
||||||
|
Listening(navigateToWhitelist)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -138,6 +141,35 @@ 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,
|
||||||
|
|||||||
@@ -0,0 +1,94 @@
|
|||||||
|
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.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||||
|
import androidx.compose.material3.Checkbox
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TopAppBar
|
||||||
|
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.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.viewmodels.SettingsViewModel
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun WhitelistPage(goBack: () -> Unit, viewModel: SettingsViewModel) {
|
||||||
|
Scaffold(topBar = {
|
||||||
|
TopAppBar(
|
||||||
|
title = {
|
||||||
|
Text(text = stringResource(id = R.string.whitelist))
|
||||||
|
},
|
||||||
|
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 uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
|
Box(Modifier.padding(padding)) {
|
||||||
|
LazyColumn {
|
||||||
|
for ((packageName, appInfo) in uiState.applications) {
|
||||||
|
item {
|
||||||
|
var isWhitelisted by remember { mutableStateOf(appInfo.isWhitelisted) }
|
||||||
|
|
||||||
|
Box(
|
||||||
|
Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.clickable(
|
||||||
|
role = Role.Checkbox,
|
||||||
|
onClick = {
|
||||||
|
if (isWhitelisted) {
|
||||||
|
viewModel.removeFromWhitelist(packageName)
|
||||||
|
} else {
|
||||||
|
viewModel.addToWhitelist(packageName)
|
||||||
|
}
|
||||||
|
isWhitelisted = !isWhitelisted
|
||||||
|
}
|
||||||
|
)) {
|
||||||
|
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 = appInfo.icon, contentDescription = null)
|
||||||
|
Text(appInfo.displayName, Modifier.weight(1f))
|
||||||
|
Checkbox(
|
||||||
|
checked = isWhitelisted,
|
||||||
|
onCheckedChange = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
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
|
||||||
@@ -14,13 +17,21 @@ 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: MutableMap<String, AppInfo> = mutableMapOf(),
|
||||||
)
|
)
|
||||||
|
|
||||||
class SettingsViewModel(private val settingsPrefsRepo: SettingsPreferencesRepository): ViewModel() {
|
class SettingsViewModel(private val settingsPrefsRepo: SettingsPreferencesRepository, context: Context): ViewModel() {
|
||||||
private val _uiState = MutableStateFlow(SettingsState())
|
private val _uiState = MutableStateFlow(SettingsState())
|
||||||
val uiState = _uiState.asStateFlow()
|
val uiState = _uiState.asStateFlow()
|
||||||
|
|
||||||
@@ -34,7 +45,56 @@ class SettingsViewModel(private val settingsPrefsRepo: SettingsPreferencesReposi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun getWhitelistedApps(): Set<String> {
|
||||||
|
return settingsPrefsRepo.preferences.first().whitelist
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getPackages(context: Context) {
|
||||||
|
if (uiState.value.applications.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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_uiState.update {
|
||||||
|
SettingsState(
|
||||||
|
appTheme = uiState.value.appTheme,
|
||||||
|
applications = apps,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addToWhitelist(packageName: String) {
|
||||||
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
_uiState.value.applications[packageName]?.let { it.isWhitelisted = true }
|
||||||
|
settingsPrefsRepo.addToWhitelist(packageName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeFromWhitelist(packageName: String) {
|
||||||
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
_uiState.value.applications[packageName]?.let { it.isWhitelisted = false }
|
||||||
|
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(
|
||||||
@@ -48,7 +108,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)
|
SettingsViewModel(application.settingsPreferencesRepository, application.applicationContext)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,10 @@
|
|||||||
<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>
|
||||||
|
|
||||||
<!-- Lyrics -->
|
<!-- Lyrics -->
|
||||||
<string name="lyrics">كلمات الأغنية</string>
|
<string name="lyrics">كلمات الأغنية</string>
|
||||||
|
|||||||
@@ -33,6 +33,10 @@
|
|||||||
<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>
|
||||||
|
|
||||||
<!-- Lyrics -->
|
<!-- Lyrics -->
|
||||||
<string name="lyrics">Lyrics</string>
|
<string name="lyrics">Lyrics</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user