Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4efdce54 authored by Philipp Heckel's avatar Philipp Heckel
Browse files

Works and is not super ugly

parent 73f610af
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -34,8 +34,8 @@ data class SubscriptionWithMetadata(
    val topic: String,
    val instant: Boolean,
    val mutedUntil: Long,
    val upAppId: String,
    val upConnectorToken: String,
    val upAppId: String?,
    val upConnectorToken: String?,
    val totalCount: Int,
    val newCount: Int,
    val lastActive: Long
+13 −3
Original line number Diff line number Diff line
@@ -283,8 +283,8 @@ class MainActivity : AppCompatActivity(), ActionMode.Callback, AddFragment.Subsc
            topic = topic,
            instant = instant,
            mutedUntil = 0,
            upAppId = "",
            upConnectorToken = "",
            upAppId = null,
            upConnectorToken = null,
            totalCount = 0,
            newCount = 0,
            lastActive = Date().time/1000
@@ -314,11 +314,21 @@ class MainActivity : AppCompatActivity(), ActionMode.Callback, AddFragment.Subsc
    private fun onSubscriptionItemClick(subscription: Subscription) {
        if (actionMode != null) {
            handleActionModeClick(subscription)
        } else if (subscription.upAppId != null) { // Not UnifiedPush
            displayUnifiedPushToast(subscription)
        } else {
            startDetailView(subscription)
        }
    }

    private fun displayUnifiedPushToast(subscription: Subscription) {
        runOnUiThread {
            val appId = subscription.upAppId ?: ""
            val toastMessage = getString(R.string.main_unified_push_toast, appId)
            Toast.makeText(this@MainActivity, toastMessage, Toast.LENGTH_LONG).show()
        }
    }

    private fun onSubscriptionItemLongClick(subscription: Subscription) {
        if (actionMode == null) {
            beginActionMode(subscription)
@@ -415,7 +425,7 @@ class MainActivity : AppCompatActivity(), ActionMode.Callback, AddFragment.Subsc
        val dialog = builder
            .setMessage(R.string.main_action_mode_delete_dialog_message)
            .setPositiveButton(R.string.main_action_mode_delete_dialog_permanently_delete) { _, _ ->
                adapter.selected.map { viewModel.remove(it) }
                adapter.selected.map { subscriptionId -> viewModel.remove(this, subscriptionId) }
                finishActionMode()
            }
            .setNegativeButton(R.string.main_action_mode_delete_dialog_cancel) { _, _ ->
+6 −4
Original line number Diff line number Diff line
@@ -55,7 +55,9 @@ class MainAdapter(private val onClick: (Subscription) -> Unit, private val onLon

        fun bind(subscription: Subscription) {
            this.subscription = subscription
            var statusMessage = if (subscription.totalCount == 1) {
            var statusMessage = if (subscription.upAppId != null) {
                context.getString(R.string.main_item_status_unified_push, subscription.upAppId)
            } else if (subscription.totalCount == 1) {
                context.getString(R.string.main_item_status_text_one, subscription.totalCount)
            } else {
                context.getString(R.string.main_item_status_text_not_one, subscription.totalCount)
@@ -82,11 +84,11 @@ class MainAdapter(private val onClick: (Subscription) -> Unit, private val onLon
            notificationDisabledUntilImageView.visibility = if (subscription.mutedUntil > 1L) View.VISIBLE else View.GONE
            notificationDisabledForeverImageView.visibility = if (subscription.mutedUntil == 1L) View.VISIBLE else View.GONE
            instantImageView.visibility = if (subscription.instant) View.VISIBLE else View.GONE
            if (subscription.newCount > 0) {
            if (subscription.upAppId != null || subscription.newCount == 0) {
                newItemsView.visibility = View.GONE
            } else {
                newItemsView.visibility = View.VISIBLE
                newItemsView.text = if (subscription.newCount <= 99) subscription.newCount.toString() else "99+"
            } else {
                newItemsView.visibility = View.GONE
            }
            itemView.setOnClickListener { onClick(subscription) }
            itemView.setOnLongClickListener { onLongClick(subscription); true }
+8 −1
Original line number Diff line number Diff line
package io.heckel.ntfy.ui

import android.content.Context
import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import io.heckel.ntfy.data.*
import io.heckel.ntfy.up.Distributor
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlin.collections.List
@@ -22,7 +24,12 @@ class SubscriptionsViewModel(private val repository: Repository) : ViewModel() {
        repository.addSubscription(subscription)
    }

    fun remove(subscriptionId: Long) = viewModelScope.launch(Dispatchers.IO) {
    fun remove(context: Context, subscriptionId: Long) = viewModelScope.launch(Dispatchers.IO) {
        val subscription = repository.getSubscription(subscriptionId) ?: return@launch
        if (subscription.upAppId != null && subscription.upConnectorToken != null) {
            val distributor = Distributor(context)
            distributor.sendUnregistered(subscription.upAppId, subscription.upConnectorToken)
        }
        repository.removeAllNotifications(subscriptionId)
        repository.removeSubscription(subscriptionId)
    }
+85 −56
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@ import android.content.Intent
import android.util.Log
import io.heckel.ntfy.R
import io.heckel.ntfy.app.Application
import io.heckel.ntfy.data.Repository
import io.heckel.ntfy.data.Subscription
import io.heckel.ntfy.ui.SubscriberManager
import io.heckel.ntfy.util.randomString
@@ -17,27 +18,43 @@ import kotlin.random.Random

class BroadcastReceiver : android.content.BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        when (intent!!.action) {
            ACTION_REGISTER -> {
                val appId = intent.getStringExtra(EXTRA_APPLICATION) ?: ""
                val connectorToken = intent.getStringExtra(EXTRA_TOKEN) ?: ""
                Log.d(TAG, "Register: app=$appId, connectorToken=$connectorToken")
                if (appId.isBlank()) {
                    Log.w(TAG, "Trying to register an app without packageName")
        if (context == null || intent == null) {
            return
        }
                val baseUrl = context!!.getString(R.string.app_base_url) // FIXME
                val topic = "up" + randomString(TOPIC_LENGTH)
                val endpoint = topicUrlUp(baseUrl, topic)
                val app = context!!.applicationContext as Application
        when (intent.action) {
            ACTION_REGISTER -> register(context, intent)
            ACTION_UNREGISTER -> unregister(context, intent)
        }
    }

    private fun register(context: Context, intent: Intent) {
        val appId = intent.getStringExtra(EXTRA_APPLICATION) ?: return
        val connectorToken = intent.getStringExtra(EXTRA_TOKEN) ?: return
        val app = context.applicationContext as Application
        val repository = app.repository
        val distributor = Distributor(app)
        Log.d(TAG, "REGISTER received for app $appId (connectorToken=$connectorToken)")
        if (appId.isBlank()) {
            Log.w(TAG, "Refusing registration: empty application")
            distributor.sendRegistrationRefused(appId, connectorToken)
            return
        }
        GlobalScope.launch(Dispatchers.IO) {
            val existingSubscription = repository.getSubscriptionByConnectorToken(connectorToken)
            if (existingSubscription != null) {
                if (existingSubscription.upAppId == appId) {
                    val endpoint = topicUrlUp(existingSubscription.baseUrl, existingSubscription.topic)
                    Log.d(TAG, "Subscription with connectorToken $connectorToken exists. Sending endpoint $endpoint.")
                    distributor.sendEndpoint(appId, connectorToken, endpoint)
                } else {
                    Log.d(TAG, "Subscription with connectorToken $connectorToken exists for a different app. Refusing registration.")
                    distributor.sendRegistrationRefused(appId, connectorToken)
                }
                return@launch
            }
            val baseUrl = context.getString(R.string.app_base_url) // FIXME
            val topic = UP_PREFIX + randomString(TOPIC_LENGTH)
            val endpoint = topicUrlUp(baseUrl, topic)
            val subscription = Subscription(
                id = Random.nextLong(),
                baseUrl = baseUrl,
@@ -50,38 +67,50 @@ class BroadcastReceiver : android.content.BroadcastReceiver() {
                newCount = 0,
                lastActive = Date().time/1000
            )

            // Add subscription
            Log.d(TAG, "Adding subscription with for app $appId (connectorToken $connectorToken): $subscription")
            repository.addSubscription(subscription)
                    val subscriptionIdsWithInstantStatus = repository.getSubscriptionIdsWithInstantStatus()
                    val subscriberManager = SubscriberManager(app)
                    subscriberManager.refreshService(subscriptionIdsWithInstantStatus)
            distributor.sendEndpoint(appId, connectorToken, endpoint)

            // Refresh (and maybe start) foreground service
            refreshSubscriberService(app, repository)
        }
    }
            ACTION_UNREGISTER -> {
                val connectorToken = intent.getStringExtra(EXTRA_TOKEN) ?: ""
                Log.d(TAG, "Unregister: connectorToken=$connectorToken")
                val app = context!!.applicationContext as Application

    private fun unregister(context: Context, intent: Intent) {
        val connectorToken = intent.getStringExtra(EXTRA_TOKEN) ?: return
        val app = context.applicationContext as Application
        val repository = app.repository
        val distributor = Distributor(app)
        Log.d(TAG, "UNREGISTER received (connectorToken=$connectorToken)")
        GlobalScope.launch(Dispatchers.IO) {
            val existingSubscription = repository.getSubscriptionByConnectorToken(connectorToken)
            if (existingSubscription == null) {
                Log.d(TAG, "Subscription with connectorToken $connectorToken does not exist. Ignoring.")
                return@launch
            }

            // Remove subscription
            Log.d(TAG, "Removing subscription ${existingSubscription.id} with connectorToken $connectorToken")
            repository.removeSubscription(existingSubscription.id)
                    val subscriptionIdsWithInstantStatus = repository.getSubscriptionIdsWithInstantStatus()
                    val subscriberManager = SubscriberManager(app)
                    subscriberManager.refreshService(subscriptionIdsWithInstantStatus)
                    existingSubscription.upAppId?.let { appId ->
                        distributor.sendUnregistered(appId, connectorToken)
                    }
                }
            existingSubscription.upAppId?.let { appId -> distributor.sendUnregistered(appId, connectorToken) }

            // Refresh (and maybe stop) foreground service
            refreshSubscriberService(app, repository)
        }
    }

    private fun refreshSubscriberService(context: Context, repository: Repository) {
        Log.d(TAG, "Refreshing subscriber service")
        val subscriptionIdsWithInstantStatus = repository.getSubscriptionIdsWithInstantStatus()
        val subscriberManager = SubscriberManager(context)
        subscriberManager.refreshService(subscriptionIdsWithInstantStatus)
    }

    companion object {
        private const val TAG = "NtfyUpBroadcastRecv"
        private const val UP_PREFIX = "up"
        private const val TOPIC_LENGTH = 16
    }
}
Loading