diff --git a/app/src/main/java/org/unifiedpush/distributor/nextpush/account/Account.kt b/app/src/main/java/org/unifiedpush/distributor/nextpush/account/Account.kt index 26c4a49..e84064f 100644 --- a/app/src/main/java/org/unifiedpush/distributor/nextpush/account/Account.kt +++ b/app/src/main/java/org/unifiedpush/distributor/nextpush/account/Account.kt @@ -9,6 +9,7 @@ import org.unifiedpush.distributor.nextpush.utils.TAG internal const val PREF_NAME = "NextPush" private const val PREF_DEVICE_ID = "deviceId" private const val PREF_ACCOUNT_TYPE = "account::type" +private const val PREF_HAS_STARTED_ONCE = "account::has_started_once" enum class AccountType { SSO, @@ -47,6 +48,12 @@ object Account { }.apply() } + var Context.hasStartedOnce: Boolean + get() = this.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) + .getBoolean(PREF_HAS_STARTED_ONCE, false) + set(value) = this.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) + .edit().putBoolean(PREF_HAS_STARTED_ONCE, value).apply() + fun getAccount(context: Context, uninitialized: Boolean = false): AccountFactory? { return account ?: run { @@ -79,6 +86,12 @@ object Account { } } + fun logout(context: Context) { + getAccount(context)?.logout(context) + context.deviceId = null + context.hasStartedOnce = false + } + fun Context.setTypeSSO() { account = null accountType = AccountType.SSO diff --git a/app/src/main/java/org/unifiedpush/distributor/nextpush/activities/MainActivity.kt b/app/src/main/java/org/unifiedpush/distributor/nextpush/activities/MainActivity.kt index 16bd025..ef023b1 100644 --- a/app/src/main/java/org/unifiedpush/distributor/nextpush/activities/MainActivity.kt +++ b/app/src/main/java/org/unifiedpush/distributor/nextpush/activities/MainActivity.kt @@ -10,6 +10,7 @@ import android.view.View import android.widget.* // ktlint-disable no-wildcard-imports import androidx.appcompat.app.AppCompatActivity import org.unifiedpush.distributor.nextpush.R +import org.unifiedpush.distributor.nextpush.account.Account import org.unifiedpush.distributor.nextpush.account.Account.getAccount import org.unifiedpush.distributor.nextpush.account.Account.isConnected import org.unifiedpush.distributor.nextpush.activities.PermissionsRequest.requestAppPermissions @@ -92,7 +93,7 @@ class MainActivity : AppCompatActivity() { StartService.stopService() FailureHandler.clearFails() } - getAccount(this)?.logout(this) + Account.logout(this) finish() goToStartActivity(this) } diff --git a/app/src/main/java/org/unifiedpush/distributor/nextpush/api/SSEListener.kt b/app/src/main/java/org/unifiedpush/distributor/nextpush/api/SSEListener.kt index c88e066..6a5bf94 100644 --- a/app/src/main/java/org/unifiedpush/distributor/nextpush/api/SSEListener.kt +++ b/app/src/main/java/org/unifiedpush/distributor/nextpush/api/SSEListener.kt @@ -7,12 +7,14 @@ import com.google.gson.Gson import okhttp3.Response import okhttp3.sse.EventSource import okhttp3.sse.EventSourceListener +import org.unifiedpush.distributor.nextpush.account.Account.hasStartedOnce import org.unifiedpush.distributor.nextpush.api.response.SSEResponse import org.unifiedpush.distributor.nextpush.distributor.Distributor.deleteAppFromSSE import org.unifiedpush.distributor.nextpush.distributor.Distributor.sendMessage import org.unifiedpush.distributor.nextpush.services.FailureHandler import org.unifiedpush.distributor.nextpush.services.RestartWorker import org.unifiedpush.distributor.nextpush.services.StartService +import org.unifiedpush.distributor.nextpush.utils.NotificationUtils.createStartErrorNotification import org.unifiedpush.distributor.nextpush.utils.TAG import java.lang.Exception import java.util.Calendar @@ -39,6 +41,7 @@ class SSEListener(val context: Context) : EventSourceListener() { lastEventDate = Calendar.getInstance() when (type) { + "start" -> context.hasStartedOnce = true "keepalive" -> { val message = Gson().fromJson(data, SSEResponse::class.java) keepalive = message.keepalive @@ -65,21 +68,17 @@ class SSEListener(val context: Context) : EventSourceListener() { } override fun onClosed(eventSource: EventSource) { - eventSource.cancel() - if (!StartService.isServiceStarted) { - return - } Log.d(TAG, "onClosed: $eventSource") + eventSource.cancel() + if (!shouldRestart()) return FailureHandler.newFail(context, eventSource) RestartWorker.run(context, delay = 0) } override fun onFailure(eventSource: EventSource, t: Throwable?, response: Response?) { - eventSource.cancel() - if (!StartService.isServiceStarted) { - return - } Log.d(TAG, "onFailure") + eventSource.cancel() + if (!shouldRestart()) return t?.let { Log.d(TAG, "An error occurred: $t") } @@ -100,6 +99,21 @@ class SSEListener(val context: Context) : EventSourceListener() { RestartWorker.run(context, delay = delay) } + private fun shouldRestart(): Boolean { + if (!StartService.isServiceStarted) { + Log.d(TAG, "StartService not started") + return false + } + if (!context.hasStartedOnce) { + Log.d(TAG, "SSE event 'start' never received") + Log.d(TAG, "Stopping service") + StartService.stopService() + createStartErrorNotification(context) + return false + } + return true + } + companion object { var lastEventDate: Calendar? = null var keepalive = 900 diff --git a/app/src/main/java/org/unifiedpush/distributor/nextpush/services/RestartWorker.kt b/app/src/main/java/org/unifiedpush/distributor/nextpush/services/RestartWorker.kt index 8975e8f..f806dc4 100644 --- a/app/src/main/java/org/unifiedpush/distributor/nextpush/services/RestartWorker.kt +++ b/app/src/main/java/org/unifiedpush/distributor/nextpush/services/RestartWorker.kt @@ -58,5 +58,9 @@ class RestartWorker(ctx: Context, params: WorkerParameters) : Worker(ctx, params work.build() ) } + + fun stopPeriodic(context: Context) { + WorkManager.getInstance(context).cancelAllWorkByTag(UNIQUE_PERIODIC_WORK_TAG) + } } } diff --git a/app/src/main/java/org/unifiedpush/distributor/nextpush/services/StartService.kt b/app/src/main/java/org/unifiedpush/distributor/nextpush/services/StartService.kt index 9b429c8..d059c99 100644 --- a/app/src/main/java/org/unifiedpush/distributor/nextpush/services/StartService.kt +++ b/app/src/main/java/org/unifiedpush/distributor/nextpush/services/StartService.kt @@ -52,6 +52,7 @@ class StartService : Service() { RestartWorker.run(this, delay = 0) } else { networkCallback.unregister() + RestartWorker.stopPeriodic(this) } } diff --git a/app/src/main/java/org/unifiedpush/distributor/nextpush/utils/NotificationUtils.kt b/app/src/main/java/org/unifiedpush/distributor/nextpush/utils/NotificationUtils.kt index 76b6b42..d1311a5 100644 --- a/app/src/main/java/org/unifiedpush/distributor/nextpush/utils/NotificationUtils.kt +++ b/app/src/main/java/org/unifiedpush/distributor/nextpush/utils/NotificationUtils.kt @@ -130,6 +130,53 @@ object NotificationUtils { warningShown = true } + fun createStartErrorNotification(context: Context) { + val notificationChannelId = "${context.getString(R.string.app_name)}.StartError" + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val notificationManager = + context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + val channel = NotificationChannel( + notificationChannelId, + "Start error", + NotificationManager.IMPORTANCE_HIGH + ).let { + it.description = context.getString(R.string.start_error_notif_description) + it + } + notificationManager.createNotificationChannel(channel) + } + + val builder: Notification.Builder = ( + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + Notification.Builder( + context, + notificationChannelId + ) + } else { + Notification.Builder(context) + } + ).setContentTitle(context.getString(R.string.app_name)) + .setContentText(context.getString(R.string.start_error_notif_content)) + .setStyle( + Notification.BigTextStyle() + .bigText(context.getString(R.string.start_error_notif_content)) + ) + .setSmallIcon(R.drawable.ic_logo) + .setTicker(context.getString(R.string.start_error_notif_ticker)) + .setPriority(Notification.PRIORITY_HIGH) // for under android 26 compatibility + + with(NotificationManagerCompat.from(context)) { + if (ActivityCompat.checkSelfPermission( + context, + Manifest.permission.POST_NOTIFICATIONS + ) == PackageManager.PERMISSION_GRANTED + ) { + notify(NOTIFICATION_ID_WARNING, builder.build()) + } + } + } + fun deleteWarningNotification(context: Context) { val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 956c0bf..6f4818c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -15,6 +15,9 @@ Account You are connected as: %s Restart Service + The service could not be started correctly. Check the configuration of your server. + The service can not start correctly + Error NextPush is disconnected Warning Foreground