This commit is contained in:
Philipp Heckel 2021-11-26 15:51:05 -05:00
parent 7ed8287abe
commit 3556ffda8f
5 changed files with 90 additions and 33 deletions

View file

@ -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 -->

View file

@ -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,

View file

@ -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()

View file

@ -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
}
}

View file

@ -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>