diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 0f7bfcd..e8ed1be 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -118,10 +118,6 @@ - @@ -154,6 +150,11 @@ android:exported="true" android:permission="android.permission.BIND_JOB_SERVICE" /> + + diff --git a/app/src/main/java/ch/dissem/apps/abit/notification/NetworkNotification.kt b/app/src/main/java/ch/dissem/apps/abit/notification/NetworkNotification.kt index abc024e..170e787 100644 --- a/app/src/main/java/ch/dissem/apps/abit/notification/NetworkNotification.kt +++ b/app/src/main/java/ch/dissem/apps/abit/notification/NetworkNotification.kt @@ -25,7 +25,7 @@ import android.support.v4.app.NotificationCompat import ch.dissem.apps.abit.MainActivity import ch.dissem.apps.abit.R import ch.dissem.apps.abit.service.BitmessageIntentService -import ch.dissem.apps.abit.service.BitmessageService +import ch.dissem.apps.abit.service.NodeStartupService import java.util.* import kotlin.concurrent.fixedRateTimer @@ -51,9 +51,9 @@ class NetworkNotification(ctx: Context) : AbstractNotification(ctx) { @SuppressLint("StringFormatMatches") private fun update(): Boolean { - val running = BitmessageService.isRunning + val running = NodeStartupService.isRunning builder.setOngoing(running) - val connections = BitmessageService.status.getProperty("network", "connections") + val connections = NodeStartupService.status.getProperty("network", "connections") if (!running) { builder.setSmallIcon(R.drawable.ic_notification_full_node_disconnected) builder.setContentText(ctx.getString(R.string.connection_info_disconnected)) @@ -112,7 +112,6 @@ class NetworkNotification(ctx: Context) : AbstractNotification(ctx) { timer = fixedRateTimer(initialDelay = 10000, period = 10000) { if (!update()) { cancel() - ctx.stopService(Intent(ctx, BitmessageService::class.java)) } super@NetworkNotification.show() } diff --git a/app/src/main/java/ch/dissem/apps/abit/service/BitmessageService.kt b/app/src/main/java/ch/dissem/apps/abit/service/BitmessageService.kt deleted file mode 100644 index 0add64e..0000000 --- a/app/src/main/java/ch/dissem/apps/abit/service/BitmessageService.kt +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2016 Christian Basler - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package ch.dissem.apps.abit.service - -import android.app.Service -import android.content.BroadcastReceiver -import android.content.Context -import android.content.Intent -import android.content.IntentFilter -import android.net.ConnectivityManager -import android.os.Handler -import ch.dissem.apps.abit.notification.NetworkNotification -import ch.dissem.apps.abit.util.preferences -import ch.dissem.bitmessage.BitmessageContext -import ch.dissem.bitmessage.utils.Property -import org.jetbrains.anko.doAsync - -/** - * Define a Service that returns an IBinder for the - * sync adapter class, allowing the sync adapter framework to call - * onPerformSync(). - */ -class BitmessageService : Service() { - - private val bmc: BitmessageContext by lazy { Singleton.getBitmessageContext(this) } - private lateinit var notification: NetworkNotification - - private val connectivityReceiver: BroadcastReceiver = object : BroadcastReceiver() { - override fun onReceive(context: Context, intent: Intent?) { - if (bmc.isRunning() && !preferences.connectionAllowed) { - bmc.shutdown() - } - } - } - - private val cleanupHandler = Handler() - private val cleanupTask: Runnable = object : Runnable { - override fun run() { - bmc.cleanup() - if (isRunning) { - cleanupHandler.postDelayed(this, 24 * 60 * 60 * 1000L) // once a day - } - } - } - - override fun onCreate() { - registerReceiver( - connectivityReceiver, - IntentFilter().apply { - addAction(ConnectivityManager.CONNECTIVITY_ACTION) - addAction(Intent.ACTION_BATTERY_CHANGED) - } - ) - notification = NetworkNotification(this) - running = false - } - - override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { - if (!isRunning) { - running = true - notification.connecting() - if (!bmc.isRunning()) { - bmc.startup() - } - notification.show() - cleanupHandler.postDelayed(cleanupTask, 24 * 60 * 60 * 1000L) - } - return Service.START_STICKY - } - - override fun onDestroy() { - if (bmc.isRunning()) { - bmc.shutdown() - } - running = false - notification.showShutdown() - cleanupHandler.removeCallbacks(cleanupTask) - doAsync { - bmc.cleanup() - } - unregisterReceiver(connectivityReceiver) - stopSelf() - } - - override fun onBind(intent: Intent) = null - - companion object { - @Volatile - private var running = false - - val isRunning: Boolean - get() = running && Singleton.bitmessageContext?.isRunning() == true - - val status: Property - get() = Singleton.bitmessageContext?.status() ?: Property("bitmessage context") - } -} diff --git a/app/src/main/java/ch/dissem/apps/abit/service/CleanupService.kt b/app/src/main/java/ch/dissem/apps/abit/service/CleanupService.kt new file mode 100644 index 0000000..bd2a869 --- /dev/null +++ b/app/src/main/java/ch/dissem/apps/abit/service/CleanupService.kt @@ -0,0 +1,18 @@ +package ch.dissem.apps.abit.service + +import android.app.job.JobParameters +import android.app.job.JobService +import org.jetbrains.anko.doAsync + +class CleanupService : JobService() { + + override fun onStartJob(params: JobParameters?): Boolean { + doAsync { + Singleton.getBitmessageContext(this@CleanupService).cleanup() + jobFinished(params, false) + } + return true + } + + override fun onStopJob(params: JobParameters?) = false +} diff --git a/app/src/main/java/ch/dissem/apps/abit/service/NodeStartupService.kt b/app/src/main/java/ch/dissem/apps/abit/service/NodeStartupService.kt index 97087fb..9a2b8e2 100644 --- a/app/src/main/java/ch/dissem/apps/abit/service/NodeStartupService.kt +++ b/app/src/main/java/ch/dissem/apps/abit/service/NodeStartupService.kt @@ -2,8 +2,17 @@ package ch.dissem.apps.abit.service import android.app.job.JobParameters import android.app.job.JobService +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.net.ConnectivityManager +import ch.dissem.apps.abit.notification.NetworkNotification import ch.dissem.apps.abit.util.network import ch.dissem.apps.abit.util.preferences +import ch.dissem.bitmessage.BitmessageContext +import ch.dissem.bitmessage.utils.Property +import org.jetbrains.anko.doAsync /** * Starts the full node if @@ -13,14 +22,55 @@ import ch.dissem.apps.abit.util.preferences * And stops it when the preconditions for the job (unmetered network) aren't met anymore. */ class NodeStartupService : JobService() { + private val bmc: BitmessageContext by lazy { Singleton.getBitmessageContext(this) } + + private lateinit var notification: NetworkNotification + + private val connectivityReceiver: BroadcastReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent?) { + if (bmc.isRunning() && !preferences.connectionAllowed) { + bmc.shutdown() + } + } + } override fun onStartJob(params: JobParameters?): Boolean { if (preferences.online) { - network.scheduleNodeStart() + registerReceiver( + connectivityReceiver, + IntentFilter().apply { + addAction(ConnectivityManager.CONNECTIVITY_ACTION) + addAction(Intent.ACTION_BATTERY_CHANGED) + } + ) + notification = NetworkNotification(this) + NodeStartupService.running = false + + if (!isRunning) { + running = true + notification.connecting() + if (!bmc.isRunning()) { + bmc.startup() + } + notification.show() + } } return true } + override fun onDestroy() { + if (bmc.isRunning()) { + bmc.shutdown() + } + running = false + notification.showShutdown() + doAsync { + bmc.cleanup() + } + unregisterReceiver(connectivityReceiver) + stopSelf() + } + /** * Don't actually stop the service, otherwise it will be stopped after 1 or 10 minutes * depending on Android version. @@ -30,4 +80,14 @@ class NodeStartupService : JobService() { return false } + companion object { + @Volatile + private var running = false + + val isRunning: Boolean + get() = running && Singleton.bitmessageContext?.isRunning() == true + + val status: Property + get() = Singleton.bitmessageContext?.status() ?: Property("bitmessage context") + } } diff --git a/app/src/main/java/ch/dissem/apps/abit/util/NetworkUtils.kt b/app/src/main/java/ch/dissem/apps/abit/util/NetworkUtils.kt index 9e0c81e..3ee1836 100644 --- a/app/src/main/java/ch/dissem/apps/abit/util/NetworkUtils.kt +++ b/app/src/main/java/ch/dissem/apps/abit/util/NetworkUtils.kt @@ -8,14 +8,17 @@ import android.content.Context import android.content.Intent import android.os.Build import ch.dissem.apps.abit.dialog.FullNodeDialogActivity -import ch.dissem.apps.abit.service.BitmessageService +import ch.dissem.apps.abit.service.CleanupService import ch.dissem.apps.abit.service.NodeStartupService import java.lang.ref.WeakReference +import java.util.concurrent.TimeUnit val Context.network get() = NetworkUtils.getInstance(this) class NetworkUtils internal constructor(private val ctx: Context) { + private val jobScheduler by lazy { ctx.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler } + fun enableNode(ask: Boolean = true) { if (ask && !ctx.preferences.connectionAllowed) { // Ask for connection @@ -31,24 +34,32 @@ class NetworkUtils internal constructor(private val ctx: Context) { } fun disableNode() { - ctx.stopService(Intent(ctx, BitmessageService::class.java)) + jobScheduler.cancelAll() } fun scheduleNodeStart() { - val jobScheduler = ctx.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler - val serviceComponent = ComponentName(ctx, NodeStartupService::class.java) - val builder = JobInfo.Builder(0, serviceComponent) - when { - ctx.preferences.wifiOnly -> - builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED) - Build.VERSION.SDK_INT >= Build.VERSION_CODES.N -> - builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_NOT_ROAMING) - else -> - builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) + JobInfo.Builder(0, ComponentName(ctx, NodeStartupService::class.java)).let { builder -> + when { + ctx.preferences.wifiOnly -> + builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED) + Build.VERSION.SDK_INT >= Build.VERSION_CODES.N -> + builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_NOT_ROAMING) + else -> + builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) + } + builder.setRequiresCharging(ctx.preferences.requireCharging) + builder.setPersisted(true) + + jobScheduler.schedule(builder.build()) + } + + JobInfo.Builder(1, ComponentName(ctx, CleanupService::class.java)).let { builder -> + builder.setPeriodic(TimeUnit.DAYS.toMillis(1)) + builder.setRequiresDeviceIdle(true) + builder.setRequiresCharging(true) + + jobScheduler.schedule(builder.build()) } - builder.setRequiresCharging(ctx.preferences.requireCharging) - builder.setPersisted(true) - jobScheduler.schedule(builder.build()) } companion object {