👻 Clean up services
This commit is contained in:
		@@ -118,10 +118,6 @@
 | 
			
		||||
            </intent-filter>
 | 
			
		||||
        </activity>
 | 
			
		||||
 | 
			
		||||
        <service
 | 
			
		||||
            android:name=".service.BitmessageService"
 | 
			
		||||
            android:description="@string/bitmessage_service_description"
 | 
			
		||||
            android:exported="false" />
 | 
			
		||||
        <service
 | 
			
		||||
            android:name=".service.ProofOfWorkService"
 | 
			
		||||
            android:exported="false" />
 | 
			
		||||
@@ -154,6 +150,11 @@
 | 
			
		||||
            android:exported="true"
 | 
			
		||||
            android:permission="android.permission.BIND_JOB_SERVICE" />
 | 
			
		||||
 | 
			
		||||
        <service
 | 
			
		||||
            android:name=".service.CleanupService"
 | 
			
		||||
            android:exported="true"
 | 
			
		||||
            android:permission="android.permission.BIND_JOB_SERVICE" />
 | 
			
		||||
 | 
			
		||||
        <service
 | 
			
		||||
            android:name=".service.BatchProcessorService"
 | 
			
		||||
            android:exported="false" />
 | 
			
		||||
 
 | 
			
		||||
@@ -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()
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -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")
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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
 | 
			
		||||
}
 | 
			
		||||
@@ -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")
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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 {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user