From 00e9961b2ee6a496bb224e1186fe243cb11e782c Mon Sep 17 00:00:00 2001 From: koalasat Date: Fri, 25 Oct 2024 16:06:14 +0200 Subject: [PATCH] Notifications settings --- .../koalasat/pokey/models/EncryptedStorage.kt | 75 +++++++++++++++- .../pokey/service/NotificationsService.kt | 81 +++++++++++------ .../ui/notifications/NotificationsFragment.kt | 67 +++++++++++++-- .../notifications/NotificationsViewModel.kt | 86 ++++++++++++++++++- app/src/main/res/layout/fragment_home.xml | 3 +- .../res/layout/fragment_notifications.xml | 72 +++++++++++++--- app/src/main/res/layout/fragment_relays.xml | 46 ++++++---- app/src/main/res/values/strings.xml | 7 ++ 8 files changed, 366 insertions(+), 71 deletions(-) diff --git a/app/src/main/java/com/koalasat/pokey/models/EncryptedStorage.kt b/app/src/main/java/com/koalasat/pokey/models/EncryptedStorage.kt index 26552ee..ff24ab8 100644 --- a/app/src/main/java/com/koalasat/pokey/models/EncryptedStorage.kt +++ b/app/src/main/java/com/koalasat/pokey/models/EncryptedStorage.kt @@ -9,6 +9,22 @@ import androidx.security.crypto.MasterKey object PrefKeys { const val NOSTR_PUBKEY = "nostr_pubkey" + const val NOTIFY_REPLIES = "notify_replies" + const val NOTIFY_PRIVATE = "notify_private" + const val NOTIFY_ZAPS = "notify_zaps" + const val NOTIFY_QUOTES = "notify_quotes" + const val NOTIFY_REACTIONS = "notify_reactions" + const val NOTIFY_MENTIONS = "notify_mentions" + const val NOTIFY_REPOSTS = "notify_reposts" +} +object DefaultKeys { + const val NOTIFY_REPLIES = true + const val NOTIFY_REACTIONS = true + const val NOTIFY_PRIVATE = true + const val NOTIFY_ZAPS = true + const val NOTIFY_QUOTES = true + const val NOTIFY_MENTIONS = true + const val NOTIFY_REPOSTS = true } object EncryptedStorage { @@ -19,6 +35,21 @@ object EncryptedStorage { private val _pubKey = MutableLiveData() val pubKey: LiveData get() = _pubKey + private val _notifyReplies = MutableLiveData().apply { DefaultKeys.NOTIFY_REPLIES } + val notifyReplies: LiveData get() = _notifyReplies + private val _notifyReactions = MutableLiveData().apply { DefaultKeys.NOTIFY_REACTIONS } + val notifyReactions: LiveData get() = _notifyReactions + private val _notifyPrivate = MutableLiveData().apply { DefaultKeys.NOTIFY_PRIVATE } + val notifyPrivate: LiveData get() = _notifyPrivate + private val _notifyZaps = MutableLiveData().apply { DefaultKeys.NOTIFY_ZAPS } + val notifyZaps: LiveData get() = _notifyZaps + private val _notifyQuotes = MutableLiveData().apply { DefaultKeys.NOTIFY_QUOTES } + val notifyQuotes: LiveData get() = _notifyQuotes + private val _notifyMentions = MutableLiveData().apply { DefaultKeys.NOTIFY_MENTIONS } + val notifyMentions: LiveData get() = _notifyMentions + private val _notifyResposts = MutableLiveData().apply { DefaultKeys.NOTIFY_REPOSTS } + val notifyResposts: LiveData get() = _notifyResposts + fun init(context: Context) { val masterKey: MasterKey = MasterKey.Builder(context, MasterKey.DEFAULT_MASTER_KEY_ALIAS) @@ -33,11 +64,53 @@ object EncryptedStorage { EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM, ) as EncryptedSharedPreferences - _pubKey.value = sharedPreferences.getString(PrefKeys.NOSTR_PUBKEY, "").toString() + _pubKey.value = sharedPreferences.getString(PrefKeys.NOSTR_PUBKEY, "") + _notifyReplies.value = sharedPreferences.getBoolean(PrefKeys.NOTIFY_REPLIES, DefaultKeys.NOTIFY_REPLIES) + _notifyReactions.value = sharedPreferences.getBoolean(PrefKeys.NOTIFY_REACTIONS, DefaultKeys.NOTIFY_REACTIONS) + _notifyPrivate.value = sharedPreferences.getBoolean(PrefKeys.NOTIFY_PRIVATE, DefaultKeys.NOTIFY_PRIVATE) + _notifyZaps.value = sharedPreferences.getBoolean(PrefKeys.NOTIFY_ZAPS, DefaultKeys.NOTIFY_ZAPS) + _notifyQuotes.value = sharedPreferences.getBoolean(PrefKeys.NOTIFY_QUOTES, DefaultKeys.NOTIFY_QUOTES) + _notifyMentions.value = sharedPreferences.getBoolean(PrefKeys.NOTIFY_MENTIONS, DefaultKeys.NOTIFY_MENTIONS) + _notifyResposts.value = sharedPreferences.getBoolean(PrefKeys.NOTIFY_REPOSTS, DefaultKeys.NOTIFY_REPOSTS) } fun updatePubKey(newValue: String) { sharedPreferences.edit().putString(PrefKeys.NOSTR_PUBKEY, newValue).apply() _pubKey.value = newValue } + + fun updateNotifyReplies(newValue: Boolean) { + sharedPreferences.edit().putBoolean(PrefKeys.NOTIFY_REPLIES, newValue).apply() + _notifyReplies.value = newValue + } + + fun updateNotifyReactions(newValue: Boolean) { + sharedPreferences.edit().putBoolean(PrefKeys.NOTIFY_REACTIONS, newValue).apply() + _notifyReactions.value = newValue + } + + fun updateNotifyPrivate(newValue: Boolean) { + sharedPreferences.edit().putBoolean(PrefKeys.NOTIFY_PRIVATE, newValue).apply() + _notifyPrivate.value = newValue + } + + fun updateNotifyZaps(newValue: Boolean) { + sharedPreferences.edit().putBoolean(PrefKeys.NOTIFY_ZAPS, newValue).apply() + _notifyZaps.value = newValue + } + + fun updateNotifyQuotes(newValue: Boolean) { + sharedPreferences.edit().putBoolean(PrefKeys.NOTIFY_QUOTES, newValue).apply() + _notifyQuotes.value = newValue + } + + fun updateNotifyMentions(newValue: Boolean) { + sharedPreferences.edit().putBoolean(PrefKeys.NOTIFY_MENTIONS, newValue).apply() + _notifyMentions.value = newValue + } + + fun updateNotifyReposts(newValue: Boolean) { + sharedPreferences.edit().putBoolean(PrefKeys.NOTIFY_REPOSTS, newValue).apply() + _notifyResposts.value = newValue + } } diff --git a/app/src/main/java/com/koalasat/pokey/service/NotificationsService.kt b/app/src/main/java/com/koalasat/pokey/service/NotificationsService.kt index 0f225b2..8fa8956 100644 --- a/app/src/main/java/com/koalasat/pokey/service/NotificationsService.kt +++ b/app/src/main/java/com/koalasat/pokey/service/NotificationsService.kt @@ -27,6 +27,7 @@ import com.vitorpamplona.ammolite.relays.TypedFilter import com.vitorpamplona.ammolite.relays.filters.EOSETime import com.vitorpamplona.ammolite.relays.filters.SincePerRelayFilter import com.vitorpamplona.quartz.encoders.Hex +import com.vitorpamplona.quartz.encoders.LnInvoiceUtil import com.vitorpamplona.quartz.encoders.Nip19Bech32 import com.vitorpamplona.quartz.encoders.Nip19Bech32.uriToRoute import com.vitorpamplona.quartz.encoders.toNote @@ -334,37 +335,61 @@ class NotificationsService : Service() { val pubKey = EncryptedStorage.pubKey var nip32Bech32 = "" - if (event.kind == 1) { - title = if (event.content().contains("nostr:$pubKey")) { - getString(R.string.new_mention) - } else if (event.content().contains("nostr:nevent1")) { - getString(R.string.new_quote) - } else { - getString(R.string.new_reply) + when (event.kind) { + 1 -> { + title = when { + event.content().contains("nostr:$pubKey") -> { + if (!EncryptedStorage.notifyMentions.value!!) return@launch + getString(R.string.new_mention) + } + event.content().contains("nostr:nevent1") -> { + if (!EncryptedStorage.notifyQuotes.value!!) return@launch + getString(R.string.new_quote) + } + else -> { + if (!EncryptedStorage.notifyReplies.value!!) return@launch + getString(R.string.new_reply) + } + } + text = event.content().replace(Regex("nostr:[a-zA-Z0-9]+"), "") + nip32Bech32 = Hex.decode(event.id).toNote() + } + 6 -> { + if (!EncryptedStorage.notifyResposts.value!!) return@launch + + title = getString(R.string.new_repost) + nip32Bech32 = Hex.decode(event.id).toNote() } - text = event.content().replace(Regex("nostr:[a-zA-Z0-9]+"), "") - nip32Bech32 = Hex.decode(event.id).toNote() - } else if (event.kind == 6) { - title = getString(R.string.new_repost) - nip32Bech32 = Hex.decode(event.id).toNote() - } else if (event.kind == 4 || event.kind == 13) { - title = getString(R.string.new_private) - nip32Bech32 = Hex.decode(event.pubKey).toNpub() - } else if (event.kind == 7) { - title = getString(R.string.new_reaction) - text = if (event.content.isEmpty() || event.content == "+") { - "❤\uFE0F" - } else { - event.content + 4, 13 -> { + if (!EncryptedStorage.notifyPrivate.value!!) return@launch + + title = getString(R.string.new_private) + nip32Bech32 = Hex.decode(event.pubKey).toNpub() + } + 7 -> { + if (!EncryptedStorage.notifyReactions.value!!) return@launch + + title = getString(R.string.new_reaction) + text = if (event.content.isEmpty() || event.content == "+") { + "❤\uFE0F" + } else { + event.content + } + val taggedEvent = event.taggedEvents().first() + nip32Bech32 = Hex.decode(taggedEvent).toNote() + } + 9735 -> { + if (!EncryptedStorage.notifyZaps.value!!) return@launch + + title = getString(R.string.new_zap) + val bolt11 = event.firstTag("bolt11") + if (!bolt11.isNullOrEmpty()) { + val sats = LnInvoiceUtil.getAmountInSats(bolt11) + text = "⚡ $sats Sats" + } } - val taggedEvent = event.taggedEvents().first() - nip32Bech32 = Hex.decode(taggedEvent).toNote() - } else if (event.kind == 9735) { - title = getString(R.string.new_zap) - var sats = event.zapraiserAmount() - text = "⚡ $sats Sats" } - Log.d("Pokey", nip32Bech32) + if (title.isEmpty()) return@launch displayNoteNotification(title, text, nip32Bech32, event) diff --git a/app/src/main/java/com/koalasat/pokey/ui/notifications/NotificationsFragment.kt b/app/src/main/java/com/koalasat/pokey/ui/notifications/NotificationsFragment.kt index ce6a978..453a587 100644 --- a/app/src/main/java/com/koalasat/pokey/ui/notifications/NotificationsFragment.kt +++ b/app/src/main/java/com/koalasat/pokey/ui/notifications/NotificationsFragment.kt @@ -1,20 +1,18 @@ package com.koalasat.pokey.ui.notifications import android.os.Bundle +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.TextView import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider import com.koalasat.pokey.databinding.FragmentNotificationsBinding +import com.koalasat.pokey.models.EncryptedStorage class NotificationsFragment : Fragment() { private var _binding: FragmentNotificationsBinding? = null - - // This property is only valid between onCreateView and - // onDestroyView. private val binding get() = _binding!! override fun onCreateView( @@ -22,16 +20,69 @@ class NotificationsFragment : Fragment() { container: ViewGroup?, savedInstanceState: Bundle?, ): View { - val notificationsViewModel = + val viewModel = ViewModelProvider(this).get(NotificationsViewModel::class.java) _binding = FragmentNotificationsBinding.inflate(inflater, container, false) val root: View = binding.root - val textView: TextView = binding.textNotifications - notificationsViewModel.text.observe(viewLifecycleOwner) { - textView.text = it + viewModel.newReplies.value.apply { EncryptedStorage.notifyReplies.value } + binding.newReplies.setOnCheckedChangeListener { _, isChecked -> + viewModel.updateNotifyReplies(isChecked) + } + viewModel.newReplies.observe(viewLifecycleOwner) { value -> + binding.newReplies.isChecked = value + } + + viewModel.newZaps.value.apply { EncryptedStorage.notifyZaps.value } + binding.newZaps.setOnCheckedChangeListener { _, isChecked -> + viewModel.updateNotifyZaps(isChecked) + } + viewModel.newZaps.observe(viewLifecycleOwner) { value -> + Log.d("Pokey", "binding.newZaps.isChecked" + binding.newZaps.isChecked) + binding.newZaps.isChecked = value + } + + viewModel.newReactions.value.apply { EncryptedStorage.notifyReactions.value } + binding.newReactions.setOnCheckedChangeListener { _, isChecked -> + viewModel.updateNotifyReactions(isChecked) + } + viewModel.newReactions.observe(viewLifecycleOwner) { value -> + binding.newReactions.isChecked = value + } + + viewModel.newPrivate.value.apply { EncryptedStorage.notifyPrivate.value } + binding.newPrivate.setOnCheckedChangeListener { _, isChecked -> + viewModel.updateNotifyPrivate(isChecked) + } + viewModel.newPrivate.observe(viewLifecycleOwner) { value -> + binding.newPrivate.isChecked = value } + + viewModel.newQuotes.value.apply { EncryptedStorage.notifyQuotes.value } + binding.newQuotes.setOnCheckedChangeListener { _, isChecked -> + viewModel.updateNotifyQuotes(isChecked) + } + viewModel.newQuotes.observe(viewLifecycleOwner) { value -> + binding.newQuotes.isChecked = value + } + + viewModel.newMentions.value.apply { EncryptedStorage.notifyMentions.value } + binding.newMentions.setOnCheckedChangeListener { _, isChecked -> + viewModel.updateNotifyMentions(isChecked) + } + viewModel.newMentions.observe(viewLifecycleOwner) { value -> + binding.newMentions.isChecked = value + } + + viewModel.newReposts.value.apply { EncryptedStorage.notifyResposts.value } + binding.newReposts.setOnCheckedChangeListener { _, isChecked -> + viewModel.updateNotifyReposts(isChecked) + } + viewModel.newReposts.observe(viewLifecycleOwner) { value -> + binding.newReposts.isChecked = value + } + return root } diff --git a/app/src/main/java/com/koalasat/pokey/ui/notifications/NotificationsViewModel.kt b/app/src/main/java/com/koalasat/pokey/ui/notifications/NotificationsViewModel.kt index c3a1725..5d7e7d0 100644 --- a/app/src/main/java/com/koalasat/pokey/ui/notifications/NotificationsViewModel.kt +++ b/app/src/main/java/com/koalasat/pokey/ui/notifications/NotificationsViewModel.kt @@ -1,13 +1,93 @@ package com.koalasat.pokey.ui.notifications +import android.util.Log import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel +import com.koalasat.pokey.models.EncryptedStorage class NotificationsViewModel : ViewModel() { - private val _text = MutableLiveData().apply { - value = "Coming Soon!" + private val _newReplies = MutableLiveData().apply { value = EncryptedStorage.notifyReplies.value } + val newReplies: LiveData = _newReplies + + private val _newZaps = MutableLiveData().apply { value = EncryptedStorage.notifyZaps.value } + val newZaps: LiveData = _newZaps + + private val _newQuotes = MutableLiveData().apply { value = EncryptedStorage.notifyQuotes.value } + val newQuotes: LiveData = _newQuotes + + private val _newReactions = MutableLiveData().apply { value = EncryptedStorage.notifyReactions.value } + val newReactions: LiveData = _newReactions + + private val _newPrivate = MutableLiveData().apply { value = EncryptedStorage.notifyPrivate.value } + val newPrivate: LiveData = _newPrivate + + private val _newMentions = MutableLiveData().apply { value = EncryptedStorage.notifyMentions.value } + val newMentions: LiveData = _newMentions + + private val _newReposts = MutableLiveData().apply { value = EncryptedStorage.notifyResposts.value } + val newReposts: LiveData = _newReposts + + init { + EncryptedStorage.notifyReplies.observeForever { value -> + _newReplies.value = value + } + Log.d("Pokey", "_newZaps.value" + _newZaps.value) + EncryptedStorage.notifyZaps.observeForever { value -> + _newZaps.value = value + Log.d("Pokey", "observeForever" + value) + } + EncryptedStorage.notifyQuotes.observeForever { value -> + _newQuotes.value = value + } + EncryptedStorage.notifyReactions.observeForever { value -> + _newReactions.value = value + } + EncryptedStorage.notifyPrivate.observeForever { value -> + _newPrivate.value = value + } + EncryptedStorage.notifyMentions.observeForever { value -> + _newMentions.value = value + } + EncryptedStorage.notifyResposts.observeForever { value -> + _newReposts.value = value + } + } + + fun updateNotifyReplies(value: Boolean) { + _newReplies.value = value + EncryptedStorage.updateNotifyReplies(value) + } + + fun updateNotifyReactions(value: Boolean) { + _newReactions.value = value + EncryptedStorage.updateNotifyReactions(value) + } + + fun updateNotifyPrivate(value: Boolean) { + _newPrivate.value = value + EncryptedStorage.updateNotifyPrivate(value) + } + + fun updateNotifyZaps(value: Boolean) { + _newZaps.value = value + Log.d("Pokey", "updateNotifyZaps" + value) + EncryptedStorage.updateNotifyZaps(value) + } + + fun updateNotifyQuotes(value: Boolean) { + _newQuotes.value = value + EncryptedStorage.updateNotifyQuotes(value) + } + + fun updateNotifyMentions(value: Boolean) { + _newMentions.value = value + EncryptedStorage.updateNotifyMentions(value) + } + + fun updateNotifyReposts(value: Boolean) { + _newReposts.value = value + EncryptedStorage.updateNotifyReposts(value) } - val text: LiveData = _text } diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml index f96ebc3..a838e09 100644 --- a/app/src/main/res/layout/fragment_home.xml +++ b/app/src/main/res/layout/fragment_home.xml @@ -12,6 +12,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="68dp" + android:padding="16dp" android:text="@string/start" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.498" @@ -58,9 +59,9 @@ android:id="@+id/amber" android:layout_width="57dp" android:layout_height="48dp" + android:layout_marginStart="9dp" android:drawableTop="@drawable/ic_amber" android:drawablePadding="22dp" - android:layout_marginStart="9dp" android:gravity="center" tools:ignore="MissingConstraints" /> diff --git a/app/src/main/res/layout/fragment_notifications.xml b/app/src/main/res/layout/fragment_notifications.xml index d417935..259a575 100644 --- a/app/src/main/res/layout/fragment_notifications.xml +++ b/app/src/main/res/layout/fragment_notifications.xml @@ -6,17 +6,67 @@ android:layout_height="match_parent" tools:context=".ui.notifications.NotificationsFragment"> - - \ No newline at end of file + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintVertical_bias="0.5"> + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_relays.xml b/app/src/main/res/layout/fragment_relays.xml index 7836c6a..9859fd8 100644 --- a/app/src/main/res/layout/fragment_relays.xml +++ b/app/src/main/res/layout/fragment_relays.xml @@ -6,26 +6,34 @@ android:layout_height="match_parent" tools:context=".ui.relays.RelaysFragment"> - + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintVertical_bias="0.5" + android:paddingLeft="16dp" + android:paddingRight="16dp"> - + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 00252be..f8de70f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -26,4 +26,11 @@ Start Stop Logo + New replies + New reactions + New private messages + New zaps + New quotes + New mentions + New reposts