diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 8300aad..9fdb969 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -37,10 +37,19 @@ + android:parentActivityName=".ui.MainActivity" + android:exported="true"> + + + + + + + + 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 cad7da5..c7a1690 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,7 @@ 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 @@ -28,6 +29,7 @@ import io.heckel.ntfy.R import io.heckel.ntfy.app.Application import io.heckel.ntfy.db.Notification import io.heckel.ntfy.db.Repository +import io.heckel.ntfy.db.Subscription import io.heckel.ntfy.firebase.FirebaseMessenger import io.heckel.ntfy.util.Log import io.heckel.ntfy.msg.ApiService @@ -70,7 +72,7 @@ class DetailActivity : AppCompatActivity(), ActionMode.Callback, NotificationFra super.onCreate(savedInstanceState) setContentView(R.layout.activity_detail) - Log.d(MainActivity.TAG, "Create $this") + Log.d(TAG, "Create $this") // Dependencies that depend on Context notifier = NotificationService(this) @@ -79,7 +81,75 @@ class DetailActivity : AppCompatActivity(), ActionMode.Callback, NotificationFra // Show 'Back' button supportActionBar?.setDisplayHomeAsUpEnabled(true) - loadView() + // Handle direct deep links to topic "ntfy://..." + val url = intent?.data + if (intent?.action == ACTION_VIEW && url != null) { + maybeSubscribeAndLoadView(url) + } else { + loadView() + } + } + + private fun maybeSubscribeAndLoadView(url: Uri) { + if (url.pathSegments.size != 1) { + Log.w(TAG, "Invalid link $url. Aborting.") + finish() + return + } + val secure = url.getBooleanQueryParameter("secure", true) + val baseUrl = if (secure) "https://${url.host}" else "http://${url.host}" + val topic = url.pathSegments.first() + title = topicShortUrl(baseUrl, topic) + + lifecycleScope.launch(Dispatchers.IO) { + var subscription = repository.getSubscription(baseUrl, topic) + if (subscription == null) { + val instant = baseUrl != appBaseUrl + subscription = Subscription( + id = Random.nextLong(), + baseUrl = baseUrl, + topic = topic, + instant = instant, + mutedUntil = 0, + upAppId = null, + upConnectorToken = null, + 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!) + if (baseUrl == appBaseUrl) { + Log.d(TAG, "Subscribing to Firebase topic $topic") + messenger.subscribe(topic) + } + + // Fetch cached messages + try { + val user = repository.getUser(subscription.baseUrl) // May be null + val notifications = api.poll(subscription.id, subscription.baseUrl, subscription.topic, user) + notifications.forEach { notification -> repository.addNotification(notification) } + } catch (e: Exception) { + Log.e(TAG, "Unable to fetch notifications: ${e.message}", e) + } + + 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() { @@ -487,7 +557,7 @@ class DetailActivity : AppCompatActivity(), ActionMode.Callback, NotificationFra val dialog = builder .setMessage(R.string.detail_delete_dialog_message) .setPositiveButton(R.string.detail_delete_dialog_permanently_delete) { _, _ -> - Log.d(MainActivity.TAG, "Deleting subscription with subscription ID $subscriptionId (topic: $subscriptionTopic)") + Log.d(TAG, "Deleting subscription with subscription ID $subscriptionId (topic: $subscriptionTopic)") GlobalScope.launch(Dispatchers.IO) { repository.removeAllNotifications(subscriptionId) repository.removeSubscription(subscriptionId) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index b93b314..d2b93d7 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -300,4 +300,5 @@ \n%1$s \n \nKennwörter werden immer ersetzt, aber nicht hier aufgelistet. - \ No newline at end of file + Thema %1$s abonniert + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 978f1cf..4236457 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -126,6 +126,7 @@ Copied to clipboard Instant delivery on Instant delivery off + Subscribed to topic %1$s Tags: %1$s Notification deleted Undo