Open ntfy.sh links with the app, closes https://github.com/binwiederhier/ntfy/issues/20
This commit is contained in:
parent
7ed8287abe
commit
3556ffda8f
5 changed files with 90 additions and 33 deletions
|
@ -39,6 +39,16 @@
|
|||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".ui.MainActivity"/>
|
||||
|
||||
<!-- Open https://ntfy.sh links with the app -->
|
||||
<intent-filter android:label="@string/app_name">
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="@string/app_base_scheme"
|
||||
android:host="@string/app_base_host"
|
||||
android:pathPattern="/..*" /> <!-- This is awful, but it's the only way in Android -->
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!-- Subscriber foreground service for hosts other than ntfy.sh -->
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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<DetailViewModel> {
|
||||
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
<!-- Main app-->
|
||||
<string name="app_name">Ntfy</string>
|
||||
<string name="app_base_url">https://ntfy.sh</string> <!-- If changed, you must also change google-services.json! -->
|
||||
<string name="app_base_scheme">https</string> <!-- If changed, you must also change google-services.json! -->
|
||||
<string name="app_base_host">ntfy.sh</string> <!-- If changed, you must also change google-services.json! -->
|
||||
|
||||
<!-- Notification channels -->
|
||||
<string name="channel_notifications_name">Notifications</string>
|
||||
|
@ -72,6 +74,7 @@
|
|||
<string name="add_dialog_button_subscribe">Subscribe</string>
|
||||
|
||||
<!-- Detail activity -->
|
||||
<string name="detail_deep_link_subscribed_toast_message">Subscribed to topic %1$s</string>
|
||||
<string name="detail_no_notifications_text">You haven\'t received any notifications for this topic yet.</string>
|
||||
<string name="detail_how_to_intro">To send notifications to this topic, simply PUT or POST to the topic URL.</string>
|
||||
<string name="detail_how_to_example"><![CDATA[ Example (using curl):<br/><tt>$ curl -d \"Hi\" %1$s</tt> ]]></string>
|
||||
|
|
Loading…
Reference in a new issue