Improve alert profile and rule editors
This commit is contained in:
@@ -8,6 +8,9 @@ import android.widget.CheckBox
|
||||
import android.widget.EditText
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.Spinner
|
||||
import android.widget.ScrollView
|
||||
import android.widget.TextView
|
||||
import androidx.core.widget.doAfterTextChanged
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.switchmaterial.SwitchMaterial
|
||||
import se.ajpanton.notificationsmaster.alerts.AlertConfiguration
|
||||
@@ -33,17 +36,7 @@ object AlertRuleDialog {
|
||||
val app = apps?.let { choices ->
|
||||
Button(context).apply {
|
||||
text = "Choose apps"
|
||||
setOnClickListener {
|
||||
MaterialAlertDialogBuilder(context).setTitle("Apps this rule applies to")
|
||||
.setMultiChoiceItems(choices.map { choice -> choice.toString() }.toTypedArray(), choices.map { choice -> choice.packageName in selectedApps }.toBooleanArray()) { _, which, checked ->
|
||||
if (checked) selectedApps += choices[which].packageName else selectedApps -= choices[which].packageName
|
||||
text = when (selectedApps.size) {
|
||||
0 -> "Choose apps"
|
||||
1 -> choices.first { choice -> choice.packageName in selectedApps }.name
|
||||
else -> choices.first { choice -> choice.packageName in selectedApps }.name + "…"
|
||||
}
|
||||
}.setPositiveButton("Done", null).show()
|
||||
}
|
||||
setOnClickListener { showAppChooser(context, choices, selectedApps, this) }
|
||||
}.also(form::addView)
|
||||
}
|
||||
val profiles = configuration.profiles
|
||||
@@ -134,4 +127,56 @@ object AlertRuleDialog {
|
||||
}
|
||||
|
||||
private fun dp(context: Context, value: Int) = (value * context.resources.displayMetrics.density).toInt()
|
||||
|
||||
private fun showAppChooser(
|
||||
context: Context,
|
||||
apps: List<InstalledApp>,
|
||||
selected: MutableSet<String>,
|
||||
button: Button,
|
||||
) {
|
||||
val list = LinearLayout(context).apply { orientation = LinearLayout.VERTICAL }
|
||||
val search = EditText(context).apply { hint = "Filter apps" }
|
||||
fun updateButton() {
|
||||
button.text = when (selected.size) {
|
||||
0 -> "Choose apps"
|
||||
1 -> apps.first { it.packageName in selected }.name
|
||||
else -> apps.first { it.packageName in selected }.name + "…"
|
||||
}
|
||||
}
|
||||
fun showMatches(query: String) {
|
||||
val terms = query.lowercase().split(Regex("\\s+")).filter(String::isNotBlank)
|
||||
list.removeAllViews()
|
||||
apps.filter { app ->
|
||||
val searchable = "${app.name} ${app.packageName}".lowercase()
|
||||
terms.all(searchable::contains)
|
||||
}.forEach { app ->
|
||||
val checkBox = CheckBox(context).apply { isChecked = app.packageName in selected }
|
||||
list.addView(LinearLayout(context).apply {
|
||||
orientation = LinearLayout.HORIZONTAL
|
||||
isClickable = true
|
||||
setPadding(0, dp(context, 4), 0, dp(context, 4))
|
||||
addView(checkBox)
|
||||
addView(LinearLayout(context).apply {
|
||||
orientation = LinearLayout.VERTICAL
|
||||
addView(TextView(context).apply { text = app.name; textSize = 16f; setTypeface(typeface, android.graphics.Typeface.BOLD) })
|
||||
addView(TextView(context).apply { text = app.packageName; textSize = 12f; alpha = 0.72f })
|
||||
})
|
||||
setOnClickListener { checkBox.isChecked = !checkBox.isChecked }
|
||||
})
|
||||
checkBox.setOnCheckedChangeListener { _, checked ->
|
||||
if (checked) selected += app.packageName else selected -= app.packageName
|
||||
}
|
||||
}
|
||||
}
|
||||
search.doAfterTextChanged { showMatches(it.toString()) }
|
||||
showMatches("")
|
||||
val content = LinearLayout(context).apply {
|
||||
orientation = LinearLayout.VERTICAL
|
||||
setPadding(dp(context, 24), 0, dp(context, 24), 0)
|
||||
addView(search)
|
||||
addView(ScrollView(context).apply { addView(list) }, LinearLayout.LayoutParams(-1, dp(context, 420)))
|
||||
}
|
||||
MaterialAlertDialogBuilder(context).setTitle("Apps this rule applies to")
|
||||
.setView(content).setPositiveButton("Done") { _, _ -> updateButton() }.show()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,9 @@ import android.content.Intent
|
||||
import android.media.RingtoneManager
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.os.VibrationAttributes
|
||||
import android.os.VibrationEffect
|
||||
import android.os.VibratorManager
|
||||
import android.view.View
|
||||
import android.widget.Button
|
||||
import android.widget.EditText
|
||||
@@ -22,9 +25,10 @@ class ProfilesFragment : Fragment(R.layout.fragment_profiles) {
|
||||
private lateinit var store: AlertConfigurationStore
|
||||
private var selectedSoundUri: String? = null
|
||||
private var soundButton: Button? = null
|
||||
private var soundName: android.widget.TextView? = null
|
||||
private val chooseSound = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||
selectedSoundUri = result.data?.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI, Uri::class.java)?.toString()
|
||||
soundButton?.text = if (selectedSoundUri == null) "Choose sound" else "Change sound"
|
||||
updateSoundSelection()
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
@@ -37,6 +41,7 @@ class ProfilesFragment : Fragment(R.layout.fragment_profiles) {
|
||||
override fun onDestroyView() {
|
||||
binding = null
|
||||
soundButton = null
|
||||
soundName = null
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
@@ -79,9 +84,24 @@ class ProfilesFragment : Fragment(R.layout.fragment_profiles) {
|
||||
}
|
||||
}
|
||||
soundButton = pickerButton
|
||||
val selectedSound = android.widget.TextView(context).apply { text = soundLabel() }
|
||||
soundName = selectedSound
|
||||
val testVibration = Button(context).apply {
|
||||
text = "Test vibration"
|
||||
setOnClickListener {
|
||||
parsePattern(vibration.text.toString())?.takeIf { it.any { duration -> duration > 0 } }?.let { pattern ->
|
||||
context.getSystemService(VibratorManager::class.java).defaultVibrator.vibrate(
|
||||
VibrationEffect.createWaveform(pattern.toLongArray(), -1),
|
||||
VibrationAttributes.Builder().setUsage(VibrationAttributes.USAGE_NOTIFICATION).build(),
|
||||
)
|
||||
} ?: run { vibration.error = "Enter a vibration pattern first" }
|
||||
}
|
||||
}
|
||||
form.addView(name)
|
||||
form.addView(pickerButton)
|
||||
form.addView(selectedSound)
|
||||
form.addView(vibration)
|
||||
form.addView(testVibration)
|
||||
val dialog = MaterialAlertDialogBuilder(context)
|
||||
.setTitle(if (existing == null) "Add profile" else "Edit profile")
|
||||
.setView(form)
|
||||
@@ -134,5 +154,14 @@ class ProfilesFragment : Fragment(R.layout.fragment_profiles) {
|
||||
if (profile.vibrationPattern.any { it > 0 }) add("Vibration")
|
||||
}.joinToString(" + ")
|
||||
|
||||
private fun updateSoundSelection() {
|
||||
soundButton?.text = if (selectedSoundUri == null) "Choose sound" else "Change sound"
|
||||
soundName?.text = soundLabel()
|
||||
}
|
||||
|
||||
private fun soundLabel(): String = selectedSoundUri?.let { uri ->
|
||||
RingtoneManager.getRingtone(requireContext(), Uri.parse(uri))?.getTitle(requireContext()) ?: uri
|
||||
} ?: "No sound selected"
|
||||
|
||||
private fun dp(value: Int) = (value * resources.displayMetrics.density).toInt()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user