package se.ajpanton.notificationsmaster import android.os.Bundle import android.view.View import android.widget.TextView import androidx.core.os.bundleOf import androidx.fragment.app.Fragment import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.RecyclerView import se.ajpanton.notificationsmaster.alerts.AlertConfigurationStore import se.ajpanton.notificationsmaster.alerts.AlertAppSettings import se.ajpanton.notificationsmaster.alerts.AlertRule import se.ajpanton.notificationsmaster.alerts.AlertRuleEditor import se.ajpanton.notificationsmaster.databinding.FragmentAppRulesBinding class AppRulesFragment : Fragment(R.layout.fragment_app_rules) { private var binding: FragmentAppRulesBinding? = null private lateinit var packageName: String private lateinit var store: AlertConfigurationStore private val adapter = RuleAdapter(::edit) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) packageName = requireArguments().getString(ARG_PACKAGE)!! } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { binding = FragmentAppRulesBinding.bind(view) store = AlertConfigurationStore(requireContext()) binding!!.rules.adapter = adapter binding!!.addRule.setOnClickListener { edit(null) } binding!!.allowMultipleMatches.isChecked = store.load().appSettings.firstOrNull { it.packageName == packageName }?.allowMultipleMatches == true binding!!.allowMultipleMatches.setOnCheckedChangeListener { _, enabled -> setAllowMultipleMatches(enabled) } ItemTouchHelper(object : ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP or ItemTouchHelper.DOWN, 0) { override fun onMove(recyclerView: RecyclerView, holder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean { adapter.move(holder.bindingAdapterPosition, target.bindingAdapterPosition) saveOrder() return true } override fun onSwiped(holder: RecyclerView.ViewHolder, direction: Int) = Unit }).attachToRecyclerView(binding!!.rules) refresh() } override fun onResume() { super.onResume() activity?.title = InstalledApps.name(requireContext(), packageName) } override fun onDestroyView() { binding = null; super.onDestroyView() } private fun refresh() = adapter.submit(store.load().rules.filter { it.packageName == packageName }.sortedBy { it.order }) private fun edit(existing: AlertRule?) { val configuration = store.load() AlertRuleDialog.show(requireContext(), configuration, existing, onSave = { _, definition -> store.save(if (existing == null) AlertRuleEditor.addForApps(configuration, listOf(packageName), definition) else AlertRuleEditor.updateForApp(configuration, existing.id, definition)) refresh() }, onDelete = existing?.let { rule -> { store.save(AlertRuleEditor.remove(store.load(), setOf(rule.id))); refresh() } }, ) } private fun saveOrder() { store.save(AlertRuleEditor.reorderForApp(store.load(), packageName, adapter.items.map { it.id })) } private fun setAllowMultipleMatches(enabled: Boolean) { val configuration = store.load() val old = configuration.appSettings.firstOrNull { it.packageName == packageName } val setting = (old ?: AlertAppSettings(packageName)).copy(allowMultipleMatches = enabled) saveAppSettings(configuration, setting) } private fun saveAppSettings(configuration: se.ajpanton.notificationsmaster.alerts.AlertConfiguration, setting: AlertAppSettings) { store.save(configuration.copy(appSettings = configuration.appSettings.filterNot { it.packageName == packageName } + if (setting.allowMultipleMatches) listOf(setting) else emptyList())) } private class RuleAdapter(private val onClick: (AlertRule) -> Unit) : RecyclerView.Adapter() { var items = emptyList(); private set fun submit(rules: List) { items = rules; notifyDataSetChanged() } fun move(from: Int, to: Int) { items = items.toMutableList().apply { add(to, removeAt(from)) }; notifyItemMoved(from, to) } override fun onCreateViewHolder(parent: android.view.ViewGroup, viewType: Int) = Holder(TextView(parent.context).apply { layoutParams = RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.WRAP_CONTENT) val padding = (16 * parent.resources.displayMetrics.density).toInt() setPadding(padding, padding, padding, padding); textSize = 16f; isClickable = true }) override fun onBindViewHolder(holder: Holder, position: Int) { val rule = items[position] holder.text.text = rule.name ?: "Rule ${position + 1}" + "\n" + rule.sources.joinToString { it.label() } + rule.matcher.value.takeIf { it.isNotBlank() }?.let { " • contains $it" }.orEmpty() holder.text.setOnClickListener { onClick(rule) } } override fun getItemCount() = items.size class Holder(val text: TextView) : RecyclerView.ViewHolder(text) } companion object { private const val ARG_PACKAGE = "package" fun newInstance(packageName: String) = AppRulesFragment().apply { arguments = bundleOf(ARG_PACKAGE to packageName) } } }