Files
notifications-master/app/src/main/java/se/ajpanton/notificationlog/EventSettingsFragment.kt
T

135 lines
6.7 KiB
Kotlin

package se.ajpanton.notificationlog
import android.os.Bundle
import android.view.View
import android.widget.CheckBox
import android.widget.LinearLayout
import android.widget.TextView
import androidx.annotation.StringRes
import androidx.fragment.app.Fragment
import se.ajpanton.notificationlog.databinding.FragmentEventSettingsBinding
import se.ajpanton.notificationlog.settings.AppRuleMode
import se.ajpanton.notificationlog.settings.LoggingRuleStore
import se.ajpanton.notificationlog.settings.LoggingType
import se.ajpanton.notificationlog.capture.SeenApps
class EventSettingsFragment : Fragment(R.layout.fragment_event_settings) {
private var binding: FragmentEventSettingsBinding? = null
private lateinit var type: LoggingType
private lateinit var store: LoggingRuleStore
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding = FragmentEventSettingsBinding.bind(view)
type = requireArguments().getSerializable(ARG_TYPE, LoggingType::class.java)!!
store = LoggingRuleStore(requireContext())
binding!!.pageTitle.setText(requireArguments().getInt(ARG_TITLE))
bindRule()
binding!!.appListRefresh.setOnRefreshListener { reloadAppList(); binding!!.appListRefresh.isRefreshing = false }
}
override fun onDestroyView() { binding = null; super.onDestroyView() }
private fun bindRule() {
val rule = store.ruleFor(type)
listOf(
binding!!.masterToggle,
binding!!.appRuleToggle,
binding!!.onlySeenToggle,
binding!!.seenFirstToggle,
binding!!.routineUpdatesToggle,
).forEach { it.setOnCheckedChangeListener(null) }
binding!!.masterToggle.isChecked = rule.enabled
binding!!.appRuleToggle.isChecked = rule.appRuleMode == AppRuleMode.WHITELIST
binding!!.onlySeenToggle.isChecked = rule.onlySeenApps
binding!!.seenFirstToggle.isChecked = rule.seenAppsFirst
binding!!.seenFirstToggle.isEnabled = !rule.onlySeenApps
binding!!.routineUpdatesToggle.visibility = if (type == LoggingType.EDITS) View.VISIBLE else View.GONE
binding!!.routineUpdatesToggle.isChecked = rule.ignoreRoutineUpdates
updateRuleLabels()
binding!!.masterToggle.setOnCheckedChangeListener { _, checked -> save { copy(enabled = checked) } }
binding!!.appRuleToggle.setOnCheckedChangeListener { _, checked -> save { copy(appRuleMode = if (checked) AppRuleMode.WHITELIST else AppRuleMode.BLACKLIST) } }
binding!!.onlySeenToggle.setOnCheckedChangeListener { _, checked -> save { copy(onlySeenApps = checked) } }
binding!!.seenFirstToggle.setOnCheckedChangeListener { _, checked -> save { copy(seenAppsFirst = checked) } }
binding!!.routineUpdatesToggle.setOnCheckedChangeListener { _, checked -> save { copy(ignoreRoutineUpdates = checked) } }
reloadAppList()
}
private fun save(change: se.ajpanton.notificationlog.settings.LoggingRule.() -> se.ajpanton.notificationlog.settings.LoggingRule) {
store.save(type, store.ruleFor(type).change())
bindRule()
}
private fun updateRuleLabels() {
val rule = store.ruleFor(type)
binding!!.appRuleToggle.text = if (rule.appRuleMode == AppRuleMode.WHITELIST) "Whitelist chosen" else "Blacklist chosen"
binding!!.onlySeenToggle.text = if (rule.onlySeenApps) "Show only seen apps" else "Show all apps"
}
private fun reloadAppList() {
val container = binding!!.appList
container.removeAllViews()
val rule = store.ruleFor(type)
val seen = SeenApps.snapshot()
val apps = requireContext().packageManager.getInstalledApplications(0)
.map { info ->
AppRow(
label = requireContext().packageManager.getApplicationLabel(info).toString(),
packageName = info.packageName,
seen = info.packageName in seen,
selected = info.packageName in rule.selectedPackages,
)
}
.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER) { it.label })
val shown = if (rule.onlySeenApps) apps.filter { it.seen || it.selected } else apps
val seenRows = shown.filter { it.seen }
val otherRows = shown.filterNot { it.seen }
if (!rule.onlySeenApps && rule.seenAppsFirst && seenRows.isNotEmpty()) {
seenRows.forEach { container.addView(appView(it)) }
if (otherRows.isNotEmpty()) container.addView(separator())
otherRows.forEach { container.addView(appView(it)) }
} else if (rule.onlySeenApps && seenRows.isNotEmpty() && otherRows.isNotEmpty()) {
seenRows.forEach { container.addView(appView(it)) }
container.addView(separator())
otherRows.forEach { container.addView(appView(it)) }
} else {
shown.forEach { container.addView(appView(it)) }
}
}
private fun appView(row: AppRow): View = LinearLayout(requireContext()).apply {
orientation = LinearLayout.HORIZONTAL
val checkbox = CheckBox(context).apply { isChecked = row.selected }
checkbox.setOnCheckedChangeListener { _, checked ->
val current = store.ruleFor(type)
val packages = current.selectedPackages.toMutableSet().apply {
if (checked) add(row.packageName) else remove(row.packageName)
}
store.save(type, current.copy(selectedPackages = packages))
// Deliberately do not reload: an unchecked unseen row must remain reachable.
}
addView(checkbox)
addView(LinearLayout(context).apply {
orientation = LinearLayout.VERTICAL
layoutParams = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f)
addView(TextView(context).apply { text = row.label; textSize = 16f; setTypeface(typeface, 1) })
addView(TextView(context).apply { text = row.packageName; textSize = 12f })
})
}
private fun separator() = View(requireContext()).apply {
layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 1).apply { setMargins(0, 12, 0, 12) }
setBackgroundColor(0x33000000)
}
private data class AppRow(val label: String, val packageName: String, val seen: Boolean, val selected: Boolean)
companion object {
private const val ARG_TITLE = "title"
private const val ARG_TYPE = "type"
fun newInstance(@StringRes title: Int, type: LoggingType) = EventSettingsFragment().apply {
arguments = Bundle().apply { putInt(ARG_TITLE, title); putSerializable(ARG_TYPE, type) }
}
}
}