From 976b067ab647e0b6384f3f1f541a412bf1bbc2fd Mon Sep 17 00:00:00 2001 From: ojhdt Date: Thu, 10 Nov 2022 22:17:46 +0800 Subject: [PATCH] disable pushing of specific apps --- app/build.gradle | 9 +- app/src/main/AndroidManifest.xml | 6 + .../extension/auto/core/util/BrowserUtil.kt | 30 +++ .../extension/auto/core/util/FileUtil.kt | 51 +++- .../extension/auto/data/AppDatabase.kt | 4 +- .../extension/auto/data/AppModelDao.kt | 25 ++ .../extension/auto/domain/model/AppModel.kt | 18 ++ .../auto/domain/service/ConnService.kt | 248 ++++++++++++++---- .../extension/auto/ui/main/AppModelDialog.kt | 71 +++++ .../extension/auto/ui/main/MainScreen.kt | 31 ++- .../extension/auto/ui/main/MainViewModel.kt | 34 ++- 11 files changed, 465 insertions(+), 62 deletions(-) create mode 100644 app/src/main/java/com/ojhdtapp/parabox/extension/auto/core/util/BrowserUtil.kt create mode 100644 app/src/main/java/com/ojhdtapp/parabox/extension/auto/data/AppModelDao.kt create mode 100644 app/src/main/java/com/ojhdtapp/parabox/extension/auto/domain/model/AppModel.kt create mode 100644 app/src/main/java/com/ojhdtapp/parabox/extension/auto/ui/main/AppModelDialog.kt diff --git a/app/build.gradle b/app/build.gradle index fe6f912..833958f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -66,6 +66,9 @@ dependencies { debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_version" implementation "com.google.android.material:material:1.8.0-alpha02" + // Chrome Custom Tab + implementation "androidx.browser:browser:1.4.0" + // Room def room_version = "2.4.3" implementation "androidx.room:room-runtime:$room_version" @@ -74,7 +77,7 @@ dependencies { implementation "androidx.room:room-ktx:$room_version" // Development Kit - implementation 'com.ojhdt:parabox-development-kit:1.0.12-SNAPSHOT' + implementation 'com.ojhdt:parabox-development-kit:1.0.5' // DataStore implementation "androidx.datastore:datastore-preferences:1.0.0" @@ -87,6 +90,10 @@ dependencies { // Accompanist implementation "com.google.accompanist:accompanist-systemuicontroller:0.27.0" + // Coroutine Lifecycle Scopes + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4" + // ViewModel def lifecycle_version = "2.6.0-alpha02" implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version" diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 3bec97f..974a4ad 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -5,10 +5,16 @@ + + + + + + ?, subject: String?) { + val intent = Intent(Intent.ACTION_SENDTO) + intent.data = Uri.parse("mailto:") // only email apps should handle this + intent.putExtra(Intent.EXTRA_EMAIL, addresses) + intent.putExtra(Intent.EXTRA_SUBJECT, subject) + context.startActivity(intent) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ojhdtapp/parabox/extension/auto/core/util/FileUtil.kt b/app/src/main/java/com/ojhdtapp/parabox/extension/auto/core/util/FileUtil.kt index 394b7fe..17898b7 100644 --- a/app/src/main/java/com/ojhdtapp/parabox/extension/auto/core/util/FileUtil.kt +++ b/app/src/main/java/com/ojhdtapp/parabox/extension/auto/core/util/FileUtil.kt @@ -2,7 +2,13 @@ package com.ojhdtapp.parabox.extension.auto.core.util import android.content.Context import android.graphics.Bitmap +import android.graphics.Canvas +import android.graphics.drawable.AdaptiveIconDrawable +import android.graphics.drawable.BitmapDrawable +import android.graphics.drawable.Drawable +import android.graphics.drawable.LayerDrawable import android.net.Uri +import android.os.Build import androidx.core.content.FileProvider import com.ojhdtapp.parabox.extension.auto.BuildConfig import java.io.ByteArrayOutputStream @@ -14,12 +20,53 @@ import java.text.SimpleDateFormat import java.util.* import kotlin.math.roundToInt + object FileUtil { - fun getUriFromBitmap(context: Context, bm: Bitmap): Uri? { + + fun getAppIcon(context: Context, packageName: String): Bitmap? { + try { + val drawable = context.packageManager.getApplicationIcon(packageName) + if (drawable is BitmapDrawable) { + return drawable.bitmap + } else if (drawable is AdaptiveIconDrawable) { + val drr = arrayOfNulls(2) + drr[0] = drawable.background + drr[1] = drawable.foreground + val layerDrawable = LayerDrawable(drr) + val width = layerDrawable.intrinsicWidth + val height = layerDrawable.intrinsicHeight + val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888) + val canvas = Canvas(bitmap) + layerDrawable.setBounds(0, 0, canvas.width, canvas.height) + layerDrawable.draw(canvas) + return bitmap + } + } catch (e: Exception) { + e.printStackTrace() + } + return null + } + + fun getSmallIcon(context: Context, pkgName: String?, id: Int): Bitmap? { + var smallIcon: Bitmap? = null + val remotePkgContext: Context + try { + remotePkgContext = context.createPackageContext(pkgName, 0) + val drawable = remotePkgContext.resources.getDrawable(id) + if (drawable != null) { + smallIcon = (drawable as BitmapDrawable).bitmap + } + } catch (e: java.lang.Exception) { + e.printStackTrace() + } + return smallIcon + } + + fun getUriFromBitmap(context: Context, bm: Bitmap, name: String): Uri? { val targetDir = File(context.externalCacheDir, "bm") if (!targetDir.exists()) targetDir.mkdirs() val tempFile = - File(targetDir, "temp_${System.currentTimeMillis().toDateAndTimeString()}.png") + File(targetDir, "temp_${name}.png") val bytes = ByteArrayOutputStream() bm.compress(Bitmap.CompressFormat.PNG, 100, bytes) val bitmapData = bytes.toByteArray() diff --git a/app/src/main/java/com/ojhdtapp/parabox/extension/auto/data/AppDatabase.kt b/app/src/main/java/com/ojhdtapp/parabox/extension/auto/data/AppDatabase.kt index 9a43c28..e2522cf 100644 --- a/app/src/main/java/com/ojhdtapp/parabox/extension/auto/data/AppDatabase.kt +++ b/app/src/main/java/com/ojhdtapp/parabox/extension/auto/data/AppDatabase.kt @@ -2,9 +2,11 @@ package com.ojhdtapp.parabox.extension.auto.data import androidx.room.Database import androidx.room.RoomDatabase +import com.ojhdtapp.parabox.extension.auto.domain.model.AppModel import com.ojhdtapp.parabox.extension.auto.domain.model.WxContact -@Database(entities = [WxContact::class], version = 1, exportSchema = false) +@Database(entities = [WxContact::class, AppModel::class], version = 1, exportSchema = false) abstract class AppDatabase : RoomDatabase() { abstract val wxContactDao: WxContactDao + abstract val appModelDao: AppModelDao } \ No newline at end of file diff --git a/app/src/main/java/com/ojhdtapp/parabox/extension/auto/data/AppModelDao.kt b/app/src/main/java/com/ojhdtapp/parabox/extension/auto/data/AppModelDao.kt new file mode 100644 index 0000000..366c92d --- /dev/null +++ b/app/src/main/java/com/ojhdtapp/parabox/extension/auto/data/AppModelDao.kt @@ -0,0 +1,25 @@ +package com.ojhdtapp.parabox.extension.auto.data + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import androidx.room.Update +import com.ojhdtapp.parabox.extension.auto.domain.model.AppModel +import com.ojhdtapp.parabox.extension.auto.domain.model.AppModelDisabledUpdate +import kotlinx.coroutines.flow.Flow + +@Dao +interface AppModelDao { + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insert(appModel: AppModel): Long + + @Query("SELECT * FROM appmodel WHERE packageName = :packageName LIMIT 1") + suspend fun queryByPackageName(packageName: String): AppModel? + + @Update(entity = AppModel::class) + fun updateDisabledState(appModelDisabledUpdate: AppModelDisabledUpdate) + + @Query("SELECT * FROM appmodel") + fun getAll(): Flow> +} \ No newline at end of file diff --git a/app/src/main/java/com/ojhdtapp/parabox/extension/auto/domain/model/AppModel.kt b/app/src/main/java/com/ojhdtapp/parabox/extension/auto/domain/model/AppModel.kt new file mode 100644 index 0000000..886fb32 --- /dev/null +++ b/app/src/main/java/com/ojhdtapp/parabox/extension/auto/domain/model/AppModel.kt @@ -0,0 +1,18 @@ +package com.ojhdtapp.parabox.extension.auto.domain.model + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity +data class AppModel( + val packageName: String, + val appName: String, + @PrimaryKey(autoGenerate = true) val id: Long = 0, + val disabled: Boolean = false, +) + +data class AppModelDisabledUpdate( + @ColumnInfo(name = "id") val id: Long, + @ColumnInfo(name = "disabled") val disabled: Boolean +) \ No newline at end of file diff --git a/app/src/main/java/com/ojhdtapp/parabox/extension/auto/domain/service/ConnService.kt b/app/src/main/java/com/ojhdtapp/parabox/extension/auto/domain/service/ConnService.kt index 4233cda..5126a5c 100644 --- a/app/src/main/java/com/ojhdtapp/parabox/extension/auto/domain/service/ConnService.kt +++ b/app/src/main/java/com/ojhdtapp/parabox/extension/auto/domain/service/ConnService.kt @@ -5,19 +5,16 @@ import android.content.ComponentName import android.content.Intent import android.content.ServiceConnection import android.content.pm.PackageManager -import android.graphics.Bitmap +import android.content.pm.PackageManager.NameNotFoundException import android.graphics.drawable.Icon -import android.os.Bundle import android.os.IBinder import android.os.Message -import android.service.notification.NotificationListenerService import android.service.notification.StatusBarNotification import android.util.Log import androidx.core.graphics.drawable.toBitmap import androidx.lifecycle.lifecycleScope import com.ojhdtapp.paraboxdevelopmentkit.connector.ParaboxKey import com.ojhdtapp.paraboxdevelopmentkit.connector.ParaboxMetadata -import com.ojhdtapp.paraboxdevelopmentkit.connector.ParaboxResult import com.ojhdtapp.paraboxdevelopmentkit.connector.ParaboxService import com.ojhdtapp.paraboxdevelopmentkit.messagedto.PluginConnection import com.ojhdtapp.paraboxdevelopmentkit.messagedto.Profile @@ -31,11 +28,10 @@ import com.ojhdtapp.parabox.extension.auto.core.util.FileUtil import com.ojhdtapp.parabox.extension.auto.core.util.NotificationUtil import com.ojhdtapp.parabox.extension.auto.core.util.dataStore import com.ojhdtapp.parabox.extension.auto.data.AppDatabase +import com.ojhdtapp.parabox.extension.auto.domain.model.AppModel import com.ojhdtapp.parabox.extension.auto.domain.model.WxContact -import com.ojhdtapp.parabox.extension.auto.domain.util.CustomKey import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.delay import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import javax.inject.Inject @@ -56,15 +52,30 @@ class ConnService : ParaboxService() { private fun receiveWXSbn(sbn: StatusBarNotification) { val time = sbn.postTime - Log.d("parabox", "receiveWXSbn: $time") - val title = sbn.notification.extras.getString(Notification.EXTRA_TITLE, "") - val content = sbn.notification.extras.getString(Notification.EXTRA_TEXT, "") + val title = sbn.notification.extras.getCharSequence(Notification.EXTRA_TITLE, "").toString() + val content = + sbn.notification.extras.getCharSequence(Notification.EXTRA_TEXT, "").toString() + if (content.contains("撤回了一条消息")) return val icon = sbn.notification.extras.getParcelable(Notification.EXTRA_LARGE_ICON) val bitmap = icon?.loadDrawable(this)?.toBitmap() - if (content.contains("撤回了一条消息")) return + val wxIconBitmap = FileUtil.getAppIcon(baseContext, sbn.packageName) + val wxIconUri = wxIconBitmap?.let { + FileUtil.getUriFromBitmap(baseContext, it, "微信").apply { + grantUriPermission( + "com.ojhdtapp.parabox", + this, + Intent.FLAG_GRANT_READ_URI_PERMISSION + ) + grantUriPermission( + "com.ojhdtapp.parabox", + this, + Intent.FLAG_GRANT_WRITE_URI_PERMISSION + ) + } + } lifecycleScope.launch(Dispatchers.IO) { val avatarUri = bitmap?.let { - FileUtil.getUriFromBitmap(baseContext, it).apply { + FileUtil.getUriFromBitmap(baseContext, it, title).apply { grantUriPermission( "com.ojhdtapp.parabox", this, @@ -82,43 +93,181 @@ class ConnService : ParaboxService() { if (contactId == null) { contactId = database.wxContactDao.insert(WxContact(title)) } - contactId?.let { id -> - Log.d("parabox", "receiveWXSbn: $id") - wxSbnMap.put(id, sbn) - val processedContent = content.replace("\\[\\d+条\\]".toRegex(), "") - val arr = processedContent.split(":".toRegex(), 2) - val isGroup = arr.size > 1 - val profile = Profile( - name = if (isGroup) arr.first() else title, - avatar = null, - id = id, - avatarUri = if (isGroup) null else avatarUri + val id = contactId + 10000 + wxSbnMap.put(id, sbn) + val processedContent = content.replace("\\[\\d+条\\]".toRegex(), "") + val arr = processedContent.split(":".toRegex(), 2) + val isGroup = arr.size > 1 + val profile = Profile( + name = if (isGroup) arr.first() else title, + avatar = null, + id = null, + avatarUri = if (isGroup) wxIconUri else avatarUri + ) + val subjectProfile = Profile( + name = title, + avatar = null, + id = id, + avatarUri = avatarUri + ) + receiveMessage( + ReceiveMessageDto( + contents = listOf(PlainText(arr.last())), + profile = profile, + subjectProfile = subjectProfile, + timestamp = time, + messageId = null, + pluginConnection = PluginConnection( + connectionType = connectionType, + sendTargetType = if (isGroup) SendTargetType.GROUP else SendTargetType.USER, + id = id + ) + ), + onResult = { + Log.d("parabox", "receiveMessage result: $it") + } + ) + } + } + } + + private fun receiveConversationSbn(sbn: StatusBarNotification) { + val time = sbn.postTime + val title = sbn.notification.extras.getCharSequence(Notification.EXTRA_TITLE, "").toString() + val content = + sbn.notification.extras.getCharSequence(Notification.EXTRA_TEXT, "").toString() +// val smallIconId = sbn.notification.extras.getInt(Notification.EXTRA_SMALL_ICON, 0) +// val bitmap: Bitmap? = if(smallIconId == 0) null else { +// FileUtil.getSmallIcon(baseContext, sbn.packageName, smallIconId) +// } + lifecycleScope.launch(Dispatchers.IO) { + val appModel: AppModel = + database.appModelDao.queryByPackageName(sbn.packageName) ?: run { + val ai = try { + packageManager.getApplicationInfo(sbn.packageName, 0) + } catch (e: NameNotFoundException) { + null + } + val appName = ai?.let { packageManager.getApplicationLabel(it).toString() } + AppModel( + packageName = sbn.packageName, + appName = appName ?: "Unknown", + ).also { + database.appModelDao.insert(it) + } + } + if (appModel.disabled) return@launch + val bitmap = FileUtil.getAppIcon(baseContext, sbn.packageName) + val avatarUri = bitmap?.let { + FileUtil.getUriFromBitmap(baseContext, it, appModel.appName).apply { + grantUriPermission( + "com.ojhdtapp.parabox", + this, + Intent.FLAG_GRANT_READ_URI_PERMISSION + ) + grantUriPermission( + "com.ojhdtapp.parabox", + this, + Intent.FLAG_GRANT_WRITE_URI_PERMISSION + ) + } + } + val profile = Profile( + name = title, + avatar = null, + id = null, + avatarUri = avatarUri + ) + val subjectProfile = Profile( + name = appModel.appName, + avatar = null, + id = appModel.id, + avatarUri = avatarUri + ) + receiveMessage( + ReceiveMessageDto( + contents = listOf(PlainText(text = content)), + profile = profile, + subjectProfile = subjectProfile, + timestamp = time, + messageId = null, + pluginConnection = PluginConnection( + connectionType = connectionType, + sendTargetType = SendTargetType.GROUP, + id = appModel.id ) - val subjectProfile = Profile( - name = title, - avatar = null, - id = id, - avatarUri = avatarUri + ), + onResult = { + Log.d("parabox", "receiveMessage result: $it") + } + ) + } + } + + private fun receiveOtherSbn(sbn: StatusBarNotification) { + val time = sbn.postTime + val title = sbn.notification.extras.getCharSequence(Notification.EXTRA_TITLE, "").toString() + val content = + sbn.notification.extras.getCharSequence(Notification.EXTRA_TEXT, "").toString() +// val smallIconId = sbn.notification.extras.getInt(Notification.EXTRA_SMALL_ICON, 0) +// val bitmap: Bitmap? = if(smallIconId == 0) null else { +// FileUtil.getSmallIcon(baseContext, sbn.packageName, smallIconId) +// } + lifecycleScope.launch(Dispatchers.IO) { + val appModel: AppModel = + database.appModelDao.queryByPackageName(sbn.packageName) ?: run { + val ai = try { + packageManager.getApplicationInfo(sbn.packageName, 0) + } catch (e: NameNotFoundException) { + null + } + val appName = ai?.let { packageManager.getApplicationLabel(it).toString() } + AppModel( + packageName = sbn.packageName, + appName = appName ?: "Unknown", + ).also { + database.appModelDao.insert(it) + } + } + if (appModel.disabled) return@launch + val bitmap = FileUtil.getAppIcon(baseContext, sbn.packageName) + val avatarUri = bitmap?.let { + FileUtil.getUriFromBitmap(baseContext, it, appModel.appName).apply { + grantUriPermission( + "com.ojhdtapp.parabox", + this, + Intent.FLAG_GRANT_READ_URI_PERMISSION ) - receiveMessage( - ReceiveMessageDto( - contents = listOf(PlainText(arr.last())), - profile = profile, - subjectProfile = subjectProfile, - timestamp = time, - messageId = null, - pluginConnection = PluginConnection( - connectionType = connectionType, - sendTargetType = if (isGroup) SendTargetType.GROUP else SendTargetType.USER, - id = id - ) - ), - onResult = { - Log.d("parabox", "receiveMessage result: $it") - } + grantUriPermission( + "com.ojhdtapp.parabox", + this, + Intent.FLAG_GRANT_WRITE_URI_PERMISSION ) } } + val profile = Profile( + name = appModel.appName, + avatar = null, + id = appModel.id, + avatarUri = avatarUri + ) + receiveMessage( + ReceiveMessageDto( + contents = listOf(PlainText(text = "$title\n$content")), + profile = profile, + subjectProfile = profile, + timestamp = time, + messageId = null, + pluginConnection = PluginConnection( + connectionType = connectionType, + sendTargetType = SendTargetType.USER, + id = appModel.id + ) + ), + onResult = { + Log.d("parabox", "receiveMessage result: $it") + } + ) } } @@ -200,7 +349,18 @@ class ConnService : ParaboxService() { "com.tencent.mm" -> { receiveWXSbn(sbn) } - else -> {} + in listOf( + "com.google.android.apps.messaging", + "com.android.mms", + "com.google.android.gm", + "com.google.android.youtube", + "com.tencent.mobileqq" + ) -> { + receiveConversationSbn(sbn) + } + else -> { + receiveOtherSbn(sbn) + } } } }) diff --git a/app/src/main/java/com/ojhdtapp/parabox/extension/auto/ui/main/AppModelDialog.kt b/app/src/main/java/com/ojhdtapp/parabox/extension/auto/ui/main/AppModelDialog.kt new file mode 100644 index 0000000..4ac60f3 --- /dev/null +++ b/app/src/main/java/com/ojhdtapp/parabox/extension/auto/ui/main/AppModelDialog.kt @@ -0,0 +1,71 @@ +package com.ojhdtapp.parabox.extension.auto.ui.main + +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import com.ojhdtapp.parabox.extension.auto.domain.model.AppModel + +@Composable +fun AppModelDialog( + modifier: Modifier = Modifier, + showDialog: Boolean, + appModelList: List, + onValueChange: (target: AppModel, value: Boolean) -> Unit, + onDismiss: () -> Unit, +) { + if (showDialog) { + AlertDialog( + onDismissRequest = onDismiss, + confirmButton = { + TextButton(onClick = onDismiss) { + Text(text = "完成") + } + }, + title = { + Text(text = "应用列表") + }, + text = { + LazyColumn( + modifier = Modifier.heightIn(0.dp, 480.dp), + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + item { + if (appModelList.isEmpty()) { + Box( + modifier = Modifier + .fillMaxWidth() + .height(160.dp), + contentAlignment = Alignment.Center + ) { + Text(text = "暂无会话") + } + } + } + items(items = appModelList, key = { it.id }) { + Row( + modifier = modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically + ) { + Text( + modifier = Modifier.weight(1f), + text = it.appName, + style = MaterialTheme.typography.bodyMedium, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + Checkbox( + checked = it.disabled, + onCheckedChange = { value -> onValueChange(it, value) }) + } + } + } + } + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ojhdtapp/parabox/extension/auto/ui/main/MainScreen.kt b/app/src/main/java/com/ojhdtapp/parabox/extension/auto/ui/main/MainScreen.kt index 43a2516..977351f 100644 --- a/app/src/main/java/com/ojhdtapp/parabox/extension/auto/ui/main/MainScreen.kt +++ b/app/src/main/java/com/ojhdtapp/parabox/extension/auto/ui/main/MainScreen.kt @@ -48,6 +48,7 @@ import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import com.ojhdtapp.parabox.extension.auto.MainActivity import com.ojhdtapp.parabox.extension.auto.R +import com.ojhdtapp.parabox.extension.auto.core.util.BrowserUtil import com.ojhdtapp.parabox.extension.auto.core.util.launchPlayStore import com.ojhdtapp.parabox.extension.auto.domain.util.ServiceStatus import com.ojhdtapp.parabox.extension.auto.ui.util.NormalPreference @@ -85,6 +86,22 @@ fun MainScreen( mutableStateOf(false) } + var showDialog by remember { + mutableStateOf(false) + } + + AppModelDialog( + showDialog = showDialog, + appModelList = viewModel.appModelStateFlow.collectAsState().value, + onValueChange = { target, value -> + viewModel.updateAppModelDisabledState( + target.id, + value + ) + }, + onDismiss = { showDialog = false } + ) + Scaffold( modifier = modifier, topBar = { @@ -140,7 +157,10 @@ fun MainScreen( (context as MainActivity).isAndroidAutoInstalled() } } - LazyColumn(contentPadding = paddingValues) { + LazyColumn( + contentPadding = paddingValues, + modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection) + ) { item { Spacer(modifier = Modifier.height(16.dp)) } @@ -197,7 +217,7 @@ fun MainScreen( ) Spacer(modifier = Modifier.height(16.dp)) Text( - text = "本插件将为 Parabox 添加通知消息监听,需首先安装主端", + text = "本插件将为 Parabox 添加通知消息监听接入,需首先安装主端", style = MaterialTheme.typography.labelLarge, color = MaterialTheme.colorScheme.onSurfaceVariant ) @@ -275,12 +295,17 @@ fun MainScreen( } } } + item { + NormalPreference(title = "对指定应用禁用监听", subtitle = "对于某些不需要监听通知的应用,可于此处禁用") { + showDialog = true + } + } item { PreferencesCategory(text = "关于") } item { NormalPreference(title = "版本", subtitle = viewModel.appVersion) { - + BrowserUtil.launchURL(context, "https://github.com/Parabox-App/parabox-extension-auto") } } } diff --git a/app/src/main/java/com/ojhdtapp/parabox/extension/auto/ui/main/MainViewModel.kt b/app/src/main/java/com/ojhdtapp/parabox/extension/auto/ui/main/MainViewModel.kt index e0ed791..3efcb72 100644 --- a/app/src/main/java/com/ojhdtapp/parabox/extension/auto/ui/main/MainViewModel.kt +++ b/app/src/main/java/com/ojhdtapp/parabox/extension/auto/ui/main/MainViewModel.kt @@ -9,16 +9,13 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.ojhdtapp.parabox.extension.auto.core.util.DataStoreKeys import com.ojhdtapp.parabox.extension.auto.core.util.dataStore +import com.ojhdtapp.parabox.extension.auto.data.AppDatabase +import com.ojhdtapp.parabox.extension.auto.domain.model.AppModelDisabledUpdate import com.ojhdtapp.parabox.extension.auto.domain.util.ServiceStatus import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.qualifiers.ApplicationContext -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.catch -import kotlinx.coroutines.flow.map +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import java.io.IOException import javax.inject.Inject @@ -26,6 +23,7 @@ import javax.inject.Inject @HiltViewModel class MainViewModel @Inject constructor( @ApplicationContext val context: Context, + val database: AppDatabase, ) : ViewModel() { // UiEvent private val _uiEventFlow = MutableSharedFlow() @@ -97,14 +95,28 @@ class MainViewModel @Inject constructor( } private val _refreshingKey = mutableStateOf(0) - val refreshingKey : State = _refreshingKey - fun refreshKey(){ + val refreshingKey: State = _refreshingKey + fun refreshKey() { _refreshingKey.value = _refreshingKey.value + 1 } + val appModelStateFlow = database.appModelDao.getAll().stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5000), + initialValue = emptyList() + ) + + fun updateAppModelDisabledState(id: Long, value: Boolean) { + viewModelScope.launch(Dispatchers.IO) { + database.appModelDao.updateDisabledState( + AppModelDisabledUpdate(id, value) + ) + } + } + val appVersion = com.ojhdtapp.parabox.extension.auto.BuildConfig.VERSION_NAME } -sealed interface UiEvent{ - data class ShowSnackbar(val message: String): UiEvent +sealed interface UiEvent { + data class ShowSnackbar(val message: String) : UiEvent } \ No newline at end of file