519 lines
26 KiB
Kotlin
519 lines
26 KiB
Kotlin
package io.heckel.ntfy.ui
|
|
|
|
import android.content.ContentResolver
|
|
import android.content.ClipData
|
|
import android.content.ClipboardManager
|
|
import android.content.Context
|
|
import android.content.Intent
|
|
import android.net.Uri
|
|
import android.os.Bundle
|
|
import android.provider.Settings
|
|
import android.text.TextUtils
|
|
import android.widget.Toast
|
|
import androidx.activity.result.ActivityResultLauncher
|
|
import androidx.activity.result.contract.ActivityResultContracts
|
|
import androidx.appcompat.app.AppCompatActivity
|
|
import androidx.core.content.FileProvider
|
|
import androidx.core.graphics.drawable.toDrawable
|
|
import androidx.lifecycle.lifecycleScope
|
|
import androidx.preference.*
|
|
import androidx.preference.Preference.OnPreferenceClickListener
|
|
import io.heckel.ntfy.BuildConfig
|
|
import io.heckel.ntfy.R
|
|
import io.heckel.ntfy.db.Repository
|
|
import io.heckel.ntfy.db.Subscription
|
|
import io.heckel.ntfy.msg.DownloadAttachmentWorker
|
|
import io.heckel.ntfy.msg.NotificationService
|
|
import io.heckel.ntfy.service.SubscriberServiceManager
|
|
import io.heckel.ntfy.util.*
|
|
import kotlinx.coroutines.*
|
|
import java.io.File
|
|
import java.io.IOException
|
|
import java.util.*
|
|
|
|
/**
|
|
* Subscription settings
|
|
*/
|
|
class DetailSettingsActivity : AppCompatActivity() {
|
|
private lateinit var repository: Repository
|
|
private lateinit var serviceManager: SubscriberServiceManager
|
|
private lateinit var settingsFragment: SettingsFragment
|
|
private lateinit var notificationService: NotificationService
|
|
private var subscriptionId: Long = 0
|
|
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
super.onCreate(savedInstanceState)
|
|
setContentView(R.layout.activity_settings)
|
|
|
|
Log.d(TAG, "Create $this")
|
|
|
|
repository = Repository.getInstance(this)
|
|
serviceManager = SubscriberServiceManager(this)
|
|
notificationService = NotificationService(this)
|
|
subscriptionId = intent.getLongExtra(DetailActivity.EXTRA_SUBSCRIPTION_ID, 0)
|
|
|
|
if (savedInstanceState == null) {
|
|
settingsFragment = SettingsFragment() // Empty constructor!
|
|
settingsFragment.arguments = Bundle().apply {
|
|
this.putLong(DetailActivity.EXTRA_SUBSCRIPTION_ID, subscriptionId)
|
|
}
|
|
supportFragmentManager
|
|
.beginTransaction()
|
|
.replace(R.id.settings_layout, settingsFragment)
|
|
.commit()
|
|
}
|
|
|
|
// Title
|
|
val displayName = intent.getStringExtra(DetailActivity.EXTRA_SUBSCRIPTION_DISPLAY_NAME) ?: return
|
|
title = displayName
|
|
|
|
// Show 'Back' button
|
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
|
}
|
|
|
|
override fun onSupportNavigateUp(): Boolean {
|
|
finish() // Return to previous activity when nav "back" is pressed!
|
|
return true
|
|
}
|
|
|
|
class SettingsFragment : PreferenceFragmentCompat() {
|
|
private lateinit var resolver: ContentResolver
|
|
private lateinit var repository: Repository
|
|
private lateinit var serviceManager: SubscriberServiceManager
|
|
private lateinit var notificationService: NotificationService
|
|
private lateinit var subscription: Subscription
|
|
|
|
private lateinit var iconSetPref: Preference
|
|
private lateinit var openChannelsPref: Preference
|
|
private lateinit var iconSetLauncher: ActivityResultLauncher<String>
|
|
private lateinit var iconRemovePref: Preference
|
|
|
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
|
setPreferencesFromResource(R.xml.detail_preferences, rootKey)
|
|
|
|
// Dependencies (Fragments need a default constructor)
|
|
repository = Repository.getInstance(requireActivity())
|
|
serviceManager = SubscriberServiceManager(requireActivity())
|
|
notificationService = NotificationService(requireActivity())
|
|
resolver = requireContext().applicationContext.contentResolver
|
|
|
|
// Create result launcher for custom icon (must be created in onCreatePreferences() directly)
|
|
iconSetLauncher = createIconPickLauncher()
|
|
|
|
// Load subscription and users
|
|
val subscriptionId = arguments?.getLong(DetailActivity.EXTRA_SUBSCRIPTION_ID) ?: return
|
|
runBlocking {
|
|
withContext(Dispatchers.IO) {
|
|
subscription = repository.getSubscription(subscriptionId) ?: return@withContext
|
|
activity?.runOnUiThread {
|
|
loadView()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun loadView() {
|
|
if (subscription.upAppId == null) {
|
|
loadInstantPref()
|
|
loadMutedUntilPref()
|
|
loadMinPriorityPref()
|
|
loadAutoDeletePref()
|
|
loadInsistentMaxPriorityPref()
|
|
loadIconSetPref()
|
|
loadIconRemovePref()
|
|
if (notificationService.channelsSupported()) {
|
|
loadDedicatedChannelsPrefs()
|
|
loadOpenChannelsPrefs()
|
|
}
|
|
} else {
|
|
val notificationsHeaderId = context?.getString(R.string.detail_settings_notifications_header_key) ?: return
|
|
val notificationsHeader: PreferenceCategory? = findPreference(notificationsHeaderId)
|
|
notificationsHeader?.isVisible = false
|
|
}
|
|
loadDisplayNamePref()
|
|
loadTopicUrlPref()
|
|
}
|
|
|
|
private fun loadInstantPref() {
|
|
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)
|
|
pref?.isVisible = BuildConfig.FIREBASE_AVAILABLE && subscription.baseUrl == appBaseUrl
|
|
pref?.isChecked = subscription.instant
|
|
pref?.preferenceDataStore = object : PreferenceDataStore() {
|
|
override fun putBoolean(key: String?, value: Boolean) {
|
|
save(subscription.copy(instant = value), refresh = true)
|
|
}
|
|
override fun getBoolean(key: String?, defValue: Boolean): Boolean {
|
|
return subscription.instant
|
|
}
|
|
}
|
|
pref?.summaryProvider = Preference.SummaryProvider<SwitchPreference> { preference ->
|
|
if (preference.isChecked) {
|
|
getString(R.string.detail_settings_notifications_instant_summary_on)
|
|
} else {
|
|
getString(R.string.detail_settings_notifications_instant_summary_off)
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun loadDedicatedChannelsPrefs() {
|
|
val prefId = context?.getString(R.string.detail_settings_notifications_dedicated_channels_key) ?: return
|
|
val pref: SwitchPreference? = findPreference(prefId)
|
|
pref?.isVisible = true
|
|
pref?.isChecked = subscription.dedicatedChannels
|
|
pref?.preferenceDataStore = object : PreferenceDataStore() {
|
|
override fun putBoolean(key: String?, value: Boolean) {
|
|
save(subscription.copy(dedicatedChannels = value))
|
|
if (value) {
|
|
notificationService.createSubscriptionNotificationChannels(subscription)
|
|
} else {
|
|
notificationService.deleteSubscriptionNotificationChannels(subscription)
|
|
}
|
|
openChannelsPref.isVisible = value
|
|
}
|
|
override fun getBoolean(key: String?, defValue: Boolean): Boolean {
|
|
return subscription.dedicatedChannels
|
|
}
|
|
}
|
|
pref?.summaryProvider = Preference.SummaryProvider<SwitchPreference> { preference ->
|
|
if (preference.isChecked) {
|
|
getString(R.string.detail_settings_notifications_dedicated_channels_summary_on)
|
|
} else {
|
|
getString(R.string.detail_settings_notifications_dedicated_channels_summary_off)
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun loadOpenChannelsPrefs() {
|
|
val prefId = context?.getString(R.string.detail_settings_notifications_open_channels_key) ?: return
|
|
openChannelsPref = findPreference(prefId) ?: return
|
|
openChannelsPref.isVisible = subscription.dedicatedChannels
|
|
openChannelsPref.preferenceDataStore = object : PreferenceDataStore() { } // Dummy store to protect from accidentally overwriting
|
|
openChannelsPref.onPreferenceClickListener = Preference.OnPreferenceClickListener { _ ->
|
|
val settingsIntent = Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS)
|
|
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
.putExtra(Settings.EXTRA_APP_PACKAGE, requireContext().applicationContext.packageName)
|
|
startActivity(settingsIntent);
|
|
true
|
|
}
|
|
}
|
|
|
|
private fun loadMutedUntilPref() {
|
|
val prefId = context?.getString(R.string.detail_settings_notifications_muted_until_key) ?: return
|
|
val pref: ListPreference? = findPreference(prefId)
|
|
pref?.isVisible = true // Hack: Show all settings at once, because subscription is loaded asynchronously
|
|
pref?.value = subscription.mutedUntil.toString()
|
|
pref?.preferenceDataStore = object : PreferenceDataStore() {
|
|
override fun putString(key: String?, value: String?) {
|
|
val mutedUntilValue = value?.toLongOrNull() ?:return
|
|
when (mutedUntilValue) {
|
|
Repository.MUTED_UNTIL_SHOW_ALL -> save(subscription.copy(mutedUntil = mutedUntilValue))
|
|
Repository.MUTED_UNTIL_FOREVER -> save(subscription.copy(mutedUntil = mutedUntilValue))
|
|
Repository.MUTED_UNTIL_TOMORROW -> {
|
|
val date = Calendar.getInstance()
|
|
date.add(Calendar.DAY_OF_MONTH, 1)
|
|
date.set(Calendar.HOUR_OF_DAY, 8)
|
|
date.set(Calendar.MINUTE, 30)
|
|
date.set(Calendar.SECOND, 0)
|
|
date.set(Calendar.MILLISECOND, 0)
|
|
save(subscription.copy(mutedUntil = date.timeInMillis/1000))
|
|
}
|
|
else -> {
|
|
val mutedUntilTimestamp = System.currentTimeMillis()/1000 + mutedUntilValue * 60
|
|
save(subscription.copy(mutedUntil = mutedUntilTimestamp))
|
|
}
|
|
}
|
|
}
|
|
override fun getString(key: String?, defValue: String?): String {
|
|
return subscription.mutedUntil.toString()
|
|
}
|
|
}
|
|
pref?.summaryProvider = Preference.SummaryProvider<ListPreference> {
|
|
when (val mutedUntilValue = subscription.mutedUntil) {
|
|
Repository.MUTED_UNTIL_SHOW_ALL -> getString(R.string.settings_notifications_muted_until_show_all)
|
|
Repository.MUTED_UNTIL_FOREVER -> getString(R.string.settings_notifications_muted_until_forever)
|
|
else -> {
|
|
val formattedDate = formatDateShort(mutedUntilValue)
|
|
getString(R.string.settings_notifications_muted_until_x, formattedDate)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun loadMinPriorityPref() {
|
|
val prefId = context?.getString(R.string.detail_settings_notifications_min_priority_key) ?: return
|
|
val pref: ListPreference? = findPreference(prefId)
|
|
pref?.isVisible = true // Hack: Show all settings at once, because subscription is loaded asynchronously
|
|
pref?.value = subscription.minPriority.toString()
|
|
pref?.preferenceDataStore = object : PreferenceDataStore() {
|
|
override fun putString(key: String?, value: String?) {
|
|
val minPriorityValue = value?.toIntOrNull() ?:return
|
|
save(subscription.copy(minPriority = minPriorityValue))
|
|
}
|
|
override fun getString(key: String?, defValue: String?): String {
|
|
return subscription.minPriority.toString()
|
|
}
|
|
}
|
|
pref?.summaryProvider = Preference.SummaryProvider<ListPreference> { preference ->
|
|
var value = preference.value.toIntOrNull() ?: Repository.MIN_PRIORITY_USE_GLOBAL
|
|
val global = value == Repository.MIN_PRIORITY_USE_GLOBAL
|
|
if (value == Repository.MIN_PRIORITY_USE_GLOBAL) {
|
|
value = repository.getMinPriority()
|
|
}
|
|
val summary = when (value) {
|
|
PRIORITY_MIN -> getString(R.string.settings_notifications_min_priority_summary_any)
|
|
PRIORITY_MAX -> getString(R.string.settings_notifications_min_priority_summary_max)
|
|
else -> {
|
|
val minPriorityString = toPriorityString(requireContext(), value)
|
|
getString(R.string.settings_notifications_min_priority_summary_x_or_higher, value, minPriorityString)
|
|
}
|
|
}
|
|
maybeAppendGlobal(summary, global)
|
|
}
|
|
}
|
|
|
|
private fun loadAutoDeletePref() {
|
|
val prefId = context?.getString(R.string.detail_settings_notifications_auto_delete_key) ?: return
|
|
val pref: ListPreference? = findPreference(prefId)
|
|
pref?.isVisible = true // Hack: Show all settings at once, because subscription is loaded asynchronously
|
|
pref?.value = subscription.autoDelete.toString()
|
|
pref?.preferenceDataStore = object : PreferenceDataStore() {
|
|
override fun putString(key: String?, value: String?) {
|
|
val seconds = value?.toLongOrNull() ?:return
|
|
save(subscription.copy(autoDelete = seconds))
|
|
}
|
|
override fun getString(key: String?, defValue: String?): String {
|
|
return subscription.autoDelete.toString()
|
|
}
|
|
}
|
|
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 (global) {
|
|
seconds = repository.getAutoDeleteSeconds()
|
|
}
|
|
val summary = when (seconds) {
|
|
Repository.AUTO_DELETE_NEVER -> getString(R.string.settings_notifications_auto_delete_summary_never)
|
|
Repository.AUTO_DELETE_ONE_DAY_SECONDS -> getString(R.string.settings_notifications_auto_delete_summary_one_day)
|
|
Repository.AUTO_DELETE_THREE_DAYS_SECONDS -> getString(R.string.settings_notifications_auto_delete_summary_three_days)
|
|
Repository.AUTO_DELETE_ONE_WEEK_SECONDS -> getString(R.string.settings_notifications_auto_delete_summary_one_week)
|
|
Repository.AUTO_DELETE_ONE_MONTH_SECONDS -> getString(R.string.settings_notifications_auto_delete_summary_one_month)
|
|
Repository.AUTO_DELETE_THREE_MONTHS_SECONDS -> getString(R.string.settings_notifications_auto_delete_summary_three_months)
|
|
else -> getString(R.string.settings_notifications_auto_delete_summary_one_month) // Must match default const
|
|
}
|
|
maybeAppendGlobal(summary, global)
|
|
}
|
|
}
|
|
|
|
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?.value = subscription.insistent.toString()
|
|
pref?.preferenceDataStore = object : PreferenceDataStore() {
|
|
override fun putString(key: String?, value: String?) {
|
|
val intValue = value?.toIntOrNull() ?:return
|
|
save(subscription.copy(insistent = intValue))
|
|
}
|
|
override fun getString(key: String?, defValue: String?): String {
|
|
return subscription.insistent.toString()
|
|
}
|
|
}
|
|
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.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
|
|
iconSetPref.isVisible = subscription.icon == null
|
|
iconSetPref.preferenceDataStore = object : PreferenceDataStore() { } // Dummy store to protect from accidentally overwriting
|
|
iconSetPref.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
|
iconSetLauncher.launch("image/*")
|
|
true
|
|
}
|
|
}
|
|
|
|
private fun loadIconRemovePref() {
|
|
val prefId = context?.getString(R.string.detail_settings_appearance_icon_remove_key) ?: return
|
|
iconRemovePref = findPreference(prefId) ?: return
|
|
iconRemovePref.isVisible = subscription.icon != null
|
|
iconRemovePref.preferenceDataStore = object : PreferenceDataStore() { } // Dummy store to protect from accidentally overwriting
|
|
iconRemovePref.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
|
iconRemovePref.isVisible = false
|
|
iconSetPref.isVisible = true
|
|
deleteIcon(subscription.icon)
|
|
save(subscription.copy(icon = null))
|
|
true
|
|
}
|
|
|
|
// Set icon (if it exists)
|
|
if (subscription.icon != null) {
|
|
try {
|
|
val bitmap = subscription.icon!!.readBitmapFromUri(requireContext())
|
|
iconRemovePref.icon = bitmap.toDrawable(resources)
|
|
} catch (e: Exception) {
|
|
Log.w(TAG, "Unable to set icon ${subscription.icon}", e)
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun loadDisplayNamePref() {
|
|
val prefId = context?.getString(R.string.detail_settings_appearance_display_name_key) ?: return
|
|
val pref: EditTextPreference? = findPreference(prefId)
|
|
pref?.isVisible = true // Hack: Show all settings at once, because subscription is loaded asynchronously
|
|
pref?.text = subscription.displayName
|
|
pref?.dialogMessage = getString(R.string.detail_settings_appearance_display_name_message, topicShortUrl(subscription.baseUrl, subscription.topic))
|
|
pref?.preferenceDataStore = object : PreferenceDataStore() {
|
|
override fun putString(key: String?, value: String?) {
|
|
val displayName = if (value != "") value else null
|
|
val newSubscription = subscription.copy(displayName = displayName)
|
|
save(newSubscription)
|
|
// Update activity title
|
|
activity?.runOnUiThread {
|
|
activity?.title = displayName(newSubscription)
|
|
}
|
|
// Update dedicated notification channel
|
|
if (newSubscription.dedicatedChannels) {
|
|
notificationService.createSubscriptionNotificationChannels(newSubscription)
|
|
}
|
|
}
|
|
override fun getString(key: String?, defValue: String?): String {
|
|
return subscription.displayName ?: ""
|
|
}
|
|
}
|
|
pref?.summaryProvider = Preference.SummaryProvider<EditTextPreference> { provider ->
|
|
if (TextUtils.isEmpty(provider.text)) {
|
|
getString(
|
|
R.string.detail_settings_appearance_display_name_default_summary,
|
|
displayName(subscription)
|
|
)
|
|
} else {
|
|
provider.text
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun loadTopicUrlPref() {
|
|
// Topic URL
|
|
val topicUrlPrefId = context?.getString(R.string.detail_settings_about_topic_url_key) ?: return
|
|
val topicUrlPref: Preference? = findPreference(topicUrlPrefId)
|
|
val topicUrl = topicShortUrl(subscription.baseUrl, subscription.topic)
|
|
topicUrlPref?.summary = topicUrl
|
|
topicUrlPref?.onPreferenceClickListener = OnPreferenceClickListener {
|
|
val context = context ?: return@OnPreferenceClickListener false
|
|
val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
|
val clip = ClipData.newPlainText("topic url", topicUrl)
|
|
clipboard.setPrimaryClip(clip)
|
|
Toast
|
|
.makeText(context, getString(R.string.detail_settings_about_topic_url_copied_to_clipboard_message), Toast.LENGTH_LONG)
|
|
.show()
|
|
true
|
|
}
|
|
}
|
|
|
|
private fun createIconPickLauncher(): ActivityResultLauncher<String> {
|
|
return registerForActivityResult(ActivityResultContracts.GetContent()) { inputUri ->
|
|
if (inputUri == null) {
|
|
return@registerForActivityResult
|
|
}
|
|
lifecycleScope.launch(Dispatchers.IO) {
|
|
val outputUri = createUri() ?: return@launch
|
|
try {
|
|
// Early size & mime type check
|
|
val mimeType = resolver.getType(inputUri)
|
|
if (!supportedImage(mimeType)) {
|
|
throw IOException("unknown image type or not supported")
|
|
}
|
|
val stat = fileStat(requireContext(), inputUri) // May throw
|
|
if (stat.size > SUBSCRIPTION_ICON_MAX_SIZE_BYTES) {
|
|
throw IOException("image too large, max supported is ${SUBSCRIPTION_ICON_MAX_SIZE_BYTES/1024/1024}MB")
|
|
}
|
|
|
|
// Write to cache storage
|
|
val inputStream = resolver.openInputStream(inputUri) ?: throw IOException("Couldn't open content URI for reading")
|
|
val outputStream = resolver.openOutputStream(outputUri) ?: throw IOException("Couldn't open content URI for writing")
|
|
inputStream.use {
|
|
it.copyTo(outputStream)
|
|
}
|
|
|
|
// Read image, check dimensions
|
|
val bitmap = outputUri.readBitmapFromUri(requireContext())
|
|
if (bitmap.width > SUBSCRIPTION_ICON_MAX_WIDTH || bitmap.height > SUBSCRIPTION_ICON_MAX_HEIGHT) {
|
|
throw IOException("image exceeds max dimensions of ${SUBSCRIPTION_ICON_MAX_WIDTH}x${SUBSCRIPTION_ICON_MAX_HEIGHT}")
|
|
}
|
|
|
|
// Display "remove" preference
|
|
iconRemovePref.icon = bitmap.toDrawable(resources)
|
|
iconRemovePref.isVisible = true
|
|
iconSetPref.isVisible = false
|
|
|
|
// Finally, save (this is last!)
|
|
save(subscription.copy(icon = outputUri.toString()))
|
|
} catch (e: Exception) {
|
|
Log.w(TAG, "Saving icon failed", e)
|
|
requireActivity().runOnUiThread {
|
|
Toast.makeText(context, getString(R.string.detail_settings_appearance_icon_error_saving, e.message), Toast.LENGTH_LONG).show()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun createUri(): Uri? {
|
|
val dir = File(requireContext().cacheDir, SUBSCRIPTION_ICONS)
|
|
if (!dir.exists() && !dir.mkdirs()) {
|
|
return null
|
|
}
|
|
val file = File(dir, subscription.id.toString())
|
|
return FileProvider.getUriForFile(requireContext(), DownloadAttachmentWorker.FILE_PROVIDER_AUTHORITY, file)
|
|
}
|
|
|
|
private fun deleteIcon(uri: String?) {
|
|
if (uri == null) {
|
|
return
|
|
}
|
|
try {
|
|
resolver.delete(Uri.parse(uri), null, null)
|
|
} catch (e: Exception) {
|
|
Log.w(TAG, "Unable to delete $uri", e)
|
|
}
|
|
}
|
|
|
|
private fun save(newSubscription: Subscription, refresh: Boolean = false) {
|
|
subscription = newSubscription
|
|
lifecycleScope.launch(Dispatchers.IO) {
|
|
repository.updateSubscription(newSubscription)
|
|
if (refresh) {
|
|
SubscriberServiceManager.refresh(requireContext())
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun maybeAppendGlobal(summary: String, global: Boolean): String {
|
|
return if (global) {
|
|
summary + " (" + getString(R.string.detail_settings_global_setting_suffix) + ")"
|
|
} else {
|
|
summary
|
|
}
|
|
}
|
|
}
|
|
|
|
companion object {
|
|
private const val TAG = "NtfyDetailSettingsActiv"
|
|
private const val SUBSCRIPTION_ICONS = "subscriptionIcons"
|
|
private const val SUBSCRIPTION_ICON_MAX_SIZE_BYTES = 4194304
|
|
private const val SUBSCRIPTION_ICON_MAX_WIDTH = 2048
|
|
private const val SUBSCRIPTION_ICON_MAX_HEIGHT = 2048
|
|
}
|
|
}
|