Make subscription-specific setting work

This commit is contained in:
Philipp Heckel 2022-12-06 15:29:09 -05:00
parent fea0709a55
commit 5101d1a0b3
12 changed files with 108 additions and 32 deletions

View file

@ -2,11 +2,11 @@
"formatVersion": 1,
"database": {
"version": 12,
"identityHash": "d230005f4d9824ba9aa34c61003bdcbb",
"identityHash": "40948c056fa4ccc9765735111177cf85",
"entities": [
{
"tableName": "Subscription",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `baseUrl` TEXT NOT NULL, `topic` TEXT NOT NULL, `instant` INTEGER NOT NULL, `mutedUntil` INTEGER NOT NULL, `minPriority` INTEGER NOT NULL, `autoDelete` INTEGER NOT NULL, `lastNotificationId` TEXT, `icon` TEXT, `upAppId` TEXT, `upConnectorToken` TEXT, `displayName` TEXT, PRIMARY KEY(`id`))",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `baseUrl` TEXT NOT NULL, `topic` TEXT NOT NULL, `instant` INTEGER NOT NULL, `mutedUntil` INTEGER NOT NULL, `minPriority` INTEGER NOT NULL, `autoDelete` INTEGER NOT NULL, `insistent` INTEGER NOT NULL, `lastNotificationId` TEXT, `icon` TEXT, `upAppId` TEXT, `upConnectorToken` TEXT, `displayName` TEXT, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
@ -50,6 +50,12 @@
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "insistent",
"columnName": "insistent",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "lastNotificationId",
"columnName": "lastNotificationId",
@ -338,7 +344,7 @@
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'd230005f4d9824ba9aa34c61003bdcbb')"
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '40948c056fa4ccc9765735111177cf85')"
]
}
}

View file

