Add per-app event logging overrides
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package se.ajpanton.notificationlog
|
||||
|
||||
import android.os.Bundle
|
||||
import android.content.res.ColorStateList
|
||||
import android.text.SpannableString
|
||||
import android.text.Spanned
|
||||
import android.text.style.StyleSpan
|
||||
@@ -9,12 +10,16 @@ import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.CheckBox
|
||||
import android.widget.ImageButton
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.color.MaterialColors
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.switchmaterial.SwitchMaterial
|
||||
import se.ajpanton.notificationlog.capture.SeenApps
|
||||
import se.ajpanton.notificationlog.databinding.FragmentEventSettingsBinding
|
||||
import se.ajpanton.notificationlog.settings.AppListItem
|
||||
@@ -23,10 +28,14 @@ import se.ajpanton.notificationlog.settings.AppRuleMode
|
||||
import se.ajpanton.notificationlog.settings.ListedApp
|
||||
import se.ajpanton.notificationlog.settings.AppFilterSettings
|
||||
import se.ajpanton.notificationlog.settings.AppFilterSettingsStore
|
||||
import se.ajpanton.notificationlog.settings.LoggingRuleStore
|
||||
import se.ajpanton.notificationlog.settings.LoggingType
|
||||
import se.ajpanton.notificationlog.settings.PerAppEventSettingsStore
|
||||
|
||||
class FilterAppsFragment : Fragment(R.layout.fragment_event_settings) {
|
||||
private var binding: FragmentEventSettingsBinding? = null
|
||||
private lateinit var store: AppFilterSettingsStore
|
||||
private lateinit var perAppEventSettings: PerAppEventSettingsStore
|
||||
private lateinit var appAdapter: AppListAdapter
|
||||
private var appLoadGeneration = 0
|
||||
|
||||
@@ -34,7 +43,8 @@ class FilterAppsFragment : Fragment(R.layout.fragment_event_settings) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
binding = FragmentEventSettingsBinding.bind(view)
|
||||
store = AppFilterSettingsStore(requireContext())
|
||||
appAdapter = AppListAdapter(::setPackageSelected)
|
||||
perAppEventSettings = PerAppEventSettingsStore(requireContext())
|
||||
appAdapter = AppListAdapter(::setPackageSelected, ::showPerAppEventSettings)
|
||||
binding!!.appList.layoutManager = LinearLayoutManager(requireContext())
|
||||
binding!!.appList.adapter = appAdapter
|
||||
binding!!.appListRefresh.setOnRefreshListener(::reloadAppList)
|
||||
@@ -118,9 +128,10 @@ class FilterAppsFragment : Fragment(R.layout.fragment_event_settings) {
|
||||
packageName = info.packageName,
|
||||
seen = info.packageName in seen,
|
||||
selected = info.packageName in rule.selectedPackages,
|
||||
hasEventOverride = perAppEventSettings.hasOverride(info.packageName),
|
||||
)
|
||||
}
|
||||
val items = AppListOrdering.items(apps, rule.onlySeenApps, rule.seenAppsFirst, rule.mode)
|
||||
val items = AppListOrdering.items(apps, rule.onlySeenApps, rule.seenAppsFirst)
|
||||
activity?.runOnUiThread {
|
||||
if (binding === currentBinding && generation == appLoadGeneration) {
|
||||
appAdapter.submit(items)
|
||||
@@ -140,8 +151,69 @@ class FilterAppsFragment : Fragment(R.layout.fragment_event_settings) {
|
||||
// Do not reload: an unchecked unseen row remains visible until the next requested refresh.
|
||||
}
|
||||
|
||||
private fun showPerAppEventSettings(app: ListedApp) {
|
||||
val eventTypes = listOf(LoggingType.APPEARING, LoggingType.DISAPPEARING, LoggingType.EDITS)
|
||||
val rules = LoggingRuleStore(requireContext())
|
||||
val content = LinearLayout(requireContext()).apply {
|
||||
orientation = LinearLayout.VERTICAL
|
||||
setPadding(dp(24), 0, dp(24), 0)
|
||||
}
|
||||
val useGlobal = SwitchMaterial(requireContext()).apply { text = "Use global" }
|
||||
val eventToggles = eventTypes.associateWith { type ->
|
||||
SwitchMaterial(requireContext()).apply {
|
||||
text = type.label()
|
||||
layoutParams = LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||||
).apply { marginStart = dp(24) }
|
||||
}.also(content::addView)
|
||||
}
|
||||
content.addView(useGlobal, 0)
|
||||
|
||||
fun globalEvents() = eventTypes.associateWith { rules.ruleFor(it).enabled }
|
||||
fun refresh() {
|
||||
val useGlobalSetting = perAppEventSettings.usesGlobal(app.packageName)
|
||||
useGlobal.setOnCheckedChangeListener(null)
|
||||
useGlobal.isChecked = useGlobalSetting
|
||||
eventToggles.forEach { (type, toggle) ->
|
||||
toggle.setOnCheckedChangeListener(null)
|
||||
toggle.isEnabled = !useGlobalSetting
|
||||
toggle.isChecked = perAppEventSettings.isEnabled(
|
||||
app.packageName,
|
||||
type,
|
||||
globalEvents().getValue(type),
|
||||
)
|
||||
toggle.setOnCheckedChangeListener { _, enabled ->
|
||||
perAppEventSettings.setEnabled(app.packageName, type, enabled)
|
||||
}
|
||||
}
|
||||
useGlobal.setOnCheckedChangeListener { _, enabled ->
|
||||
if (enabled) perAppEventSettings.useGlobal(app.packageName)
|
||||
else perAppEventSettings.startUsingOverride(app.packageName, globalEvents())
|
||||
appAdapter.updateEventOverride(app.packageName, !enabled)
|
||||
refresh()
|
||||
}
|
||||
}
|
||||
refresh()
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle("When logging, only log")
|
||||
.setView(content)
|
||||
.setPositiveButton("Close", null)
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun LoggingType.label() = when (this) {
|
||||
LoggingType.APPEARING -> "Appearing"
|
||||
LoggingType.DISAPPEARING -> "Disappearing"
|
||||
LoggingType.EDITS -> "Edits"
|
||||
else -> error("Only event types are shown in the per-app dialog")
|
||||
}
|
||||
|
||||
private fun dp(value: Int): Int = (value * resources.displayMetrics.density).toInt()
|
||||
|
||||
private class AppListAdapter(
|
||||
private val onSelectedChanged: (String, Boolean) -> Unit,
|
||||
private val onEventFilterClicked: (ListedApp) -> Unit,
|
||||
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
private var items: List<AppListItem> = emptyList()
|
||||
|
||||
@@ -162,6 +234,12 @@ class FilterAppsFragment : Fragment(R.layout.fragment_event_settings) {
|
||||
setPadding(0, dp(parent, 4), 0, dp(parent, 4))
|
||||
}
|
||||
val checkbox = CheckBox(parent.context)
|
||||
val filter = ImageButton(parent.context).apply {
|
||||
setImageResource(R.drawable.ic_filter_list)
|
||||
contentDescription = "Edit logging events"
|
||||
layoutParams = LinearLayout.LayoutParams(dp(parent, 48), dp(parent, 48))
|
||||
setPadding(dp(parent, 12), dp(parent, 12), dp(parent, 12), dp(parent, 12))
|
||||
}
|
||||
val textColumn = LinearLayout(parent.context).apply {
|
||||
orientation = LinearLayout.VERTICAL
|
||||
layoutParams = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f)
|
||||
@@ -171,8 +249,9 @@ class FilterAppsFragment : Fragment(R.layout.fragment_event_settings) {
|
||||
textColumn.addView(label)
|
||||
textColumn.addView(packageName)
|
||||
row.addView(checkbox)
|
||||
row.addView(filter)
|
||||
row.addView(textColumn)
|
||||
AppHolder(row, checkbox, label, packageName)
|
||||
AppHolder(row, checkbox, filter, label, packageName)
|
||||
} else if (viewType == SECTION_TITLE) {
|
||||
SectionTitleHolder(TextView(parent.context).apply {
|
||||
layoutParams = RecyclerView.LayoutParams(
|
||||
@@ -208,6 +287,15 @@ class FilterAppsFragment : Fragment(R.layout.fragment_event_settings) {
|
||||
} else current
|
||||
}
|
||||
}
|
||||
holder.filter.contentDescription = "Edit logging events for ${app.label}"
|
||||
holder.filter.imageTintList = ColorStateList.valueOf(
|
||||
MaterialColors.getColor(
|
||||
holder.filter,
|
||||
if (app.hasEventOverride) com.google.android.material.R.attr.colorPrimary
|
||||
else com.google.android.material.R.attr.colorOnSurface,
|
||||
),
|
||||
)
|
||||
holder.filter.setOnClickListener { onEventFilterClicked(app) }
|
||||
}
|
||||
if (holder is SectionTitleHolder && item is AppListItem.SectionTitle) {
|
||||
holder.text.text = item.value
|
||||
@@ -216,9 +304,21 @@ class FilterAppsFragment : Fragment(R.layout.fragment_event_settings) {
|
||||
|
||||
override fun getItemCount(): Int = items.size
|
||||
|
||||
fun updateEventOverride(packageName: String, hasEventOverride: Boolean) {
|
||||
items = items.map { item ->
|
||||
if (item is AppListItem.App && item.value.packageName == packageName) {
|
||||
AppListItem.App(item.value.copy(hasEventOverride = hasEventOverride))
|
||||
} else {
|
||||
item
|
||||
}
|
||||
}
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
private class AppHolder(
|
||||
view: View,
|
||||
val checkbox: CheckBox,
|
||||
val filter: ImageButton,
|
||||
val label: TextView,
|
||||
val packageName: TextView,
|
||||
) : RecyclerView.ViewHolder(view)
|
||||
|
||||
Reference in New Issue
Block a user