diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index cac0668..0c46d28 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -46,6 +46,13 @@ android:label="@string/check_service_name" android:permission="android.permission.BIND_JOB_SERVICE"/> + + + + + + + diff --git a/app/src/main/java/com/afollestad/nocknock/App.kt b/app/src/main/java/com/afollestad/nocknock/App.kt index 06e17bd..db2842b 100644 --- a/app/src/main/java/com/afollestad/nocknock/App.kt +++ b/app/src/main/java/com/afollestad/nocknock/App.kt @@ -11,6 +11,7 @@ import android.app.job.JobScheduler import android.content.Context import com.afollestad.nocknock.di.AppComponent import com.afollestad.nocknock.di.DaggerAppComponent +import com.afollestad.nocknock.engine.statuscheck.BootReceiver import com.afollestad.nocknock.engine.statuscheck.CheckStatusJob import com.afollestad.nocknock.ui.AddSiteActivity import com.afollestad.nocknock.ui.MainActivity @@ -51,6 +52,7 @@ class App : Application(), Injector { is ViewSiteActivity -> appComponent.inject(target) is AddSiteActivity -> appComponent.inject(target) is CheckStatusJob -> appComponent.inject(target) + is BootReceiver -> appComponent.inject(target) else -> throw IllegalStateException("Can't inject into $target") } } diff --git a/app/src/main/java/com/afollestad/nocknock/di/AppComponent.kt b/app/src/main/java/com/afollestad/nocknock/di/AppComponent.kt index 1346a67..f6bd78f 100644 --- a/app/src/main/java/com/afollestad/nocknock/di/AppComponent.kt +++ b/app/src/main/java/com/afollestad/nocknock/di/AppComponent.kt @@ -9,6 +9,7 @@ import android.app.Application import android.app.NotificationManager import android.app.job.JobScheduler import com.afollestad.nocknock.engine.EngineModule +import com.afollestad.nocknock.engine.statuscheck.BootReceiver import com.afollestad.nocknock.engine.statuscheck.CheckStatusJob import com.afollestad.nocknock.notifications.NotificationsModule import com.afollestad.nocknock.ui.AddSiteActivity @@ -40,6 +41,8 @@ interface AppComponent { fun inject(job: CheckStatusJob) + fun inject(bootReceiver: BootReceiver) + @Component.Builder interface Builder { diff --git a/app/src/main/java/com/afollestad/nocknock/ui/MainActivity.kt b/app/src/main/java/com/afollestad/nocknock/ui/MainActivity.kt index af68ae7..e292e57 100644 --- a/app/src/main/java/com/afollestad/nocknock/ui/MainActivity.kt +++ b/app/src/main/java/com/afollestad/nocknock/ui/MainActivity.kt @@ -112,7 +112,17 @@ class MainActivity : AppCompatActivity(), View.OnClickListener { list.addItemDecoration(DividerItemDecoration(this, VERTICAL)) fab.setOnClickListener(this) + notificationManager.createChannels() + ensureCheckJobs() + } + + private fun ensureCheckJobs() { + rootView.scopeWhileAttached(IO) { + launch(coroutineContext) { + checkStatusManager.ensureScheduledChecks() + } + } } override fun onResume() { diff --git a/engine/src/main/java/com/afollestad/nocknock/engine/statuscheck/BootReceiver.kt b/engine/src/main/java/com/afollestad/nocknock/engine/statuscheck/BootReceiver.kt new file mode 100644 index 0000000..29d9150 --- /dev/null +++ b/engine/src/main/java/com/afollestad/nocknock/engine/statuscheck/BootReceiver.kt @@ -0,0 +1,54 @@ +/* + * Licensed under Apache-2.0 + * + * Designed and developed by Aidan Follestad (@afollestad) + */ +package com.afollestad.nocknock.engine.statuscheck + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.Intent.ACTION_BOOT_COMPLETED +import android.util.Log +import com.afollestad.nocknock.engine.BuildConfig +import com.afollestad.nocknock.utilities.ext.injector +import kotlinx.coroutines.Dispatchers.IO +import kotlinx.coroutines.Dispatchers.Main +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.async +import kotlinx.coroutines.launch +import javax.inject.Inject + +/** @author Aidan Follestad (afollestad) */ +class BootReceiver : BroadcastReceiver() { + + companion object { + private fun log(message: String) { + if (BuildConfig.DEBUG) { + Log.d("BootReceiver", message) + } + } + } + + @Inject lateinit var checkStatusManager: CheckStatusManager + + override fun onReceive( + context: Context, + intent: Intent + ) { + require(ACTION_BOOT_COMPLETED == intent.action) { + "BootReceiver should only receive ACTION_BOOT_COMPLETED intents." + } + + log("Received boot event! Let's go.") + context.injector() + .injectInto(this) + + val pendingResult = goAsync() + GlobalScope.launch(Main) { + async(IO) { checkStatusManager.ensureScheduledChecks() }.await() + pendingResult.resultCode = 0 + pendingResult.finish() + } + } +} diff --git a/engine/src/main/java/com/afollestad/nocknock/engine/statuscheck/CheckStatusManager.kt b/engine/src/main/java/com/afollestad/nocknock/engine/statuscheck/CheckStatusManager.kt index 1411ebe..616718b 100644 --- a/engine/src/main/java/com/afollestad/nocknock/engine/statuscheck/CheckStatusManager.kt +++ b/engine/src/main/java/com/afollestad/nocknock/engine/statuscheck/CheckStatusManager.kt @@ -15,6 +15,7 @@ import com.afollestad.nocknock.data.ServerModel import com.afollestad.nocknock.data.ServerStatus.ERROR import com.afollestad.nocknock.data.ServerStatus.OK import com.afollestad.nocknock.engine.R +import com.afollestad.nocknock.engine.db.ServerModelStore import com.afollestad.nocknock.engine.statuscheck.CheckStatusJob.Companion.KEY_SITE_ID import com.afollestad.nocknock.utilities.BuildConfig import com.afollestad.nocknock.utilities.providers.StringProvider @@ -33,6 +34,8 @@ data class CheckResult( /** @author Aidan Follestad (afollestad) */ interface CheckStatusManager { + suspend fun ensureScheduledChecks() + fun scheduleCheck( site: ServerModel, rightNow: Boolean = false @@ -47,7 +50,8 @@ class RealCheckStatusManager @Inject constructor( private val app: Application, private val jobScheduler: JobScheduler, private val okHttpClient: OkHttpClient, - private val stringProvider: StringProvider + private val stringProvider: StringProvider, + private val siteStore: ServerModelStore ) : CheckStatusManager { companion object { @@ -59,6 +63,25 @@ class RealCheckStatusManager @Inject constructor( } } + override suspend fun ensureScheduledChecks() { + val sites = siteStore.get() + if (sites.isEmpty()) { + return + } + log("Ensuring sites have scheduled checks.") + + sites.forEach { site -> + val existingJob = jobScheduler.allPendingJobs + .firstOrNull { job -> job.id == site.id } + if (existingJob == null) { + log("Site ${site.id} does NOT have a scheduled job, running one now.") + scheduleCheck(site, rightNow = true) + } else { + log("Site ${site.id} already has a scheduled job. Nothing to do.") + } + } + } + override fun scheduleCheck( site: ServerModel, rightNow: Boolean diff --git a/utilities/src/main/java/com/afollestad/nocknock/utilities/ext/ContextExt.kt b/utilities/src/main/java/com/afollestad/nocknock/utilities/ext/InjectorExt.kt similarity index 100% rename from utilities/src/main/java/com/afollestad/nocknock/utilities/ext/ContextExt.kt rename to utilities/src/main/java/com/afollestad/nocknock/utilities/ext/InjectorExt.kt