diff --git a/app/src/main/java/ch/dissem/apps/abit/ComposeMessageFragment.kt b/app/src/main/java/ch/dissem/apps/abit/ComposeMessageFragment.kt index ff1b37b..bca4f5f 100644 --- a/app/src/main/java/ch/dissem/apps/abit/ComposeMessageFragment.kt +++ b/app/src/main/java/ch/dissem/apps/abit/ComposeMessageFragment.kt @@ -35,7 +35,7 @@ import ch.dissem.apps.abit.ComposeMessageActivity.Companion.EXTRA_SUBJECT import ch.dissem.apps.abit.adapter.ContactAdapter import ch.dissem.apps.abit.dialog.SelectEncodingDialogFragment import ch.dissem.apps.abit.service.Singleton -import ch.dissem.apps.abit.util.Preferences +import ch.dissem.apps.abit.util.preferences import ch.dissem.bitmessage.entity.BitmessageAddress import ch.dissem.bitmessage.entity.Plaintext import ch.dissem.bitmessage.entity.Plaintext.Type.BROADCAST @@ -94,8 +94,7 @@ class ComposeMessageFragment : Fragment() { if (containsKey(EXTRA_CONTENT)) { content = getString(EXTRA_CONTENT) } - encoding = getSerializable(EXTRA_ENCODING) as? Plaintext.Encoding ?: - Plaintext.Encoding.SIMPLE + encoding = getSerializable(EXTRA_ENCODING) as? Plaintext.Encoding ?: Plaintext.Encoding.SIMPLE if (containsKey(EXTRA_PARENT)) { val parent = getSerializable(EXTRA_PARENT) as Plaintext @@ -221,7 +220,7 @@ class ComposeMessageFragment : Fragment() { } val sender = sender_input.selectedItem as? ch.dissem.bitmessage.entity.BitmessageAddress sender?.let { builder.from(it) } - if (!Preferences.requestAcknowledgements(ctx)) { + if (!ctx.preferences.requestAcknowledgements) { builder.preventAck() } when (encoding) { diff --git a/app/src/main/java/ch/dissem/apps/abit/MainActivity.kt b/app/src/main/java/ch/dissem/apps/abit/MainActivity.kt index 646579e..516e7bb 100644 --- a/app/src/main/java/ch/dissem/apps/abit/MainActivity.kt +++ b/app/src/main/java/ch/dissem/apps/abit/MainActivity.kt @@ -31,10 +31,7 @@ import ch.dissem.apps.abit.listener.ListSelectionListener import ch.dissem.apps.abit.repository.AndroidLabelRepository.Companion.LABEL_ARCHIVE import ch.dissem.apps.abit.service.Singleton import ch.dissem.apps.abit.service.Singleton.currentLabel -import ch.dissem.apps.abit.util.NetworkUtils -import ch.dissem.apps.abit.util.Preferences -import ch.dissem.apps.abit.util.getColor -import ch.dissem.apps.abit.util.getIcon +import ch.dissem.apps.abit.util.* import ch.dissem.bitmessage.BitmessageContext import ch.dissem.bitmessage.entity.BitmessageAddress import ch.dissem.bitmessage.entity.Conversation @@ -253,11 +250,11 @@ class MainActivity : AppCompatActivity(), ListSelectionListener { .withIdentifier(ID_NODE_SWITCH) .withName(R.string.online) .withIcon(CommunityMaterial.Icon.cmd_cloud_outline) - .withChecked(Preferences.isOnline(this)) + .withChecked(preferences.online) .withOnCheckedChangeListener { _, _, isChecked -> - Preferences.setOnline(this, isChecked) + preferences.online = isChecked if (isChecked) { - NetworkUtils.enableNode(this, true) + network.enableNode(true) } } @@ -360,7 +357,7 @@ class MainActivity : AppCompatActivity(), ListSelectionListener { } override fun onResume() { - NetworkUtils.enableNode(this, false) + network.enableNode(false) updateUnread() Singleton.getMessageListener(this).resetNotification() currentLabel.addObserver(this) { label -> diff --git a/app/src/main/java/ch/dissem/apps/abit/SettingsFragment.kt b/app/src/main/java/ch/dissem/apps/abit/SettingsFragment.kt index 8c304dd..717a5cd 100644 --- a/app/src/main/java/ch/dissem/apps/abit/SettingsFragment.kt +++ b/app/src/main/java/ch/dissem/apps/abit/SettingsFragment.kt @@ -17,7 +17,10 @@ package ch.dissem.apps.abit import android.app.Activity -import android.content.* +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.content.ServiceConnection import android.os.Bundle import android.os.IBinder import android.support.v4.app.Fragment @@ -34,8 +37,8 @@ import ch.dissem.apps.abit.service.BatchProcessorService import ch.dissem.apps.abit.service.SimpleJob import ch.dissem.apps.abit.service.Singleton import ch.dissem.apps.abit.util.Exports -import ch.dissem.apps.abit.util.NetworkUtils -import ch.dissem.apps.abit.util.Preferences +import ch.dissem.apps.abit.util.network +import ch.dissem.apps.abit.util.preferences import ch.dissem.bitmessage.entity.Plaintext import com.mikepenz.aboutlibraries.Libs import com.mikepenz.aboutlibraries.LibsBuilder @@ -99,7 +102,7 @@ class SettingsFragment : PreferenceFragmentCompat(), PreferenceFragmentCompat.On val bmc = Singleton.getBitmessageContext(ctx) bmc.internals.nodeRegistry.clear() bmc.cleanup() - Preferences.cleanupExportDirectory(ctx) + ctx.preferences.cleanupExportDirectory() uiThread { Toast.makeText( @@ -118,7 +121,7 @@ class SettingsFragment : PreferenceFragmentCompat(), PreferenceFragmentCompat.On indeterminateProgressDialog(R.string.export_data_summary, R.string.export_data).apply { doAsync { - val exportDirectory = Preferences.getExportDirectory(ctx) + val exportDirectory = ctx.preferences.exportDirectory exportDirectory.mkdirs() val file = Exports.exportData(exportDirectory, ctx) val contentUri = getUriForFile(ctx, "ch.dissem.apps.abit.fileprovider", file) @@ -157,7 +160,7 @@ class SettingsFragment : PreferenceFragmentCompat(), PreferenceFragmentCompat.On override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { val ctx = context ?: throw IllegalStateException("No context available") when (requestCode) { - WRITE_EXPORT_REQUEST_CODE -> Preferences.cleanupExportDirectory(ctx) + WRITE_EXPORT_REQUEST_CODE -> ctx.preferences.cleanupExportDirectory() READ_IMPORT_REQUEST_CODE -> { if (resultCode == Activity.RESULT_OK && data?.data != null) { indeterminateProgressDialog(R.string.import_data_summary, R.string.import_data).apply { @@ -228,7 +231,7 @@ class SettingsFragment : PreferenceFragmentCompat(), PreferenceFragmentCompat.On private fun connectivityChangeListener() = OnPreferenceChangeListener { _, _ -> - context?.let { ctx -> NetworkUtils.scheduleNodeStart(ctx) } + context?.network?.scheduleNodeStart() true } diff --git a/app/src/main/java/ch/dissem/apps/abit/dialog/FullNodeDialogActivity.kt b/app/src/main/java/ch/dissem/apps/abit/dialog/FullNodeDialogActivity.kt index 985d6e4..9f86455 100644 --- a/app/src/main/java/ch/dissem/apps/abit/dialog/FullNodeDialogActivity.kt +++ b/app/src/main/java/ch/dissem/apps/abit/dialog/FullNodeDialogActivity.kt @@ -19,8 +19,8 @@ package ch.dissem.apps.abit.dialog import android.app.Activity import android.os.Bundle import ch.dissem.apps.abit.R -import ch.dissem.apps.abit.util.NetworkUtils -import ch.dissem.apps.abit.util.Preferences +import ch.dissem.apps.abit.util.network +import ch.dissem.apps.abit.util.preferences import kotlinx.android.synthetic.main.dialog_full_node.* /** @@ -31,12 +31,12 @@ class FullNodeDialogActivity : Activity() { super.onCreate(savedInstanceState) setContentView(R.layout.dialog_full_node) ok.setOnClickListener { - Preferences.setWifiOnly(this@FullNodeDialogActivity, false) - NetworkUtils.enableNode(applicationContext) + preferences.wifiOnly = false + network.enableNode() finish() } dismiss.setOnClickListener { - NetworkUtils.scheduleNodeStart(applicationContext) + network.scheduleNodeStart() finish() } } diff --git a/app/src/main/java/ch/dissem/apps/abit/listener/MessageListener.kt b/app/src/main/java/ch/dissem/apps/abit/listener/MessageListener.kt index b4990a2..0ef4775 100644 --- a/app/src/main/java/ch/dissem/apps/abit/listener/MessageListener.kt +++ b/app/src/main/java/ch/dissem/apps/abit/listener/MessageListener.kt @@ -19,7 +19,7 @@ package ch.dissem.apps.abit.listener import android.content.Context import ch.dissem.apps.abit.MainActivity import ch.dissem.apps.abit.notification.NewMessageNotification -import ch.dissem.apps.abit.util.Preferences +import ch.dissem.apps.abit.util.preferences import ch.dissem.bitmessage.BitmessageContext import ch.dissem.bitmessage.entity.Plaintext import ch.dissem.bitmessage.ports.MessageRepository @@ -50,7 +50,7 @@ class MessageListener(ctx: Context) : BitmessageContext.Listener.WithContext { private lateinit var conversationService: ConversationService init { - emulateConversations = Preferences.isEmulateConversations(ctx) + emulateConversations = ctx.preferences.emulateConversations } override fun receive(plaintext: Plaintext) { diff --git a/app/src/main/java/ch/dissem/apps/abit/service/BitmessageIntentService.kt b/app/src/main/java/ch/dissem/apps/abit/service/BitmessageIntentService.kt index 7156ce9..46fcc94 100644 --- a/app/src/main/java/ch/dissem/apps/abit/service/BitmessageIntentService.kt +++ b/app/src/main/java/ch/dissem/apps/abit/service/BitmessageIntentService.kt @@ -18,7 +18,7 @@ package ch.dissem.apps.abit.service import android.app.IntentService import android.content.Intent -import ch.dissem.apps.abit.util.NetworkUtils +import ch.dissem.apps.abit.util.network import ch.dissem.bitmessage.BitmessageContext import ch.dissem.bitmessage.entity.Plaintext @@ -44,10 +44,10 @@ class BitmessageIntentService : IntentService("BitmessageIntentService") { Singleton.getMessageListener(this).resetNotification() } if (it.hasExtra(EXTRA_STARTUP_NODE)) { - NetworkUtils.enableNode(this) + network.enableNode() } if (it.hasExtra(EXTRA_SHUTDOWN_NODE)) { - NetworkUtils.disableNode(this) + network.disableNode() } } } 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 index e3b1345..0add64e 100644 --- a/app/src/main/java/ch/dissem/apps/abit/service/BitmessageService.kt +++ b/app/src/main/java/ch/dissem/apps/abit/service/BitmessageService.kt @@ -23,10 +23,8 @@ import android.content.Intent import android.content.IntentFilter import android.net.ConnectivityManager import android.os.Handler -import ch.dissem.apps.abit.R -import ch.dissem.apps.abit.notification.ErrorNotification import ch.dissem.apps.abit.notification.NetworkNotification -import ch.dissem.apps.abit.util.Preferences +import ch.dissem.apps.abit.util.preferences import ch.dissem.bitmessage.BitmessageContext import ch.dissem.bitmessage.utils.Property import org.jetbrains.anko.doAsync @@ -43,7 +41,7 @@ class BitmessageService : Service() { private val connectivityReceiver: BroadcastReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent?) { - if (bmc.isRunning() && !Preferences.isConnectionAllowed(this@BitmessageService)) { + if (bmc.isRunning() && !preferences.connectionAllowed) { bmc.shutdown() } } 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 3fb1512..97087fb 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,8 @@ package ch.dissem.apps.abit.service import android.app.job.JobParameters import android.app.job.JobService -import ch.dissem.apps.abit.util.NetworkUtils -import ch.dissem.apps.abit.util.Preferences +import ch.dissem.apps.abit.util.network +import ch.dissem.apps.abit.util.preferences /** * Starts the full node if @@ -15,9 +15,8 @@ import ch.dissem.apps.abit.util.Preferences class NodeStartupService : JobService() { override fun onStartJob(params: JobParameters?): Boolean { - val bmc = Singleton.getBitmessageContext(this) - if (Preferences.isOnline(this) && !bmc.isRunning()) { - NetworkUtils.doStartBitmessageService(applicationContext) + if (preferences.online) { + network.scheduleNodeStart() } return true } @@ -26,6 +25,9 @@ class NodeStartupService : JobService() { * Don't actually stop the service, otherwise it will be stopped after 1 or 10 minutes * depending on Android version. */ - override fun onStopJob(params: JobParameters?) = false + override fun onStopJob(params: JobParameters?): Boolean { + network.scheduleNodeStart() + return false + } } diff --git a/app/src/main/java/ch/dissem/apps/abit/service/StartServiceReceiver.kt b/app/src/main/java/ch/dissem/apps/abit/service/StartServiceReceiver.kt index a811386..f5451ef 100644 --- a/app/src/main/java/ch/dissem/apps/abit/service/StartServiceReceiver.kt +++ b/app/src/main/java/ch/dissem/apps/abit/service/StartServiceReceiver.kt @@ -4,16 +4,16 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.Intent.ACTION_BOOT_COMPLETED -import ch.dissem.apps.abit.util.NetworkUtils -import ch.dissem.apps.abit.util.Preferences +import ch.dissem.apps.abit.util.network +import ch.dissem.apps.abit.util.preferences /** * Starts the Bitmessage "full node" service if conditions allow it */ class StartServiceReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent?) { - if (intent?.action == ACTION_BOOT_COMPLETED && Preferences.isOnline(context)) { - NetworkUtils.enableNode(context, false) + if (intent?.action == ACTION_BOOT_COMPLETED && context.preferences.online) { + context.network.enableNode(false) } } } 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 67f4de4..9e0c81e 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 @@ -10,14 +10,14 @@ import android.os.Build import ch.dissem.apps.abit.dialog.FullNodeDialogActivity import ch.dissem.apps.abit.service.BitmessageService import ch.dissem.apps.abit.service.NodeStartupService +import java.lang.ref.WeakReference +val Context.network get() = NetworkUtils.getInstance(this) -object NetworkUtils { +class NetworkUtils internal constructor(private val ctx: Context) { - fun enableNode(ctx: Context, ask: Boolean = true) { - if (Preferences.isConnectionAllowed(ctx) || !ask) { - scheduleNodeStart(ctx) - } else { + fun enableNode(ask: Boolean = true) { + if (ask && !ctx.preferences.connectionAllowed) { // Ask for connection val dialogIntent = Intent(ctx, FullNodeDialogActivity::class.java) if (ctx !is Activity) { @@ -25,34 +25,42 @@ object NetworkUtils { ctx.sendBroadcast(Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)) } ctx.startActivity(dialogIntent) + } else { + scheduleNodeStart() } } - fun doStartBitmessageService(ctx: Context) { - ctx.startService(Intent(ctx, BitmessageService::class.java)) - } - - fun disableNode(ctx: Context) { + fun disableNode() { ctx.stopService(Intent(ctx, BitmessageService::class.java)) } - fun scheduleNodeStart(ctx: Context) { + 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 { - Preferences.isWifiOnly(ctx) -> + 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) } - if (Preferences.requireCharging(ctx)) { - builder.setRequiresCharging(true) - } - builder.setPeriodic(15 * 60 * 1000L) + builder.setRequiresCharging(ctx.preferences.requireCharging) builder.setPersisted(true) jobScheduler.schedule(builder.build()) } + + companion object { + private var instance: WeakReference? = null + + internal fun getInstance(ctx: Context): NetworkUtils { + var networkUtils = instance?.get() + if (networkUtils == null) { + networkUtils = NetworkUtils(ctx.applicationContext) + instance = WeakReference(networkUtils) + } + return networkUtils + } + } } diff --git a/app/src/main/java/ch/dissem/apps/abit/util/Preferences.kt b/app/src/main/java/ch/dissem/apps/abit/util/Preferences.kt index 3067496..558a9ea 100644 --- a/app/src/main/java/ch/dissem/apps/abit/util/Preferences.kt +++ b/app/src/main/java/ch/dissem/apps/abit/util/Preferences.kt @@ -31,47 +31,49 @@ import org.jetbrains.anko.connectivityManager import org.jetbrains.anko.defaultSharedPreferences import org.slf4j.LoggerFactory import java.io.File +import java.lang.ref.WeakReference +val Context.preferences get() = Preferences.getInstance(this) + /** * @author Christian Basler */ -object Preferences { +class Preferences internal constructor(private val ctx: Context) { private val LOG = LoggerFactory.getLogger(Preferences::class.java) - fun isConnectionAllowed(ctx: Context) = isAllowedForWiFi(ctx) && isAllowedForCharging(ctx) + val connectionAllowed get() = isAllowedForWiFi && isAllowedForCharging - private fun isAllowedForWiFi(ctx: Context) = !isWifiOnly(ctx) || !ctx.connectivityManager.isActiveNetworkMetered + private val isAllowedForWiFi get() = !wifiOnly || !ctx.connectivityManager.isActiveNetworkMetered - private fun isAllowedForCharging(ctx: Context) = !requireCharging(ctx) || isCharging(ctx) + private val isAllowedForCharging get() = !requireCharging || isCharging - private fun isCharging(ctx: Context) = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - ctx.batteryManager.isCharging - } else { - val intent = ctx.registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED)) - val status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1) - status == BatteryManager.BATTERY_STATUS_CHARGING || status == BatteryManager.BATTERY_STATUS_FULL - } + private val isCharging + get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + ctx.batteryManager.isCharging + } else { + val intent = ctx.registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED)) + val status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1) + status == BatteryManager.BATTERY_STATUS_CHARGING || status == BatteryManager.BATTERY_STATUS_FULL + } - fun isWifiOnly(ctx: Context) = ctx.defaultSharedPreferences.getBoolean(PREFERENCE_WIFI_ONLY, true) + var wifiOnly + get() = ctx.defaultSharedPreferences.getBoolean(PREFERENCE_WIFI_ONLY, true) + set(value) { + ctx.defaultSharedPreferences.edit() + .putBoolean(PREFERENCE_WIFI_ONLY, value) + .apply() + } - fun setWifiOnly(ctx: Context, status: Boolean) { - ctx.defaultSharedPreferences.edit() - .putBoolean(PREFERENCE_WIFI_ONLY, status) - .apply() - } + val requireCharging get() = ctx.defaultSharedPreferences.getBoolean(PREFERENCE_REQUIRE_CHARGING, true) - fun requireCharging(ctx: Context) = ctx.defaultSharedPreferences.getBoolean(PREFERENCE_REQUIRE_CHARGING, true) + val emulateConversations get() = ctx.defaultSharedPreferences.getBoolean(PREFERENCE_EMULATE_CONVERSATIONS, true) - fun isEmulateConversations(ctx: Context) = - ctx.defaultSharedPreferences.getBoolean(PREFERENCE_EMULATE_CONVERSATIONS, true) + val exportDirectory by lazy { File(ctx.filesDir, "exports") } - fun getExportDirectory(ctx: Context) = File(ctx.filesDir, "exports") + val requestAcknowledgements = ctx.defaultSharedPreferences.getBoolean(PREFERENCE_REQUEST_ACK, true) - fun requestAcknowledgements(ctx: Context) = ctx.defaultSharedPreferences.getBoolean(PREFERENCE_REQUEST_ACK, true) - - fun cleanupExportDirectory(ctx: Context) { - val exportDirectory = getExportDirectory(ctx) + fun cleanupExportDirectory() { if (exportDirectory.exists()) { exportDirectory.listFiles().forEach { file -> try { @@ -85,17 +87,29 @@ object Preferences { } } - fun isOnline(ctx: Context) = ctx.defaultSharedPreferences.getBoolean(PREFERENCE_ONLINE, true) + var online + get() = ctx.defaultSharedPreferences.getBoolean(PREFERENCE_ONLINE, true) + set(value) { + ctx.defaultSharedPreferences.edit() + .putBoolean(PREFERENCE_ONLINE, value) + .apply() + if (value) { + ctx.network.enableNode(true) + } else { + ctx.network.disableNode() + } + } - fun setOnline(ctx: Context, status: Boolean) { - ctx.defaultSharedPreferences.edit() - .putBoolean(PREFERENCE_ONLINE, status) - .apply() - if (status) { - NetworkUtils.enableNode(ctx, true) - } else { - NetworkUtils.disableNode(ctx) + companion object { + private var instance: WeakReference? = null + + internal fun getInstance(ctx: Context): Preferences { + var prefs = instance?.get() + if (prefs == null) { + prefs = Preferences(ctx.applicationContext) + instance = WeakReference(prefs) + } + return prefs } } - } diff --git a/build.gradle b/build.gradle index bf3abe2..634522a 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.kotlin_version = '1.2.50' + ext.kotlin_version = '1.2.51' ext.anko_version = '0.10.5' repositories { jcenter()