diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 16c6fad3..7a24e863 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -9,8 +9,8 @@ android { defaultConfig { applicationId = logFoxPackageName - versionCode = 60 - versionName = "2.0.0" + versionCode = 61 + versionName = "2.0.1" } } diff --git a/core/core-database/src/main/kotlin/com/f0x1d/logfox/database/entity/AppCrash.kt b/core/core-database/src/main/kotlin/com/f0x1d/logfox/database/entity/AppCrash.kt index b56f4aa9..0b44c4f3 100644 --- a/core/core-database/src/main/kotlin/com/f0x1d/logfox/database/entity/AppCrash.kt +++ b/core/core-database/src/main/kotlin/com/f0x1d/logfox/database/entity/AppCrash.kt @@ -123,3 +123,8 @@ class FileConverter { @TypeConverter fun fromFile(value: File) = value.absolutePath } + +data class AppCrashesCount( + val lastCrash: AppCrash, + val count: Int = 1, +) diff --git a/core/core-preferences/src/main/kotlin/com/f0x1d/logfox/preferences/shared/AppPreferences.kt b/core/core-preferences/src/main/kotlin/com/f0x1d/logfox/preferences/shared/AppPreferences.kt index 017eb095..2c0141e0 100644 --- a/core/core-preferences/src/main/kotlin/com/f0x1d/logfox/preferences/shared/AppPreferences.kt +++ b/core/core-preferences/src/main/kotlin/com/f0x1d/logfox/preferences/shared/AppPreferences.kt @@ -8,6 +8,7 @@ import com.f0x1d.logfox.database.entity.CrashType import com.f0x1d.logfox.model.logline.LogLine import com.f0x1d.logfox.model.preferences.ShowLogValues import com.f0x1d.logfox.preferences.shared.base.BasePreferences +import com.f0x1d.logfox.preferences.shared.crashes.CrashesSort import dagger.hilt.android.qualifiers.ApplicationContext import java.util.Date import javax.inject.Inject @@ -157,6 +158,20 @@ class AppPreferences @Inject constructor( val showLogValues get() = cachedShowLogValues ?: updateCachedShowLogsValues() + val crashesSortType get() = flowSharedPreferences.getEnum( + key = "pref_crashes_sort_type", + defaultValue = CrashesSort.NEW, + ) + val crashesSortReversedOrder get() = flowSharedPreferences.getBoolean( + key = "pref_crashes_sort_reversed_order", + defaultValue = false, + ) + + fun updateCrashesSortSettings(sortType: CrashesSort, sortInReversedOrder: Boolean) { + put("pref_crashes_sort_type", sortType.name) + put("pref_crashes_sort_reversed_order", sortInReversedOrder) + } + fun collectingFor(crashType: CrashType) = get( key = "pref_collect_${crashType.readableName.lowercase()}", defaultValue = true diff --git a/core/core-preferences/src/main/kotlin/com/f0x1d/logfox/preferences/shared/crashes/CrashesSort.kt b/core/core-preferences/src/main/kotlin/com/f0x1d/logfox/preferences/shared/crashes/CrashesSort.kt new file mode 100644 index 00000000..08d7999a --- /dev/null +++ b/core/core-preferences/src/main/kotlin/com/f0x1d/logfox/preferences/shared/crashes/CrashesSort.kt @@ -0,0 +1,31 @@ +package com.f0x1d.logfox.preferences.shared.crashes + +import androidx.annotation.Keep +import androidx.annotation.StringRes +import com.f0x1d.logfox.database.entity.AppCrashesCount +import com.f0x1d.logfox.strings.Strings + +@Keep +enum class CrashesSort( + @StringRes val titleRes: Int, + val sorter: (List) -> List = { it }, +) { + NAME( + titleRes = Strings.sort_by_name, + sorter = { crashes -> + crashes.sortedBy { it.lastCrash.appName ?: it.lastCrash.packageName } + }, + ), + NEW( + titleRes = Strings.sort_by_new, + sorter = { crashes -> + crashes.sortedByDescending { it.lastCrash.dateAndTime } + }, + ), + COUNT( + titleRes = Strings.sort_by_count, + sorter = { crashes -> + crashes.sortedByDescending { it.count } + }, + ), +} diff --git a/core/core-ui/src/main/res/drawable/ic_sort.xml b/core/core-ui/src/main/res/drawable/ic_sort.xml new file mode 100644 index 00000000..84fbb970 --- /dev/null +++ b/core/core-ui/src/main/res/drawable/ic_sort.xml @@ -0,0 +1,13 @@ + + + + + diff --git a/feature/feature-crashes/src/main/kotlin/com/f0x1d/logfox/feature/crashes/adapter/CrashesAdapter.kt b/feature/feature-crashes/src/main/kotlin/com/f0x1d/logfox/feature/crashes/adapter/CrashesAdapter.kt index 934e7961..97c561f3 100644 --- a/feature/feature-crashes/src/main/kotlin/com/f0x1d/logfox/feature/crashes/adapter/CrashesAdapter.kt +++ b/feature/feature-crashes/src/main/kotlin/com/f0x1d/logfox/feature/crashes/adapter/CrashesAdapter.kt @@ -4,8 +4,8 @@ import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.DiffUtil import com.f0x1d.logfox.arch.adapter.BaseListAdapter +import com.f0x1d.logfox.database.entity.AppCrashesCount import com.f0x1d.logfox.feature.crashes.databinding.ItemCrashBinding -import com.f0x1d.logfox.feature.crashes.model.AppCrashesCount import com.f0x1d.logfox.feature.crashes.ui.viewholder.CrashViewHolder class CrashesAdapter( diff --git a/feature/feature-crashes/src/main/kotlin/com/f0x1d/logfox/feature/crashes/model/AppCrashesCount.kt b/feature/feature-crashes/src/main/kotlin/com/f0x1d/logfox/feature/crashes/model/AppCrashesCount.kt deleted file mode 100644 index d7f9477a..00000000 --- a/feature/feature-crashes/src/main/kotlin/com/f0x1d/logfox/feature/crashes/model/AppCrashesCount.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.f0x1d.logfox.feature.crashes.model - -import com.f0x1d.logfox.database.entity.AppCrash - -data class AppCrashesCount( - val lastCrash: AppCrash, - val count: Int = 1, -) diff --git a/feature/feature-crashes/src/main/kotlin/com/f0x1d/logfox/feature/crashes/ui/fragment/list/CrashesFragment.kt b/feature/feature-crashes/src/main/kotlin/com/f0x1d/logfox/feature/crashes/ui/fragment/list/CrashesFragment.kt index 697e31bd..83b6b18c 100644 --- a/feature/feature-crashes/src/main/kotlin/com/f0x1d/logfox/feature/crashes/ui/fragment/list/CrashesFragment.kt +++ b/feature/feature-crashes/src/main/kotlin/com/f0x1d/logfox/feature/crashes/ui/fragment/list/CrashesFragment.kt @@ -15,13 +15,18 @@ import com.f0x1d.logfox.arch.ui.fragment.BaseViewModelFragment import com.f0x1d.logfox.context.isHorizontalOrientation import com.f0x1d.logfox.feature.crashes.R import com.f0x1d.logfox.feature.crashes.adapter.CrashesAdapter +import com.f0x1d.logfox.feature.crashes.databinding.DialogSortingBinding import com.f0x1d.logfox.feature.crashes.databinding.FragmentCrashesBinding +import com.f0x1d.logfox.feature.crashes.databinding.ItemSortBinding import com.f0x1d.logfox.feature.crashes.viewmodel.list.CrashesViewModel import com.f0x1d.logfox.navigation.Directions +import com.f0x1d.logfox.preferences.shared.crashes.CrashesSort +import com.f0x1d.logfox.strings.Strings import com.f0x1d.logfox.ui.density.dpToPx import com.f0x1d.logfox.ui.dialog.showAreYouSureClearDialog import com.f0x1d.logfox.ui.dialog.showAreYouSureDeleteDialog import com.f0x1d.logfox.ui.view.setClickListenerOn +import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.divider.MaterialDividerItemDecoration import com.google.android.material.search.SearchView import dagger.hilt.android.AndroidEntryPoint @@ -95,9 +100,14 @@ class CrashesFragment: BaseViewModelFragment + ItemSortBinding.inflate(layoutInflater).root.apply { + id = View.generateViewId() + text = getString(type.titleRes) + tag = type + + setOnCheckedChangeListener { _, isChecked -> + if (isChecked) { + selectedSortType = type + } + } + } + }.forEach { button -> + dialogBinding.rgSorting.apply { + addView(button) + + if (button.tag == viewModel.currentSort) { + check(button.id) + } + } + } + + dialogBinding.reverseSwitch.apply { + isChecked = sortInReversedOrder + setOnCheckedChangeListener { _, isChecked -> sortInReversedOrder = isChecked } + } + + MaterialAlertDialogBuilder(requireContext()) + .setTitle(Strings.sort) + .setView(dialogBinding.root) + .setPositiveButton(android.R.string.ok) { _, _ -> + viewModel.updateSort( + sortType = selectedSortType, + sortInReversedOrder = sortInReversedOrder, + ) + } + .show() + } } diff --git a/feature/feature-crashes/src/main/kotlin/com/f0x1d/logfox/feature/crashes/ui/viewholder/CrashViewHolder.kt b/feature/feature-crashes/src/main/kotlin/com/f0x1d/logfox/feature/crashes/ui/viewholder/CrashViewHolder.kt index 57341a03..c1610c0a 100644 --- a/feature/feature-crashes/src/main/kotlin/com/f0x1d/logfox/feature/crashes/ui/viewholder/CrashViewHolder.kt +++ b/feature/feature-crashes/src/main/kotlin/com/f0x1d/logfox/feature/crashes/ui/viewholder/CrashViewHolder.kt @@ -3,8 +3,8 @@ package com.f0x1d.logfox.feature.crashes.ui.viewholder import android.annotation.SuppressLint import com.bumptech.glide.Glide import com.f0x1d.logfox.arch.ui.viewholder.BaseViewHolder +import com.f0x1d.logfox.database.entity.AppCrashesCount import com.f0x1d.logfox.feature.crashes.databinding.ItemCrashBinding -import com.f0x1d.logfox.feature.crashes.model.AppCrashesCount import com.f0x1d.logfox.strings.Strings import com.f0x1d.logfox.ui.view.loadIcon import java.util.Date diff --git a/feature/feature-crashes/src/main/kotlin/com/f0x1d/logfox/feature/crashes/viewmodel/list/AppCrashesViewModel.kt b/feature/feature-crashes/src/main/kotlin/com/f0x1d/logfox/feature/crashes/viewmodel/list/AppCrashesViewModel.kt index 6d02910d..9aaba540 100644 --- a/feature/feature-crashes/src/main/kotlin/com/f0x1d/logfox/feature/crashes/viewmodel/list/AppCrashesViewModel.kt +++ b/feature/feature-crashes/src/main/kotlin/com/f0x1d/logfox/feature/crashes/viewmodel/list/AppCrashesViewModel.kt @@ -5,10 +5,10 @@ import androidx.lifecycle.viewModelScope import com.f0x1d.logfox.arch.di.DefaultDispatcher import com.f0x1d.logfox.arch.viewmodel.BaseViewModel import com.f0x1d.logfox.database.entity.AppCrash +import com.f0x1d.logfox.database.entity.AppCrashesCount import com.f0x1d.logfox.feature.crashes.core.repository.CrashesRepository import com.f0x1d.logfox.feature.crashes.di.AppName import com.f0x1d.logfox.feature.crashes.di.PackageName -import com.f0x1d.logfox.feature.crashes.model.AppCrashesCount import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.SharingStarted diff --git a/feature/feature-crashes/src/main/kotlin/com/f0x1d/logfox/feature/crashes/viewmodel/list/CrashesViewModel.kt b/feature/feature-crashes/src/main/kotlin/com/f0x1d/logfox/feature/crashes/viewmodel/list/CrashesViewModel.kt index 41e4d5ed..6062709c 100644 --- a/feature/feature-crashes/src/main/kotlin/com/f0x1d/logfox/feature/crashes/viewmodel/list/CrashesViewModel.kt +++ b/feature/feature-crashes/src/main/kotlin/com/f0x1d/logfox/feature/crashes/viewmodel/list/CrashesViewModel.kt @@ -5,8 +5,10 @@ import androidx.lifecycle.viewModelScope import com.f0x1d.logfox.arch.di.DefaultDispatcher import com.f0x1d.logfox.arch.viewmodel.BaseViewModel import com.f0x1d.logfox.database.entity.AppCrash +import com.f0x1d.logfox.database.entity.AppCrashesCount import com.f0x1d.logfox.feature.crashes.core.repository.CrashesRepository -import com.f0x1d.logfox.feature.crashes.model.AppCrashesCount +import com.f0x1d.logfox.preferences.shared.AppPreferences +import com.f0x1d.logfox.preferences.shared.crashes.CrashesSort import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.MutableStateFlow @@ -23,20 +25,40 @@ import javax.inject.Inject @HiltViewModel class CrashesViewModel @Inject constructor( private val crashesRepository: CrashesRepository, + private val appPreferences: AppPreferences, @DefaultDispatcher private val defaultDispatcher: CoroutineDispatcher, application: Application, ): BaseViewModel(application) { - val crashes = crashesRepository.getAllAsFlow() + val currentSort get() = appPreferences.crashesSortType.get() + val currentSortInReversedOrder get() = appPreferences.crashesSortReversedOrder.get() + + val crashes = combine( + crashesRepository.getAllAsFlow(), + appPreferences.crashesSortType.asFlow(), + appPreferences.crashesSortReversedOrder.asFlow(), + ) { crashes, sortType, sortInReversedOrder -> + CrashesWithSort( + crashes = crashes, + sortType = sortType, + sortInReversedOrder = sortInReversedOrder, + ) + } .distinctUntilChanged() - .map { crashes -> - val groupedCrashes = crashes.groupBy { it.packageName } + .map { crashesWithSort -> + val groupedCrashes = crashesWithSort.crashes.groupBy { it.packageName } groupedCrashes.map { AppCrashesCount( lastCrash = it.value.first(), count = it.value.size ) + }.let(crashesWithSort.sortType.sorter).let { result -> + if (crashesWithSort.sortInReversedOrder) { + result.asReversed() + } else { + result + } } } .flowOn(defaultDispatcher) @@ -70,6 +92,11 @@ class CrashesViewModel @Inject constructor( fun updateQuery(query: String) = this.query.update { query } + fun updateSort(sortType: CrashesSort, sortInReversedOrder: Boolean) = appPreferences.updateCrashesSortSettings( + sortType = sortType, + sortInReversedOrder = sortInReversedOrder, + ) + fun deleteCrashesByPackageName(appCrash: AppCrash) = launchCatching { crashesRepository.deleteAllByPackageName(appCrash) } @@ -81,4 +108,10 @@ class CrashesViewModel @Inject constructor( fun clearCrashes() = launchCatching { crashesRepository.clear() } + + private data class CrashesWithSort( + val crashes: List, + val sortType: CrashesSort, + val sortInReversedOrder: Boolean, + ) } diff --git a/feature/feature-crashes/src/main/res/layout/dialog_sorting.xml b/feature/feature-crashes/src/main/res/layout/dialog_sorting.xml new file mode 100644 index 00000000..6bf593d2 --- /dev/null +++ b/feature/feature-crashes/src/main/res/layout/dialog_sorting.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + diff --git a/feature/feature-crashes/src/main/res/layout/item_sort.xml b/feature/feature-crashes/src/main/res/layout/item_sort.xml new file mode 100644 index 00000000..73a0a269 --- /dev/null +++ b/feature/feature-crashes/src/main/res/layout/item_sort.xml @@ -0,0 +1,7 @@ + + diff --git a/feature/feature-crashes/src/main/res/menu/crashes_menu.xml b/feature/feature-crashes/src/main/res/menu/crashes_menu.xml index 3c66443d..58c6d6a1 100644 --- a/feature/feature-crashes/src/main/res/menu/crashes_menu.xml +++ b/feature/feature-crashes/src/main/res/menu/crashes_menu.xml @@ -2,10 +2,16 @@ + + - \ No newline at end of file + diff --git a/strings/src/main/res/values-ru/strings.xml b/strings/src/main/res/values-ru/strings.xml index eb99c465..f912567d 100644 --- a/strings/src/main/res/values-ru/strings.xml +++ b/strings/src/main/res/values-ru/strings.xml @@ -146,4 +146,9 @@ %s сбои %s сбои %s Настройки персонализированных уведомлений для приложений доступны в деталях сбоя и системных настройках LogFox + Сортировка + В обратном порядке + По имени + По новизне + По количеству сбоев diff --git a/strings/src/main/res/values/strings.xml b/strings/src/main/res/values/strings.xml index 4316e53b..ce90b187 100644 --- a/strings/src/main/res/values/strings.xml +++ b/strings/src/main/res/values/strings.xml @@ -155,4 +155,9 @@ %s crashes %s crashes of %s Per app notifications settings are available in app\'s crash details and LogFox\'s system settings + Sort + In reversed order + By name + By novelty + By crashes count