Add custom alert profiles
This commit is contained in:
@@ -155,6 +155,7 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
|
||||
page == Page.VIEW_LOGS -> ViewLogsFragment()
|
||||
page == Page.SETTINGS -> SettingsFragment()
|
||||
page == Page.ALERTS -> AlertsFragment()
|
||||
page == Page.PROFILES -> ProfilesFragment()
|
||||
page == Page.LOG_DISPLAY -> LogDisplayFragment()
|
||||
page == Page.FILTER_LOGGING -> FilterLoggingFragment()
|
||||
page == Page.FILTER_APPS -> FilterAppsFragment.newInstance()
|
||||
@@ -347,6 +348,7 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
|
||||
VIEW_LOGS(R.id.nav_view_logs, R.string.page_view_logs),
|
||||
SETTINGS(R.id.nav_settings, R.string.page_settings),
|
||||
ALERTS(R.id.nav_alerts, R.string.page_alerts),
|
||||
PROFILES(R.id.nav_profiles, R.string.page_profiles),
|
||||
LOG_DISPLAY(R.id.nav_log_display, R.string.page_log_display),
|
||||
FILTER_LOGGING(R.id.nav_filter_logging, R.string.page_filter_logging),
|
||||
FILTER_APPS(R.id.nav_filter_apps, R.string.page_filter_apps),
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
package se.ajpanton.notificationsmaster
|
||||
|
||||
import android.content.Intent
|
||||
import android.media.RingtoneManager
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.Button
|
||||
import android.widget.EditText
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import se.ajpanton.notificationsmaster.alerts.AlertConfigurationStore
|
||||
import se.ajpanton.notificationsmaster.alerts.AlertProfile
|
||||
import se.ajpanton.notificationsmaster.databinding.FragmentProfilesBinding
|
||||
import java.util.UUID
|
||||
|
||||
class ProfilesFragment : Fragment(R.layout.fragment_profiles) {
|
||||
private var binding: FragmentProfilesBinding? = null
|
||||
private lateinit var store: AlertConfigurationStore
|
||||
private var selectedSoundUri: String? = null
|
||||
private var soundButton: Button? = 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"
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
binding = FragmentProfilesBinding.bind(view)
|
||||
store = AlertConfigurationStore(requireContext())
|
||||
binding!!.addProfile.setOnClickListener { showEditor() }
|
||||
refresh()
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
binding = null
|
||||
soundButton = null
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
private fun refresh() {
|
||||
val container = binding!!.profiles
|
||||
container.removeAllViews()
|
||||
store.load().profiles.forEach { profile ->
|
||||
container.addView(Button(requireContext()).apply {
|
||||
text = profile.name + "\n" + profileSummary(profile)
|
||||
isAllCaps = false
|
||||
setOnClickListener { showEditor(profile) }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun showEditor(existing: AlertProfile? = null) {
|
||||
selectedSoundUri = existing?.soundUri
|
||||
val context = requireContext()
|
||||
val form = LinearLayout(context).apply {
|
||||
orientation = LinearLayout.VERTICAL
|
||||
setPadding(dp(24), 0, dp(24), 0)
|
||||
}
|
||||
val name = EditText(context).apply {
|
||||
hint = "Profile name"
|
||||
setText(existing?.name)
|
||||
}
|
||||
val vibration = EditText(context).apply {
|
||||
hint = "Vibration timing in milliseconds, for example 0, 200, 100, 200"
|
||||
setText(existing?.vibrationPattern?.joinToString(", ") ?: "0, 200")
|
||||
inputType = android.text.InputType.TYPE_CLASS_TEXT
|
||||
}
|
||||
val pickerButton = Button(context).apply {
|
||||
text = if (selectedSoundUri == null) "Choose sound" else "Change sound"
|
||||
setOnClickListener {
|
||||
chooseSound.launch(
|
||||
Intent(RingtoneManager.ACTION_RINGTONE_PICKER)
|
||||
.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_NOTIFICATION)
|
||||
.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, selectedSoundUri?.let(Uri::parse)),
|
||||
)
|
||||
}
|
||||
}
|
||||
soundButton = pickerButton
|
||||
form.addView(name)
|
||||
form.addView(pickerButton)
|
||||
form.addView(vibration)
|
||||
val dialog = MaterialAlertDialogBuilder(context)
|
||||
.setTitle(if (existing == null) "Add profile" else "Edit profile")
|
||||
.setView(form)
|
||||
.setNegativeButton("Cancel", null)
|
||||
.setPositiveButton("Save", null)
|
||||
.apply {
|
||||
if (existing != null) setNeutralButton("Delete", null)
|
||||
}
|
||||
.show()
|
||||
dialog.getButton(android.content.DialogInterface.BUTTON_POSITIVE).setOnClickListener {
|
||||
val pattern = parsePattern(vibration.text.toString()) ?: run {
|
||||
vibration.error = "Use non-negative comma-separated milliseconds"
|
||||
return@setOnClickListener
|
||||
}
|
||||
val profileName = name.text.toString().trim()
|
||||
if (profileName.isEmpty() || selectedSoundUri == null && pattern.none { it > 0 }) {
|
||||
name.error = if (profileName.isEmpty()) "Enter a name" else null
|
||||
vibration.error = if (selectedSoundUri == null && pattern.none { it > 0 }) "Choose a sound or vibration" else null
|
||||
return@setOnClickListener
|
||||
}
|
||||
val profile = AlertProfile(existing?.id ?: UUID.randomUUID().toString(), profileName, selectedSoundUri, pattern)
|
||||
val configuration = store.load()
|
||||
store.save(configuration.copy(profiles = configuration.profiles.filterNot { it.id == profile.id } + profile))
|
||||
dialog.dismiss()
|
||||
refresh()
|
||||
}
|
||||
if (existing != null) {
|
||||
dialog.getButton(android.content.DialogInterface.BUTTON_NEUTRAL).setOnClickListener {
|
||||
val configuration = store.load()
|
||||
if (configuration.rules.any { it.profileId == existing.id }) {
|
||||
Toast.makeText(context, "Remove or change rules using this profile first", Toast.LENGTH_LONG).show()
|
||||
return@setOnClickListener
|
||||
}
|
||||
store.save(configuration.copy(profiles = configuration.profiles.filterNot { it.id == existing.id }))
|
||||
dialog.dismiss()
|
||||
refresh()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun parsePattern(text: String): List<Long>? = text.split(',')
|
||||
.map { it.trim() }
|
||||
.filter { it.isNotEmpty() }
|
||||
.map { it.toLongOrNull() ?: return null }
|
||||
.takeIf { it.all { value -> value >= 0 } }
|
||||
?: emptyList()
|
||||
|
||||
private fun profileSummary(profile: AlertProfile): String = buildList {
|
||||
if (profile.soundUri != null) add("Sound")
|
||||
if (profile.vibrationPattern.any { it > 0 }) add("Vibration")
|
||||
}.joinToString(" + ")
|
||||
|
||||
private fun dp(value: Int) = (value * resources.displayMetrics.density).toInt()
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="@dimen/page_padding">
|
||||
|
||||
<Button
|
||||
android:id="@+id/add_profile"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Add profile" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/profiles"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:orientation="vertical" />
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
@@ -12,6 +12,9 @@
|
||||
<item
|
||||
android:id="@+id/nav_alerts"
|
||||
android:title="@string/navigation_indented_alerts" />
|
||||
<item
|
||||
android:id="@+id/nav_profiles"
|
||||
android:title="@string/navigation_indented_profiles" />
|
||||
<item
|
||||
android:id="@+id/nav_log_display"
|
||||
android:title="@string/navigation_indented_log_display" />
|
||||
|
||||
@@ -8,11 +8,13 @@
|
||||
<string name="page_view_logs">View logs</string>
|
||||
<string name="page_settings">Settings</string>
|
||||
<string name="page_alerts">Alerts</string>
|
||||
<string name="page_profiles">Profiles</string>
|
||||
<string name="page_filter_apps">Filter apps</string>
|
||||
<string name="page_log_display">Log display</string>
|
||||
<string name="page_filter_logging">Filter logging</string>
|
||||
<string name="navigation_indented_log_display">    Log display</string>
|
||||
<string name="navigation_indented_alerts">    Alerts</string>
|
||||
<string name="navigation_indented_profiles">    Profiles</string>
|
||||
<string name="navigation_indented_filter_logging">    Filter logging</string>
|
||||
<string name="navigation_indented_filter_apps">    Filter apps</string>
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user