@ -102,6 +102,7 @@ class Backuper(val context: Context) {
mutedUntil = s.mutedUntil,
minPriority = s.minPriority ?: Repository.MIN_PRIORITY_USE_GLOBAL,
autoDelete = s.autoDelete ?: Repository.AUTO_DELETE_USE_GLOBAL,
insistent = s.insistent ?: Repository.INSISTENT_MAX_PRIORITY_USE_GLOBAL,
lastNotificationId = s.lastNotificationId,
icon = s.icon,
upAppId = s.upAppId,
@ -239,6 +240,7 @@ class Backuper(val context: Context) {
mutedUntil = s.mutedUntil,
minPriority = s.minPriority,
autoDelete = s.autoDelete,
insistent = s.insistent,
lastNotificationId = s.lastNotificationId,
icon = s.icon,
upAppId = s.upAppId,
@ -356,6 +358,7 @@ data class Subscription(
val mutedUntil: Long,
val minPriority: Int?,
val autoDelete: Long?,
val insistent: Int?,
val lastNotificationId: String?,
val icon: String?,
val upAppId: String?,

View file

@ -18,7 +18,7 @@ data class Subscription(
@ColumnInfo(name = "mutedUntil") val mutedUntil: Long,
@ColumnInfo(name = "minPriority") val minPriority: Int,
@ColumnInfo(name = "autoDelete") val autoDelete: Long, // Seconds
//@ColumnInfo(name = "insistent") val insistent: Boolean?, // Seconds
@ColumnInfo(name = "insistent") val insistent: Int, // Ring constantly for max priority notifications (-1 = use global, 0 = off, 1 = on)
@ColumnInfo(name = "lastNotificationId") val lastNotificationId: String?, // Used for polling, with since=<id>
@ColumnInfo(name = "icon") val icon: String?, // content://-URI (or later other identifier)
@ColumnInfo(name = "upAppId") val upAppId: String?, // UnifiedPush application package name
@ -29,8 +29,40 @@ data class Subscription(
@Ignore val lastActive: Long = 0, // Unix timestamp
@Ignore val state: ConnectionState = ConnectionState.NOT_APPLICABLE
) {
constructor(id: Long, baseUrl: String, topic: String, instant: Boolean, mutedUntil: Long, minPriority: Int, autoDelete: Long, lastNotificationId: String, icon: String, upAppId: String, upConnectorToken: String, displayName: String?) :
this(id, baseUrl, topic, instant, mutedUntil, minPriority, autoDelete, lastNotificationId, icon, upAppId, upConnectorToken, displayName, 0, 0, 0, ConnectionState.NOT_APPLICABLE)
constructor(
id: Long,
baseUrl: String,
topic: String,
instant: Boolean,
mutedUntil: Long,
minPriority: Int,
autoDelete: Long,
insistent: Int,
lastNotificationId: String,
icon: String,
upAppId: String,
upConnectorToken: String,
displayName: String?
) :
this(
id,
baseUrl,
topic,
instant,
mutedUntil,
minPriority,
autoDelete,
insistent,
lastNotificationId,
icon,
upAppId,
upConnectorToken,
displayName,
0,
0,
0,
ConnectionState.NOT_APPLICABLE
)
}
enum class ConnectionState {
@ -45,6 +77,7 @@ data class SubscriptionWithMetadata(
val mutedUntil: Long,
val autoDelete: Long,
val minPriority: Int,
val insistent: Int,
val lastNotificationId: String?,
val icon: String?,
val upAppId: String?,
@ -291,7 +324,7 @@ abstract class Database : RoomDatabase() {
interface SubscriptionDao {
@Query("""
SELECT
s.id, s.baseUrl, s.topic, s.instant, s.mutedUntil, s.minPriority, s.autoDelete, s.lastNotificationId, s.icon, s.upAppId, s.upConnectorToken, s.displayName,
s.id, s.baseUrl, s.topic, s.instant, s.mutedUntil, s.minPriority, s.autoDelete, s.insistent, s.lastNotificationId, s.icon, s.upAppId, s.upConnectorToken, s.displayName,
COUNT(n.id) totalCount,
COUNT(CASE n.notificationId WHEN 0 THEN NULL ELSE n.id END) newCount,
IFNULL(MAX(n.timestamp),0) AS lastActive
@ -304,7 +337,7 @@ interface SubscriptionDao {
@Query("""
SELECT
s.id, s.baseUrl, s.topic, s.instant, s.mutedUntil, s.minPriority, s.autoDelete, s.lastNotificationId, s.icon, s.upAppId, s.upConnectorToken, s.displayName,
s.id, s.baseUrl, s.topic, s.instant, s.mutedUntil, s.minPriority, s.autoDelete, s.insistent, s.lastNotificationId, s.icon, s.upAppId, s.upConnectorToken, s.displayName,
COUNT(n.id) totalCount,
COUNT(CASE n.notificationId WHEN 0 THEN NULL ELSE n.id END) newCount,
IFNULL(MAX(n.timestamp),0) AS lastActive
@ -317,7 +350,7 @@ interface SubscriptionDao {
@Query("""
SELECT
s.id, s.baseUrl, s.topic, s.instant, s.mutedUntil, s.minPriority, s.autoDelete, s.lastNotificationId, s.icon, s.upAppId, s.upConnectorToken, s.displayName,
s.id, s.baseUrl, s.topic, s.instant, s.mutedUntil, s.minPriority, s.autoDelete, s.insistent, s.lastNotificationId, s.icon, s.upAppId, s.upConnectorToken, s.displayName,
COUNT(n.id) totalCount,
COUNT(CASE n.notificationId WHEN 0 THEN NULL ELSE n.id END) newCount,
IFNULL(MAX(n.timestamp),0) AS lastActive
@ -330,7 +363,7 @@ interface SubscriptionDao {
@Query("""
SELECT
s.id, s.baseUrl, s.topic, s.instant, s.mutedUntil, s.minPriority, s.autoDelete, s.lastNotificationId, s.icon, s.upAppId, s.upConnectorToken, s.displayName,
s.id, s.baseUrl, s.topic, s.instant, s.mutedUntil, s.minPriority, s.autoDelete, s.insistent, s.lastNotificationId, s.icon, s.upAppId, s.upConnectorToken, s.displayName,
COUNT(n.id) totalCount,
COUNT(CASE n.notificationId WHEN 0 THEN NULL ELSE n.id END) newCount,
IFNULL(MAX(n.timestamp),0) AS lastActive
@ -343,7 +376,7 @@ interface SubscriptionDao {
@Query("""
SELECT
s.id, s.baseUrl, s.topic, s.instant, s.mutedUntil, s.minPriority, s.autoDelete, s.lastNotificationId, s.icon, s.upAppId, s.upConnectorToken, s.displayName,
s.id, s.baseUrl, s.topic, s.instant, s.mutedUntil, s.minPriority, s.autoDelete, s.insistent, s.lastNotificationId, s.icon, s.upAppId, s.upConnectorToken, s.displayName,
COUNT(n.id) totalCount,
COUNT(CASE n.notificationId WHEN 0 THEN NULL ELSE n.id END) newCount,
IFNULL(MAX(n.timestamp),0) AS lastActive

View file

@ -402,6 +402,7 @@ class Repository(private val sharedPrefs: SharedPreferences, private val databas
mutedUntil = s.mutedUntil,
minPriority = s.minPriority,
autoDelete = s.autoDelete,
insistent = s.insistent,
lastNotificationId = s.lastNotificationId,
icon = s.icon,
upAppId = s.upAppId,
@ -427,6 +428,7 @@ class Repository(private val sharedPrefs: SharedPreferences, private val databas
mutedUntil = s.mutedUntil,
minPriority = s.minPriority,
autoDelete = s.autoDelete,
insistent = s.insistent,
lastNotificationId = s.lastNotificationId,
icon = s.icon,
upAppId = s.upAppId,
@ -505,6 +507,10 @@ class Repository(private val sharedPrefs: SharedPreferences, private val databas
const val AUTO_DELETE_THREE_MONTHS_SECONDS = 90 * ONE_DAY_SECONDS
const val AUTO_DELETE_DEFAULT_SECONDS = AUTO_DELETE_ONE_MONTH_SECONDS
const val INSISTENT_MAX_PRIORITY_USE_GLOBAL = -1 // Values must match values.xml
const val INSISTENT_MAX_PRIORITY_DISABLED = 0
const val INSISTENT_MAX_PRIORITY_ENABLED = 1
const val CONNECTION_PROTOCOL_JSONHTTP = "jsonhttp"
const val CONNECTION_PROTOCOL_WS = "ws"

View file

@ -65,7 +65,8 @@ class NotificationService(val context: Context) {
private fun displayInternal(subscription: Subscription, notification: Notification, update: Boolean = false) {
val title = formatTitle(subscription, notification)
val channelId = toChannelId(notification.priority)
val insistent = notification.priority == 5 && repository.getInsistentMaxPriorityEnabled()
val insistent = notification.priority == 5 &&
(repository.getInsistentMaxPriorityEnabled() || subscription.insistent == Repository.INSISTENT_MAX_PRIORITY_ENABLED)
val builder = NotificationCompat.Builder(context, channelId)
.setSmallIcon(R.drawable.ic_notification)
.setColor(ContextCompat.getColor(context, Colors.notificationIcon(context)))

View file

@ -113,6 +113,7 @@ class DetailActivity : AppCompatActivity(), ActionMode.Callback, NotificationFra
mutedUntil = 0,
minPriority = Repository.MIN_PRIORITY_USE_GLOBAL,
autoDelete = Repository.AUTO_DELETE_USE_GLOBAL,
insistent = Repository.INSISTENT_MAX_PRIORITY_USE_GLOBAL,
lastNotificationId = null,
icon = null,
upAppId = null,
@ -241,7 +242,7 @@ class DetailActivity : AppCompatActivity(), ActionMode.Callback, NotificationFra
adapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
if (positionStart == 0) {
Log.d(TAG, "$itemCount item(s) inserted at $positionStart, scrolling to the top")
Log.d(TAG, "$itemCount item(s) inserted at 0, scrolling to the top")
mainList.scrollToPosition(positionStart)
}
}
@ -626,7 +627,7 @@ class DetailActivity : AppCompatActivity(), ActionMode.Callback, NotificationFra
handleActionModeClick(notification)
} else if (notification.click != "") {
try {
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(notification.click)))
startActivity(Intent(ACTION_VIEW, Uri.parse(notification.click)))
} catch (e: Exception) {
Log.w(TAG, "Cannot open click URL", e)
runOnUiThread {

View file

@ -110,7 +110,7 @@ class DetailSettingsActivity : AppCompatActivity() {
loadMutedUntilPref()
loadMinPriorityPref()
loadAutoDeletePref()
//loadInsistentMaxPriority()
loadInsistentMaxPriorityPref()
loadIconSetPref()
loadIconRemovePref()
} else {
@ -238,7 +238,7 @@ class DetailSettingsActivity : AppCompatActivity() {
pref?.summaryProvider = Preference.SummaryProvider<ListPreference> { preference ->
var seconds = preference.value.toLongOrNull() ?: Repository.AUTO_DELETE_USE_GLOBAL
val global = seconds == Repository.AUTO_DELETE_USE_GLOBAL
if (seconds == Repository.AUTO_DELETE_USE_GLOBAL) {
if (global) {
seconds = repository.getAutoDeleteSeconds()
}
val summary = when (seconds) {
@ -253,30 +253,34 @@ class DetailSettingsActivity : AppCompatActivity() {
maybeAppendGlobal(summary, global)
}
}
/*
private fun loadInsistentMaxPriority() {
val appBaseUrl = getString(R.string.app_base_url)
val prefId = context?.getString(R.string.detail_settings_notifications_instant_key) ?: return
val pref: SwitchPreference? = findPreference(prefId)
private fun loadInsistentMaxPriorityPref() {
val prefId = context?.getString(R.string.detail_settings_notifications_insistent_max_priority_key) ?: return
val pref: ListPreference? = findPreference(prefId)
pref?.isVisible = true
pref?.isChecked = subscription.instant
pref?.value = subscription.insistent.toString()
pref?.preferenceDataStore = object : PreferenceDataStore() {
override fun putBoolean(key: String?, value: Boolean) {
save(subscription.copy(instant = value), refresh = true)
override fun putString(key: String?, value: String?) {
val intValue = value?.toIntOrNull() ?:return
save(subscription.copy(insistent = intValue))
}
override fun getBoolean(key: String?, defValue: Boolean): Boolean {
return subscription.instant
override fun getString(key: String?, defValue: String?): String {
return subscription.insistent.toString()
}
}
pref?.summaryProvider = Preference.SummaryProvider<SwitchPreference> { preference ->
if (preference.isChecked) {
getString(R.string.detail_settings_notifications_instant_summary_on)
pref?.summaryProvider = Preference.SummaryProvider<ListPreference> { preference ->
val value = preference.value.toIntOrNull() ?: Repository.INSISTENT_MAX_PRIORITY_USE_GLOBAL
val global = value == Repository.INSISTENT_MAX_PRIORITY_USE_GLOBAL
val enabled = if (global) repository.getInsistentMaxPriorityEnabled() else value == Repository.INSISTENT_MAX_PRIORITY_ENABLED
val summary = if (enabled) {
getString(R.string.settings_notifications_insistent_max_priority_summary_enabled)
} else {
getString(R.string.detail_settings_notifications_instant_summary_off)
getString(R.string.settings_notifications_insistent_max_priority_summary_disabled)
}
maybeAppendGlobal(summary, global)
}
}
*/
private fun loadIconSetPref() {
val prefId = context?.getString(R.string.detail_settings_appearance_icon_set_key) ?: return
iconSetPref = findPreference(prefId) ?: return

View file

@ -455,6 +455,7 @@ class MainActivity : AppCompatActivity(), ActionMode.Callback, AddFragment.Subsc
mutedUntil = 0,
minPriority = Repository.MIN_PRIORITY_USE_GLOBAL,
autoDelete = Repository.AUTO_DELETE_USE_GLOBAL,
insistent = Repository.INSISTENT_MAX_PRIORITY_USE_GLOBAL,
lastNotificationId = null,
icon = null,
upAppId = null,

View file

@ -75,6 +75,7 @@ class BroadcastReceiver : android.content.BroadcastReceiver() {
mutedUntil = 0,
minPriority = Repository.MIN_PRIORITY_USE_GLOBAL,
autoDelete = Repository.AUTO_DELETE_USE_GLOBAL,
insistent = Repository.INSISTENT_MAX_PRIORITY_USE_GLOBAL,
lastNotificationId = null,
icon = null,
upAppId = appId,

View file

@ -282,7 +282,7 @@
<string name="settings_notifications_auto_delete_three_months">After 3 months</string>
<string name="settings_notifications_insistent_max_priority_title">Keep alerting for highest priority</string>
<string name="settings_notifications_insistent_max_priority_summary_enabled">Max priority notifications continuously alert until dismissed</string>
<string name="settings_notifications_insistent_max_priority_summary_disabled">If enabled, the notification sound will continuously repeat until dismissed</string>
<string name="settings_notifications_insistent_max_priority_summary_disabled">Max priority notifications only alert once</string>
<string name="settings_general_header">General</string>
<string name="settings_general_default_base_url_title">Default server</string>
<string name="settings_general_default_base_url_message">Enter your server\'s root URL to use your own server as a default when subscribing to new topics and/or sharing to topics.</string>
@ -352,6 +352,8 @@
<string name="detail_settings_notifications_instant_title">Instant delivery</string>
<string name="detail_settings_notifications_instant_summary_on">Notifications are delivered instantly. Requires a foreground service and consumes more battery.</string>
<string name="detail_settings_notifications_instant_summary_off">Notifications are delivered using Firebase. Delivery may be delayed, but consumes less battery.</string>
<string name="detail_settings_notifications_insistent_max_priority_list_item_enabled">Keep alerting</string>
<string name="detail_settings_notifications_insistent_max_priority_list_item_disabled">Alert only once</string>
<string name="detail_settings_appearance_header">Appearance</string>
<string name="detail_settings_appearance_icon_set_title">Subscription icon</string>
<string name="detail_settings_appearance_icon_set_summary">Set an icon to be displayed in notifications</string>

View file

@ -37,6 +37,7 @@
<string name="detail_settings_notifications_muted_until_key" translatable="false">SubscriptionMutedUntil</string>
<string name="detail_settings_notifications_min_priority_key" translatable="false">SubscriptionMinPriority</string>
<string name="detail_settings_notifications_auto_delete_key" translatable="false">SubscriptionAutoDelete</string>
<string name="detail_settings_notifications_insistent_max_priority_key" translatable="false">SubscriptionInsistentMaxPriority</string>
<string name="detail_settings_appearance_header_key" translatable="false">SubscriptionAppearance</string>
<string name="detail_settings_appearance_icon_set_key" translatable="false">SubscriptionIconSet</string>
<string name="detail_settings_appearance_icon_remove_key" translatable="false">SubscriptionIconRemove</string>
@ -147,6 +148,16 @@
<item>2592000</item>
<item>7776000</item>
</string-array>
<string-array name="detail_settings_notifications_insistent_max_priority_entries">
<item>@string/detail_settings_global_setting_title</item>
<item>@string/detail_settings_notifications_insistent_max_priority_list_item_enabled</item>
<item>@string/detail_settings_notifications_insistent_max_priority_list_item_disabled</item>
</string-array>
<string-array name="detail_settings_notifications_insistent_max_priority_values">
<item>-1</item> <!-- Same as Repository.INSISTENT_MAX_PRIORITY_USE_GLOBAL -->
<item>1</item>
<item>0</item>
</string-array>
<string-array name="settings_advanced_connection_protocol_entries">
<item>@string/settings_advanced_connection_protocol_entry_jsonhttp</item>
<item>@string/settings_advanced_connection_protocol_entry_ws</item>

View file

@ -28,6 +28,13 @@
app:entryValues="@array/detail_settings_notifications_auto_delete_values"
app:defaultValue="-1"
app:isPreferenceVisible="false"/> <!-- Same as Repository.AUTO_DELETE_USE_GLOBAL -->
<ListPreference
app:key="@string/detail_settings_notifications_insistent_max_priority_key"
app:title="@string/settings_notifications_insistent_max_priority_title"
app:entries="@array/detail_settings_notifications_insistent_max_priority_entries"
app:entryValues="@array/detail_settings_notifications_insistent_max_priority_values"
app:defaultValue="-1"
app:isPreferenceVisible="false"/> <!-- Same as Repository.INSISTENT_MAX_PRIORITY_USE_GLOBAL -->
</PreferenceCategory>
<PreferenceCategory
app:key="@string/detail_settings_appearance_header_key"