diff --git a/app/build.gradle b/app/build.gradle index 509991e..f35f93d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -80,4 +80,5 @@ dependencies { implementation("com.squareup.retrofit2:retrofit:2.9.0") implementation("io.reactivex.rxjava2:rxjava:2.2.21") implementation("com.google.code.gson:gson:2.9.0") + implementation("androidx.work:work-runtime-ktx:2.6.0") } 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 4d90e89..e1ab9cb 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 @@ -86,7 +86,7 @@ class MainActivity : AppCompatActivity() { format(getString(R.string.main_account_desc), ssoAccount.name) showLogout = true invalidateOptionsMenu() - startListener(this) + RestartWorker.start(this) } private fun showStart() { @@ -130,15 +130,14 @@ class MainActivity : AppCompatActivity() { Log.d(TAG, "Restarting the Listener") val receiver = object : BroadcastReceiver() { override fun onReceive(p0: Context?, p1: Intent?) { - startListener(this@MainActivity) + RestartWorker.start(this@MainActivity, delay = 0) } } - val intentFilter = IntentFilter(SERVICE_STOPPED_ACTION) + val intentFilter = IntentFilter(StartService.SERVICE_STOPPED_ACTION) registerReceiver(receiver, intentFilter) - isServiceStarted = false + StartService.isServiceStarted = false val serviceIntent = Intent(this, StartService::class.java) this.stopService(serviceIntent) - startListener(this) } private fun logout() { diff --git a/app/src/main/java/org/unifiedpush/distributor/nextpush/receivers/RegisterBroadcastReceiver.kt b/app/src/main/java/org/unifiedpush/distributor/nextpush/receivers/RegisterBroadcastReceiver.kt index ccbf232..609cbec 100644 --- a/app/src/main/java/org/unifiedpush/distributor/nextpush/receivers/RegisterBroadcastReceiver.kt +++ b/app/src/main/java/org/unifiedpush/distributor/nextpush/receivers/RegisterBroadcastReceiver.kt @@ -3,6 +3,7 @@ package org.unifiedpush.distributor.nextpush.receivers import android.content.BroadcastReceiver import android.content.Context import android.content.Intent +import android.os.PowerManager import android.util.Log import org.unifiedpush.distributor.nextpush.account.AccountUtils.isConnected import org.unifiedpush.distributor.nextpush.api.ApiUtils.apiCreateApp @@ -19,7 +20,6 @@ import org.unifiedpush.distributor.nextpush.distributor.DistributorUtils.getDb import org.unifiedpush.distributor.nextpush.distributor.DistributorUtils.sendEndpoint import org.unifiedpush.distributor.nextpush.distributor.DistributorUtils.sendRegistrationFailed import org.unifiedpush.distributor.nextpush.distributor.DistributorUtils.sendUnregistered -import org.unifiedpush.distributor.nextpush.services.wakeLock import java.lang.Exception /** @@ -30,8 +30,16 @@ private const val TAG = "RegisterBroadcastReceiver" class RegisterBroadcastReceiver : BroadcastReceiver() { - override fun onReceive(context: Context?, intent: Intent?) { - wakeLock?.acquire(10000L /*10 secs*/) + companion object { + private const val WAKE_LOCK_TAG = "NextPush:RegisterBroadcastReceiver:lock" + private var wakeLock : PowerManager.WakeLock? = null + } + + override fun onReceive(context: Context, intent: Intent?) { + wakeLock = (context.getSystemService(Context.POWER_SERVICE) as PowerManager).run { + newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKE_LOCK_TAG) + } + wakeLock?.acquire(30000L /*30 secs*/) when (intent!!.action) { ACTION_REGISTER ->{ Log.i(TAG,"REGISTER") diff --git a/app/src/main/java/org/unifiedpush/distributor/nextpush/receivers/StartReceiver.kt b/app/src/main/java/org/unifiedpush/distributor/nextpush/receivers/StartReceiver.kt index 055b934..919471f 100644 --- a/app/src/main/java/org/unifiedpush/distributor/nextpush/receivers/StartReceiver.kt +++ b/app/src/main/java/org/unifiedpush/distributor/nextpush/receivers/StartReceiver.kt @@ -3,20 +3,13 @@ package org.unifiedpush.distributor.nextpush.receivers import android.content.BroadcastReceiver import android.content.Context import android.content.Intent -import android.os.Build -import org.unifiedpush.distributor.nextpush.services.StartService +import org.unifiedpush.distributor.nextpush.services.RestartWorker class StartReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { if (intent.action == Intent.ACTION_BOOT_COMPLETED) { - Intent(context, StartService::class.java).also { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - context.startForegroundService(it) - return - } - context.startService(it) - } + RestartWorker.start(context) } } } 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 new file mode 100644 index 0000000..fc7201d --- /dev/null +++ b/app/src/main/java/org/unifiedpush/distributor/nextpush/services/RestartWorker.kt @@ -0,0 +1,40 @@ +package org.unifiedpush.distributor.nextpush.services + +import android.content.Context +import android.util.Log +import androidx.work.* +import org.unifiedpush.distributor.nextpush.services.SSEListener.Companion.lastEventDate +import java.util.* +import java.util.concurrent.TimeUnit + +private const val UNIQUE_WORK_TAG = "nextpush::RestartWorker::unique" + +class RestartWorker (ctx: Context, params: WorkerParameters) : Worker(ctx, params) { + + companion object { + private const val TAG = "RestartWorker" + fun start(context: Context, delay: Long? = null) { + val work = PeriodicWorkRequestBuilder(20, TimeUnit.MINUTES) + if (delay != null) { + lastEventDate = null + work.setInitialDelay(delay, TimeUnit.SECONDS) + } + val workManager = WorkManager.getInstance(context) + workManager.enqueueUniquePeriodicWork( + UNIQUE_WORK_TAG, + ExistingPeriodicWorkPolicy.REPLACE, + work.build() + ) + } + } + + override fun doWork(): Result { + Log.d(TAG, "Working") + val currentDate = Calendar.getInstance() + val restartDate = lastEventDate?.add(Calendar.MINUTE, 15) + if (restartDate == null || currentDate.after(restartDate)) { + StartService.startListener(applicationContext) + } + return Result.success() + } +} \ No newline at end of file diff --git a/app/src/main/java/org/unifiedpush/distributor/nextpush/services/SSEListener.kt b/app/src/main/java/org/unifiedpush/distributor/nextpush/services/SSEListener.kt index c86e5f1..c4b9f54 100644 --- a/app/src/main/java/org/unifiedpush/distributor/nextpush/services/SSEListener.kt +++ b/app/src/main/java/org/unifiedpush/distributor/nextpush/services/SSEListener.kt @@ -1,8 +1,6 @@ package org.unifiedpush.distributor.nextpush.services import android.content.Context -import android.os.CountDownTimer -import android.os.Looper import android.util.Base64 import okhttp3.sse.EventSourceListener import okhttp3.sse.EventSource @@ -16,15 +14,20 @@ import org.unifiedpush.distributor.nextpush.distributor.DistributorUtils.sendMes import org.unifiedpush.distributor.nextpush.distributor.DistributorUtils.sendUnregistered import org.unifiedpush.distributor.nextpush.services.NotificationUtils.createWarningNotification import org.unifiedpush.distributor.nextpush.services.NotificationUtils.deleteWarningNotification +import java.util.* private const val TAG = "SSEListener" class SSEListener (val context: Context) : EventSourceListener() { + companion object { + var lastEventDate : Calendar? = null + } + override fun onOpen(eventSource: EventSource, response: Response) { deleteWarningNotification(context) - nFails = 0 - wakeLock?.let { + StartService.nFails = 0 + StartService.wakeLock?.let { while (it.isHeld) { it.release() } @@ -38,7 +41,9 @@ class SSEListener (val context: Context) : EventSourceListener() { override fun onEvent(eventSource: EventSource, id: String?, type: String?, data: String) { Log.d(TAG, "New SSE message event=$type message=$data") - wakeLock?.acquire(10000L /*10 secs*/) + StartService.wakeLock?.acquire(10000L /*10 secs*/) + lastEventDate = Calendar.getInstance() + when (type) { "warning" -> Log.d(TAG, "Warning event received.") "ping" -> Log.d(TAG, "SSE ping received.") @@ -61,7 +66,7 @@ class SSEListener (val context: Context) : EventSourceListener() { db.unregisterApp(connectorToken) } } - wakeLock?.let { + StartService.wakeLock?.let { if (it.isHeld) { it.release() } @@ -69,49 +74,36 @@ class SSEListener (val context: Context) : EventSourceListener() { } override fun onClosed(eventSource: EventSource) { - if (!isServiceStarted) + if (!StartService.isServiceStarted) return Log.d(TAG, "onClosed: $eventSource") - nFails += 1 + StartService.nFails += 1 createWarningNotification(context) - startListener(context) + RestartWorker.start(context, delay = 0) } override fun onFailure(eventSource: EventSource, t: Throwable?, response: Response?) { - if (!isServiceStarted) + if (!StartService.isServiceStarted) return Log.d(TAG, "onFailure") - nFails += 1 - if (nFails > 1) - createWarningNotification(context) - val timeStop = when (nFails) { - 1 -> 2000 // 2sec - 2 -> 20000 // 20sec - 3 -> 60000 // 1min - 4 -> 300000 // 5min - 5 -> 600000 // 10min - else -> 1800000 // 30min - }.toLong() - Log.d(TAG, "Retrying in $timeStop ms") - Looper.prepare() - object : CountDownTimer(timeStop, timeStop) { - - override fun onTick(millisUntilFinished: Long) {} - - override fun onFinish() { - if (nFails > 0) - Log.d(TAG, "Trying to restart") - startListener(context) - } - - }.start() - Looper.loop() t?.let { Log.d(TAG, "An error occurred: $t") - return } response?.let { Log.d(TAG, "onFailure: ${it.code}") } + StartService.nFails += 1 + if (StartService.nFails > 1) + createWarningNotification(context) + val delay = when (StartService.nFails) { + 1 -> 2 // 2sec + 2 -> 20 // 20sec + 3 -> 60 // 1min + 4 -> 300 // 5min + 5 -> 600 // 10min + else -> return // else keep the worker with its 20min + }.toLong() + Log.d(TAG, "Retrying in $delay s") + RestartWorker.start(context, delay = delay) } } 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 0457cea..61fa922 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 @@ -26,26 +26,30 @@ import org.unifiedpush.distributor.nextpush.services.NotificationUtils.createFor import java.lang.Exception private const val TAG = "StartService" -const val WAKE_LOCK_TAG = "NextPush:StartService:lock" -const val SERVICE_STOPPED_ACTION = "org.unifiedpush.distributor.nextpush.services.STOPPED" -var isServiceStarted = false -var nFails = 0 -var wakeLock: PowerManager.WakeLock? = null - -fun startListener(context: Context){ - if (isServiceStarted && nFails == 0) return - Log.d(TAG, "Starting the Listener") - val serviceIntent = Intent(context, StartService::class.java) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - context.startForegroundService(serviceIntent) - }else{ - context.startService(serviceIntent) - } -} class StartService: Service(){ - private var isCallbackRegistered = false + companion object { + const val WAKE_LOCK_TAG = "NextPush:StartService:lock" + const val SERVICE_STOPPED_ACTION = "org.unifiedpush.distributor.nextpush.services.STOPPED" + var isServiceStarted = false + var nFails = 0 + var wakeLock: PowerManager.WakeLock? = null + private var isCallbackRegistered = false + + fun startListener(context: Context){ + if (isServiceStarted && nFails == 0) return + Log.d(TAG, "Starting the Listener") + Log.d(TAG, "Service is started: $isServiceStarted") + Log.d(TAG, "nFails: $nFails") + val serviceIntent = Intent(context, StartService::class.java) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + context.startForegroundService(serviceIntent) + }else{ + context.startService(serviceIntent) + } + } + } override fun onBind(intent: Intent?): IBinder? { return null @@ -59,6 +63,7 @@ class StartService: Service(){ } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + Log.d(TAG, "onStartCommand") if (!isCallbackRegistered) { isCallbackRegistered = true registerNetworkCallback() @@ -72,7 +77,7 @@ class StartService: Service(){ Log.d(TAG, "Destroyed") if (isServiceStarted) { apiDestroy() - startListener(this) + RestartWorker.start(this, delay = 0) } else { stopService() } @@ -121,7 +126,9 @@ class StartService: Service(){ private val networkCallback = object : NetworkCallback() { override fun onAvailable(network: Network) { Log.d(TAG, "Network is CONNECTED") - startListener(this@StartService) + if (nFails > 1) { + RestartWorker.start(this@StartService, delay = 0) + } } override fun onCapabilitiesChanged( @@ -129,7 +136,9 @@ class StartService: Service(){ networkCapabilities: NetworkCapabilities ) { Log.d(TAG, "Network Capabilities changed") - startListener(this@StartService) + if (nFails > 1) { + RestartWorker.start(this@StartService, delay = 0) + } // else, it retries in max 2sec } }