From 3e83f3d07d2f3515d2f3d14e6824b3a6ba6a4df8 Mon Sep 17 00:00:00 2001 From: S1m Date: Tue, 16 Jan 2024 23:06:42 +0100 Subject: [PATCH] Add notification when server app is not installed And fix showSingle notifications: the shown ref was shared --- .../distributor/nextpush/api/Api.kt | 165 +++++++++--------- .../nextpush/utils/Notifications.kt | 51 +++++- app/src/main/res/values/strings.xml | 1 + 3 files changed, 129 insertions(+), 88 deletions(-) diff --git a/app/src/main/java/org/unifiedpush/distributor/nextpush/api/Api.kt b/app/src/main/java/org/unifiedpush/distributor/nextpush/api/Api.kt index 8ff33d8..7e0cf3f 100644 --- a/app/src/main/java/org/unifiedpush/distributor/nextpush/api/Api.kt +++ b/app/src/main/java/org/unifiedpush/distributor/nextpush/api/Api.kt @@ -17,6 +17,7 @@ import org.unifiedpush.distributor.nextpush.account.AccountType import org.unifiedpush.distributor.nextpush.api.provider.* // ktlint-disable no-wildcard-imports import org.unifiedpush.distributor.nextpush.api.provider.ApiProvider.Companion.mApiEndpoint import org.unifiedpush.distributor.nextpush.api.response.ApiResponse +import org.unifiedpush.distributor.nextpush.utils.NoServerAppNotification import java.util.concurrent.TimeUnit import java.util.concurrent.atomic.AtomicReference @@ -35,14 +36,9 @@ class Api(context: Context) { }.getProviderAndExecute(block) } - fun apiDestroy() { - Log.d(TAG, "Destroying API") - syncSource.getAndSet(null)?.cancel() - } - - fun apiSync() { + private fun tryWithDeviceId(block: (String) -> Unit) { context.npDeviceId?.let { - syncDevice(it) + block(it) } ?: run { Log.d(TAG, "No deviceId found.") @@ -61,6 +57,7 @@ class Api(context: Context) { } override fun onError(e: Throwable) { + NoServerAppNotification(context).showBig() e.printStackTrace() then() } @@ -68,7 +65,7 @@ class Api(context: Context) { override fun onComplete() { // Sync once it is registered context.npDeviceId?.let { - syncDevice(it) + block(it) } Log.d(TAG, "mApi register: onComplete") then() @@ -81,57 +78,65 @@ class Api(context: Context) { } } - private fun syncDevice(deviceId: String) { - val client = OkHttpClient.Builder() - .readTimeout(0, TimeUnit.SECONDS) - .retryOnConnectionFailure(false) - .build() - val url = "$baseUrl${mApiEndpoint}device/$deviceId" + fun apiDestroy() { + Log.d(TAG, "Destroying API") + syncSource.getAndSet(null)?.cancel() + } - val request = Request.Builder().url(url) - .get() - .build() + fun apiSync() { + tryWithDeviceId { deviceId -> + val client = OkHttpClient.Builder() + .readTimeout(0, TimeUnit.SECONDS) + .retryOnConnectionFailure(false) + .build() + val url = "$baseUrl${mApiEndpoint}device/$deviceId" - syncSource.set( - EventSources.createFactory(client).newEventSource( - request, - SSEListener(context) + val request = Request.Builder().url(url) + .get() + .build() + + syncSource.set( + EventSources.createFactory(client).newEventSource( + request, + SSEListener(context) + ) ) - ) - Log.d(TAG, "cSync done.") + Log.d(TAG, "cSync done.") + } } fun apiDeleteDevice(block: () -> Unit = {}) { - val deviceId = context.npDeviceId ?: return - try { - withApiProvider { apiProvider, then -> - apiProvider.deleteDevice(deviceId) - ?.subscribeOn(Schedulers.io()) - ?.observeOn(AndroidSchedulers.mainThread()) - ?.subscribe(object : DisposableObserver() { - override fun onNext(response: ApiResponse) { - if (response.success) { - Log.d(TAG, "Device successfully deleted.") - } else { - Log.d(TAG, "An error occurred while deleting the device.") + tryWithDeviceId { deviceId -> + try { + withApiProvider { apiProvider, then -> + apiProvider.deleteDevice(deviceId) + ?.subscribeOn(Schedulers.io()) + ?.observeOn(AndroidSchedulers.mainThread()) + ?.subscribe(object : DisposableObserver() { + override fun onNext(response: ApiResponse) { + if (response.success) { + Log.d(TAG, "Device successfully deleted.") + } else { + Log.d(TAG, "An error occurred while deleting the device.") + } } - } - override fun onError(e: Throwable) { - e.printStackTrace() - block() - then() - } + override fun onError(e: Throwable) { + e.printStackTrace() + block() + then() + } - override fun onComplete() { - block() - then() - } - }) - context.npDeviceId = null + override fun onComplete() { + block() + then() + } + }) + context.npDeviceId = null + } + } catch (e: NoProviderException) { + e.printStackTrace() } - } catch (e: NoProviderException) { - e.printStackTrace() } } @@ -139,43 +144,43 @@ class Api(context: Context) { appName: String, block: (String?) -> Unit ) { - // The unity of connector token must already be checked here - val parameters = context.npDeviceId?.let { - mutableMapOf( - "deviceId" to it, + tryWithDeviceId { deviceId -> + // The unity of connector token must already be checked here + val parameters = mutableMapOf( + "deviceId" to deviceId, "appName" to appName ) - } ?: return - try { - withApiProvider { apiProvider, then -> - apiProvider.createApp(parameters) - ?.subscribeOn(Schedulers.io()) - ?.observeOn(AndroidSchedulers.mainThread()) - ?.subscribe(object : DisposableObserver() { - override fun onNext(response: ApiResponse) { - val nextpushToken = if (response.success) { - Log.d(TAG, "App successfully created.") - response.token - } else { - Log.d(TAG, "An error occurred while creating the application.") - null + try { + withApiProvider { apiProvider, then -> + apiProvider.createApp(parameters) + ?.subscribeOn(Schedulers.io()) + ?.observeOn(AndroidSchedulers.mainThread()) + ?.subscribe(object : DisposableObserver() { + override fun onNext(response: ApiResponse) { + val nextpushToken = if (response.success) { + Log.d(TAG, "App successfully created.") + response.token + } else { + Log.d(TAG, "An error occurred while creating the application.") + null + } + block(nextpushToken) } - block(nextpushToken) - } - override fun onError(e: Throwable) { - e.printStackTrace() - block(null) - then() - } + override fun onError(e: Throwable) { + e.printStackTrace() + block(null) + then() + } - override fun onComplete() { - then() - } - }) + override fun onComplete() { + then() + } + }) + } + } catch (e: NoProviderException) { + e.printStackTrace() } - } catch (e: NoProviderException) { - e.printStackTrace() } } diff --git a/app/src/main/java/org/unifiedpush/distributor/nextpush/utils/Notifications.kt b/app/src/main/java/org/unifiedpush/distributor/nextpush/utils/Notifications.kt index 685d3be..232b031 100644 --- a/app/src/main/java/org/unifiedpush/distributor/nextpush/utils/Notifications.kt +++ b/app/src/main/java/org/unifiedpush/distributor/nextpush/utils/Notifications.kt @@ -8,6 +8,7 @@ import android.app.PendingIntent import android.content.Context import android.content.Intent import android.content.pm.PackageManager +import android.net.Uri import android.os.Build import androidx.core.app.ActivityCompat import androidx.core.app.NotificationManagerCompat @@ -40,11 +41,12 @@ data class NotificationData( open class AppNotification( private val context: Context, + private val shown: AtomicBoolean, private val notificationId: Int, private val notificationData: NotificationData, - private val channelData: ChannelData + private val channelData: ChannelData, ) { - private fun createNotificationChannel() { + internal fun createNotificationChannel() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager @@ -60,7 +62,7 @@ open class AppNotification( } } - private fun createNotification( + internal fun createNotification( intent: PendingIntent?, bigText: Boolean = false ): Notification { @@ -124,7 +126,7 @@ open class AppNotification( } } - fun create(bigText: Boolean = false): Notification { + open fun create(bigText: Boolean = false): Notification { createNotificationChannel() return createNotification( @@ -154,10 +156,6 @@ open class AppNotification( deleteNotification(notificationId) } } - - companion object { - private val shown = AtomicBoolean(false) - } } private val Context.warningChannelData: ChannelData @@ -170,6 +168,7 @@ private val Context.warningChannelData: ChannelData class DisconnectedNotification(context: Context) : AppNotification( context, + Notifications.disconnectedShown, NOTIFICATION_ID_WARNING, NotificationData( context.getString(R.string.app_name), @@ -183,6 +182,7 @@ class DisconnectedNotification(context: Context) : AppNotification( class NoPingNotification(context: Context) : AppNotification( context, + Notifications.noPingShown, NOTIFICATION_ID_NO_PING, NotificationData( context.getString(R.string.app_name), @@ -196,6 +196,7 @@ class NoPingNotification(context: Context) : AppNotification( class NoStartNotification(context: Context) : AppNotification( context, + Notifications.noStartShown, NOTIFICATION_ID_NO_START, NotificationData( context.getString(R.string.app_name), @@ -207,8 +208,34 @@ class NoStartNotification(context: Context) : AppNotification( context.warningChannelData ) +class NoServerAppNotification(val context: Context) : AppNotification( + context, + Notifications.noServerShown, + NOTIFICATION_ID_NO_START, + NotificationData( + context.getString(R.string.app_name), + context.getString(R.string.no_server_app_notif_content), + context.getString(R.string.warning_notif_ticker), + Notification.PRIORITY_HIGH, + false + ), + context.warningChannelData +) { + override fun create(bigText: Boolean): Notification{ + createNotificationChannel() + + val notificationIntent = Intent(Intent.ACTION_VIEW) + notificationIntent.setData(Uri.parse("https://apps.nextcloud.com/apps/uppush")) + return createNotification( + PendingIntent.getActivity(context, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE), + bigText + ) + } +} + class LowKeepAliveNotification(context: Context, keepalive: Int) : AppNotification( context, + Notifications.lowKeepAliveShown, NOTIFICATION_ID_LOW_KEEPALIVE, NotificationData( context.getString(R.string.app_name), @@ -222,6 +249,7 @@ class LowKeepAliveNotification(context: Context, keepalive: Int) : AppNotificati class ForegroundNotification(context: Context) : AppNotification( context, + Notifications.ignoreShown, NOTIFICATION_ID_FOREGROUND, NotificationData( context.getString(R.string.app_name), @@ -245,6 +273,7 @@ class ForegroundNotification(context: Context) : AppNotification( class FromPushNotification(context: Context, title: String, content: String) : AppNotification( context, + Notifications.ignoreShown, Notifications.nextNotificationId.getAndIncrement(), NotificationData( title, @@ -263,4 +292,10 @@ class FromPushNotification(context: Context, title: String, content: String) : A private object Notifications { val nextNotificationId = AtomicInteger(50000) + val disconnectedShown = AtomicBoolean(false) + val noPingShown = AtomicBoolean(false) + val noStartShown = AtomicBoolean(false) + val noServerShown = AtomicBoolean(false) + val lowKeepAliveShown = AtomicBoolean(false) + val ignoreShown = AtomicBoolean(true) } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8d1c8e6..7ef22a4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -20,6 +20,7 @@ NextPush is disconnected Warn when NextPush is disconnected or an issue occurred. Warning + Couldn\'t create a device.\n\nYou have probably not installed the UnifiedPush Provider application on your nextcloud server. The service could not be started correctly. Check the configuration of your server. You may be using a reverse proxy with buffering enabled. The server app is configured with a low keepalive: %ss. It will drain your battery. We recommend using a higher keepalive. NextPush was disconnected 5 times before receiving the ping. You probably have a problem with your server configuration. Your reverse proxy timeout is probably too low.