diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 549fd3d..d3c2351 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -39,6 +39,16 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/java/io/heckel/ntfy/data/Database.kt b/app/src/main/java/io/heckel/ntfy/data/Database.kt
index afb2640..d04dcab 100644
--- a/app/src/main/java/io/heckel/ntfy/data/Database.kt
+++ b/app/src/main/java/io/heckel/ntfy/data/Database.kt
@@ -40,7 +40,7 @@ data class SubscriptionWithMetadata(
@Entity
data class Notification(
- @PrimaryKey val id: String,
+ @PrimaryKey val id: String, // TODO make [id, subscriptionId] the primary key
@ColumnInfo(name = "subscriptionId") val subscriptionId: Long,
@ColumnInfo(name = "timestamp") val timestamp: Long, // Unix timestamp
@ColumnInfo(name = "message") val message: String,
diff --git a/app/src/main/java/io/heckel/ntfy/ui/DetailActivity.kt b/app/src/main/java/io/heckel/ntfy/ui/DetailActivity.kt
index 79177bc..83baac0 100644
--- a/app/src/main/java/io/heckel/ntfy/ui/DetailActivity.kt
+++ b/app/src/main/java/io/heckel/ntfy/ui/DetailActivity.kt
@@ -5,6 +5,8 @@ import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
+import android.content.Intent.ACTION_VIEW
+import android.net.Uri
import android.os.Bundle
import android.text.Html
import android.util.Log
@@ -24,12 +26,15 @@ import io.heckel.ntfy.BuildConfig
import io.heckel.ntfy.R
import io.heckel.ntfy.app.Application
import io.heckel.ntfy.data.Notification
+import io.heckel.ntfy.data.Subscription
import io.heckel.ntfy.data.topicShortUrl
import io.heckel.ntfy.data.topicUrl
+import io.heckel.ntfy.firebase.FirebaseMessenger
import io.heckel.ntfy.msg.ApiService
import io.heckel.ntfy.msg.NotificationService
import kotlinx.coroutines.*
import java.util.*
+import kotlin.random.Random
class DetailActivity : AppCompatActivity(), ActionMode.Callback, NotificationFragment.NotificationSettingsListener {
private val viewModel by viewModels {
@@ -37,8 +42,10 @@ class DetailActivity : AppCompatActivity(), ActionMode.Callback, NotificationFra
}
private val repository by lazy { (application as Application).repository }
private val api = ApiService()
+ private val messenger = FirebaseMessenger()
private var subscriberManager: SubscriberManager? = null // Context-dependent
private var notifier: NotificationService? = null // Context-dependent
+ private var appBaseUrl: String? = null // Context-dependent
// Which subscription are we looking at
private var subscriptionId: Long = 0L // Set in onCreate()
@@ -65,10 +72,70 @@ class DetailActivity : AppCompatActivity(), ActionMode.Callback, NotificationFra
// Dependencies that depend on Context
subscriberManager = SubscriberManager(this)
notifier = NotificationService(this)
+ appBaseUrl = getString(R.string.app_base_url)
// Show 'Back' button
supportActionBar?.setDisplayHomeAsUpEnabled(true)
+ // Handle direct deep links to topic "https://ntfy.sh/..."
+ val url = intent?.data
+ if (intent?.action == ACTION_VIEW && url != null) {
+ val topic = url.pathSegments.first()
+ title = topicShortUrl(appBaseUrl!!, topic) // We assume the app base URL
+ maybeSubscribeAndLoadView(topic)
+ } else {
+ loadView()
+ }
+ }
+
+ private fun maybeSubscribeAndLoadView(topic: String) {
+ lifecycleScope.launch(Dispatchers.IO) {
+ val baseUrl = appBaseUrl!!
+ var subscription = repository.getSubscription(baseUrl, topic)
+ if (subscription == null) {
+ subscription = Subscription(
+ id = Random.nextLong(),
+ baseUrl = baseUrl,
+ topic = topic,
+ instant = false,
+ mutedUntil = 0,
+ totalCount = 0,
+ newCount = 0,
+ lastActive = Date().time/1000
+ )
+ repository.addSubscription(subscription)
+
+ // Subscribe to Firebase topic if ntfy.sh (even if instant, just to be sure!)
+ Log.d(MainActivity.TAG, "Subscribing to Firebase")
+ messenger.subscribe(topic)
+
+ // Fetch cached messages
+ try {
+ val notifications = api.poll(subscription.id, subscription.baseUrl, subscription.topic)
+ notifications.forEach { notification -> repository.addNotification(notification) }
+ } catch (e: Exception) {
+ Log.e(MainActivity.TAG, "Unable to fetch notifications: ${e.stackTrace}")
+ }
+
+ runOnUiThread {
+ val message = getString(R.string.detail_deep_link_subscribed_toast_message, topicShortUrl(baseUrl, topic))
+ Toast.makeText(this@DetailActivity, message, Toast.LENGTH_LONG).show()
+ }
+ }
+
+ intent.putExtra(MainActivity.EXTRA_SUBSCRIPTION_ID, subscription.id)
+ intent.putExtra(MainActivity.EXTRA_SUBSCRIPTION_BASE_URL, subscription.baseUrl)
+ intent.putExtra(MainActivity.EXTRA_SUBSCRIPTION_TOPIC, subscription.topic)
+ intent.putExtra(MainActivity.EXTRA_SUBSCRIPTION_INSTANT, subscription.instant)
+ intent.putExtra(MainActivity.EXTRA_SUBSCRIPTION_MUTED_UNTIL, subscription.mutedUntil)
+
+ runOnUiThread {
+ loadView()
+ }
+ }
+ }
+
+ private fun loadView() {
// Get extras required for the return to the main activity
subscriptionId = intent.getLongExtra(MainActivity.EXTRA_SUBSCRIPTION_ID, 0)
subscriptionBaseUrl = intent.getStringExtra(MainActivity.EXTRA_SUBSCRIPTION_BASE_URL) ?: return
@@ -434,17 +501,15 @@ class DetailActivity : AppCompatActivity(), ActionMode.Callback, NotificationFra
val dialog = builder
.setMessage(R.string.detail_delete_dialog_message)
.setPositiveButton(R.string.detail_delete_dialog_permanently_delete) { _, _ ->
- // Return to main activity
- val result = Intent()
- .putExtra(MainActivity.EXTRA_SUBSCRIPTION_ID, subscriptionId)
- .putExtra(MainActivity.EXTRA_SUBSCRIPTION_BASE_URL, subscriptionBaseUrl)
- .putExtra(MainActivity.EXTRA_SUBSCRIPTION_TOPIC, subscriptionTopic)
- .putExtra(MainActivity.EXTRA_SUBSCRIPTION_INSTANT, subscriptionInstant)
- .putExtra(MainActivity.EXTRA_SUBSCRIPTION_MUTED_UNTIL, subscriptionMutedUntil)
- setResult(RESULT_OK, result)
+ Log.d(MainActivity.TAG, "Deleting subscription with subscription ID $subscriptionId (topic: $subscriptionTopic)")
+ GlobalScope.launch(Dispatchers.IO) {
+ repository.removeAllNotifications(subscriptionId)
+ repository.removeSubscription(subscriptionId)
+ if (subscriptionBaseUrl == appBaseUrl) {
+ messenger.unsubscribe(subscriptionTopic)
+ }
+ }
finish()
-
- // The deletion will be done in MainActivity.onResult
}
.setNegativeButton(R.string.detail_delete_dialog_cancel) { _, _ -> /* Do nothing */ }
.create()
diff --git a/app/src/main/java/io/heckel/ntfy/ui/MainActivity.kt b/app/src/main/java/io/heckel/ntfy/ui/MainActivity.kt
index 989ab7f..9ce23b9 100644
--- a/app/src/main/java/io/heckel/ntfy/ui/MainActivity.kt
+++ b/app/src/main/java/io/heckel/ntfy/ui/MainActivity.kt
@@ -345,27 +345,7 @@ class MainActivity : AppCompatActivity(), ActionMode.Callback, AddFragment.Subsc
intent.putExtra(EXTRA_SUBSCRIPTION_TOPIC, subscription.topic)
intent.putExtra(EXTRA_SUBSCRIPTION_INSTANT, subscription.instant)
intent.putExtra(EXTRA_SUBSCRIPTION_MUTED_UNTIL, subscription.mutedUntil)
- startActivityForResult(intent, REQUEST_CODE_DELETE_SUBSCRIPTION)
- }
-
- override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
- if (requestCode == REQUEST_CODE_DELETE_SUBSCRIPTION && resultCode == RESULT_OK) {
- val subscriptionId = data?.getLongExtra(EXTRA_SUBSCRIPTION_ID, 0)
- val subscriptionBaseUrl = data?.getStringExtra(EXTRA_SUBSCRIPTION_BASE_URL)
- val subscriptionTopic = data?.getStringExtra(EXTRA_SUBSCRIPTION_TOPIC)
- Log.d(TAG, "Deleting subscription with subscription ID $subscriptionId (topic: $subscriptionTopic)")
-
- subscriptionId?.let { id -> viewModel.remove(id) }
- subscriptionBaseUrl?.let { baseUrl ->
- if (baseUrl == appBaseUrl) {
- Log.d(TAG, "Unsubscribing from Firebase")
- subscriptionTopic?.let { topic -> messenger.unsubscribe(topic) }
- }
- // Subscriber service changes are triggered in the observe() call above
- }
- } else {
- super.onActivityResult(requestCode, resultCode, data)
- }
+ startActivity(intent)
}
private fun handleActionModeClick(subscription: Subscription) {
@@ -492,7 +472,6 @@ class MainActivity : AppCompatActivity(), ActionMode.Callback, AddFragment.Subsc
const val EXTRA_SUBSCRIPTION_TOPIC = "subscriptionTopic"
const val EXTRA_SUBSCRIPTION_INSTANT = "subscriptionInstant"
const val EXTRA_SUBSCRIPTION_MUTED_UNTIL = "subscriptionMutedUntil"
- const val REQUEST_CODE_DELETE_SUBSCRIPTION = 1
const val ANIMATION_DURATION = 80L
}
}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index c42eefa..565662b 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -2,6 +2,8 @@
Ntfy
https://ntfy.sh
+ https
+ ntfy.sh
Notifications
@@ -72,6 +74,7 @@
Subscribe
+ Subscribed to topic %1$s
You haven\'t received any notifications for this topic yet.
To send notifications to this topic, simply PUT or POST to the topic URL.
$ curl -d \"Hi\" %1$s ]]>