Skip to content

Commit

Permalink
[feat]: blacklisting in crash details
Browse files Browse the repository at this point in the history
  • Loading branch information
F0x1d committed Aug 17, 2024
1 parent 3d35e39 commit 2968292
Show file tree
Hide file tree
Showing 12 changed files with 94 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ interface DisabledAppDao {
@Query("SELECT * FROM DisabledApp WHERE package_name = :packageName")
suspend fun getByPackageName(packageName: String): DisabledApp?

@Query("SELECT * FROM DisabledApp WHERE package_name = :packageName")
fun getByPackageNameAsFlow(packageName: String): Flow<DisabledApp?>

@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(item: DisabledApp)

Expand Down
10 changes: 10 additions & 0 deletions core/ui/src/main/res/drawable/ic_check_circle.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:tint="?colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000000"
android:pathData="M12 2C6.5 2 2 6.5 2 12S6.5 22 12 22 22 17.5 22 12 17.5 2 12 2M12 20C7.59 20 4 16.41 4 12S7.59 4 12 4 20 7.59 20 12 16.41 20 12 20M16.59 7.58L10 14.17L7.41 11.59L6 13L10 17L18 9L16.59 7.58Z" />
</vector>
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class AppsPickerFragment: BaseComposeViewModelFragment<AppsPickerViewModel>() {
state to checkedApps
}.map { (state, checkedAppPackageNames) ->
state.copy(
topBarTitle = handler.provideTopAppBarTitle(requireContext()),
topBarTitle = handler.providePickerTopAppBarTitle(requireContext()),
checkedAppPackageNames = checkedAppPackageNames.toImmutableSet(),
multiplySelectionEnabled = handler.supportsMultiplySelection,
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.f0x1d.logfox.feature.apps.picker.ui.fragment.picker.compose

import androidx.activity.compose.BackHandler
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
Expand Down Expand Up @@ -125,6 +126,7 @@ private fun LoadingContent(modifier: Modifier = Modifier) {
}
}

@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun AppsContent(
items: ImmutableList<InstalledApp>,
Expand All @@ -142,7 +144,7 @@ private fun AppsContent(
key = { _, item -> item.id },
contentType = { _, item -> item.javaClass },
) { index, item ->
Column {
Column(modifier = Modifier.animateItemPlacement()) {
AppContent(
item = item,
isChecked = remember(checkedItems) {
Expand All @@ -166,7 +168,7 @@ private fun AppsContent(
}

@Composable
private fun AppContent(
internal fun AppContent(
item: InstalledApp,
isChecked: Boolean,
onClick: (InstalledApp) -> Unit,
Expand Down Expand Up @@ -218,7 +220,7 @@ private fun AppContent(
}
}

private val MockApps = persistentListOf(
internal val MockApps = persistentListOf(
InstalledApp("LogFox", "com.f0x1d.logfox"),
InstalledApp("Sense", "com.f0x1d.sense"),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ interface AppsPickerResultHandler {
val supportsMultiplySelection: Boolean get() = false
val checkedAppPackageNames: Flow<Set<String>> get() = flowOf(emptySet())

fun provideTopAppBarTitle(context: Context) = context.getString(Strings.apps)
fun providePickerTopAppBarTitle(context: Context) = context.getString(Strings.apps)

fun onAppChecked(app: InstalledApp, checked: Boolean) = Unit

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.withContext
import javax.inject.Inject

interface DisabledAppsRepository : DatabaseProxyRepository<DisabledApp> {
suspend fun isDisabledFor(packageName: String): Boolean
fun disabledForFlow(packageName: String): Flow<Boolean>

suspend fun checkApp(packageName: String)
suspend fun checkApp(packageName: String, checked: Boolean)
Expand All @@ -27,6 +29,12 @@ internal class DisabledAppsRepositoryImpl @Inject constructor(
database.disabledApps().getByPackageName(packageName) != null
}

override fun disabledForFlow(packageName: String): Flow<Boolean> =
database.disabledApps()
.getByPackageNameAsFlow(packageName)
.map { it != null }
.flowOn(ioDispatcher)

override suspend fun checkApp(packageName: String) = checkApp(
packageName = packageName,
checked = withContext(ioDispatcher) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ import com.f0x1d.logfox.feature.crashes.core.controller.notificationChannelId
import com.f0x1d.logfox.feature.crashes.databinding.FragmentCrashDetailsBinding
import com.f0x1d.logfox.feature.crashes.viewmodel.CrashDetailsViewModel
import com.f0x1d.logfox.strings.Strings
import com.f0x1d.logfox.ui.Icons
import com.f0x1d.logfox.ui.dialog.showAreYouSureDeleteDialog
import com.f0x1d.logfox.ui.dialog.showAreYouSureDialog
import com.f0x1d.logfox.ui.view.loadIcon
import com.f0x1d.logfox.ui.view.setClickListenerOn
import com.f0x1d.logfox.ui.view.setupBackButtonForNavController
Expand Down Expand Up @@ -53,10 +55,28 @@ class CrashDetailsFragment: BaseViewModelFragment<CrashDetailsViewModel, Fragmen
}

toolbar.setupBackButtonForNavController()
toolbar.menu.apply {
findItem(R.id.notifications_item).setVisible(
notificationsChannelsAvailable
&& viewModel.useSeparateNotificationsChannelsForCrashes
)
}

viewModel.crash.collectWithLifecycle {
setupFor(it ?: return@collectWithLifecycle)
}

viewModel.blacklisted.collectWithLifecycle { blacklisted ->
toolbar.menu.findItem(R.id.blacklist_item).apply {
if (blacklisted == null) {
isVisible = false
} else {
isVisible = true
setIcon(if (blacklisted) Icons.ic_check_circle else Icons.ic_block)
setTitle(if (blacklisted) Strings.remove_from_blacklist else Strings.add_to_blacklist)
}
}
}
}

@SuppressLint("InlinedApi")
Expand All @@ -69,17 +89,24 @@ class CrashDetailsFragment: BaseViewModelFragment<CrashDetailsViewModel, Fragmen
data = Uri.fromParts("package", appCrash.packageName, null)
}.let(::startActivity)
}

findItem(R.id.notifications_item).setVisible(
notificationsChannelsAvailable
&& viewModel.useSeparateNotificationsChannelsForCrashes
)
setClickListenerOn(R.id.notifications_item) {
Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS).apply {
putExtra(Settings.EXTRA_APP_PACKAGE, requireContext().packageName)
putExtra(Settings.EXTRA_CHANNEL_ID, appCrash.notificationChannelId)
}.let(::startActivity)
}
setClickListenerOn(R.id.blacklist_item) {
if (viewModel.blacklisted.value == false) {
showAreYouSureDialog(
title = Strings.blacklist,
message = Strings.warning_blacklist,
) {
viewModel.changeBlacklist(appCrash)
}
} else {
viewModel.changeBlacklist(appCrash)
}
}
setClickListenerOn(R.id.delete_item) {
showAreYouSureDeleteDialog {
viewModel.deleteCrash(appCrash)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,16 @@ import com.f0x1d.logfox.arch.viewmodel.BaseViewModel
import com.f0x1d.logfox.database.entity.AppCrash
import com.f0x1d.logfox.datetime.DateTimeFormatter
import com.f0x1d.logfox.feature.crashes.core.repository.CrashesRepository
import com.f0x1d.logfox.feature.crashes.core.repository.DisabledAppsRepository
import com.f0x1d.logfox.feature.crashes.di.CrashId
import com.f0x1d.logfox.model.deviceData
import com.f0x1d.logfox.preferences.shared.AppPreferences
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import javax.inject.Inject
Expand All @@ -24,6 +28,7 @@ import javax.inject.Inject
class CrashDetailsViewModel @Inject constructor(
@CrashId val crashId: Long,
private val crashesRepository: CrashesRepository,
private val disabledAppsRepository: DisabledAppsRepository,
private val appPreferences: AppPreferences,
@IODispatcher private val ioDispatcher: CoroutineDispatcher,
dateTimeFormatter: DateTimeFormatter,
Expand All @@ -46,6 +51,19 @@ class CrashDetailsViewModel @Inject constructor(
initialValue = null,
)

@OptIn(ExperimentalCoroutinesApi::class)
val blacklisted = crashesRepository.getByIdAsFlow(crashId)
.flatMapLatest { crash ->
crash?.let {
disabledAppsRepository.disabledForFlow(it.packageName)
} ?: flowOf(null)
}
.stateIn(
scope = viewModelScope,
started = SharingStarted.Eagerly,
initialValue = null,
)

val wrapCrashLogLines get() = appPreferences.wrapCrashLogLines
val useSeparateNotificationsChannelsForCrashes get() = appPreferences.useSeparateNotificationsChannelsForCrashes

Expand Down Expand Up @@ -74,6 +92,10 @@ class CrashDetailsViewModel @Inject constructor(
}
}

fun changeBlacklist(appCrash: AppCrash) = launchCatching {
disabledAppsRepository.checkApp(appCrash.packageName)
}

fun deleteCrash(appCrash: AppCrash) = launchCatching {
crashesRepository.delete(appCrash)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ class CrashesViewModel @Inject constructor(
apps.map(DisabledApp::packageName).toSet()
}

override fun provideTopAppBarTitle(context: Context): String =
override fun providePickerTopAppBarTitle(context: Context): String =
context.getString(Strings.blacklist)

override fun onAppChecked(app: InstalledApp, checked: Boolean) {
Expand Down
6 changes: 6 additions & 0 deletions feature/crashes/src/main/res/menu/crash_details_menu.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@
android:title="@string/notifications"
app:showAsAction="ifRoom" />

<item
android:id="@+id/blacklist_item"
android:icon="@drawable/ic_block"
android:title="@string/add_to_blacklist"
app:showAsAction="ifRoom" />

<item
android:id="@+id/delete_item"
android:icon="@drawable/ic_delete"
Expand Down
2 changes: 2 additions & 0 deletions strings/src/main/res/values-ru/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -155,4 +155,6 @@
<string name="open_crashes_page_on_startup">Открывать вкладку сбоев при запуске</string>
<string name="wrap_log_lines_in_details">Перенос строк в деталях лога</string>
<string name="blacklist">Черный список</string>
<string name="add_to_blacklist">Добавить в черный список</string>
<string name="remove_from_blacklist">Убрать из черного списка</string>
</resources>
3 changes: 3 additions & 0 deletions strings/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -164,4 +164,7 @@
<string name="open_crashes_page_on_startup">Open crashes page on startup</string>
<string name="wrap_log_lines_in_details">Wrap log lines in details</string>
<string name="blacklist">Blacklist</string>
<string name="add_to_blacklist">Add to blacklist</string>
<string name="remove_from_blacklist">Remove from blacklist</string>
<string name="warning_blacklist">Are you sure want to add this app to blacklist? LogFox does not observe crashes for blacklisted apps</string>
</resources>

0 comments on commit 2968292

Please sign in to comment.