🔥 Massively simplified how Abit connects to the network

* Removed features "Synchronization" and "Server POW"
* Service isn't foreground anymore (not yet sure this is a good decision)
* "Full node" renamed to "online"
This commit is contained in:
Christian Basler 2018-07-05 19:44:04 +02:00
parent 87bc01701c
commit 3767d976c8
31 changed files with 101 additions and 833 deletions

View File

@ -11,8 +11,6 @@
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" /> <uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS" /> <uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" /> <uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<application <application
android:name="android.support.multidex.MultiDexApplication" android:name="android.support.multidex.MultiDexApplication"
@ -122,18 +120,12 @@
<service <service
android:name=".service.BitmessageService" android:name=".service.BitmessageService"
android:description="@string/bitmessage_service_description"
android:exported="false" /> android:exported="false" />
<service <service
android:name=".service.ProofOfWorkService" android:name=".service.ProofOfWorkService"
android:exported="false" /> android:exported="false" />
<!-- Synchronization -->
<provider
android:name=".synchronization.StubProvider"
android:authorities="ch.dissem.apps.abit.provider"
android:exported="false"
android:syncable="true" />
<!-- Exports --> <!-- Exports -->
<provider <provider
android:name="android.support.v4.content.FileProvider" android:name="android.support.v4.content.FileProvider"
@ -145,30 +137,6 @@
android:resource="@xml/file_paths" /> android:resource="@xml/file_paths" />
</provider> </provider>
<service
android:name=".synchronization.AuthenticatorService"
android:exported="true"
tools:ignore="ExportedService">
<intent-filter>
<action android:name="android.accounts.AccountAuthenticator" />
</intent-filter>
<meta-data
android:name="android.accounts.AccountAuthenticator"
android:resource="@xml/authenticator" />
</service>
<service
android:name=".synchronization.SyncService"
android:exported="true"
tools:ignore="ExportedService">
<intent-filter>
<action android:name="android.content.SyncAdapter" />
</intent-filter>
<meta-data
android:name="android.content.SyncAdapter"
android:resource="@xml/syncadapter" />
</service>
<service <service
android:name=".service.BitmessageIntentService" android:name=".service.BitmessageIntentService"
android:exported="false" /> android:exported="false" />
@ -182,7 +150,7 @@
</receiver> </receiver>
<service <service
android:name=".service.StartupNodeOnWifiService" android:name=".service.NodeStartupService"
android:exported="true" android:exported="true"
android:permission="android.permission.BIND_JOB_SERVICE" /> android:permission="android.permission.BIND_JOB_SERVICE" />

View File

@ -76,7 +76,7 @@ class ComposeMessageFragment : Fragment() {
parents.addAll(draft.parents) parents.addAll(draft.parents)
} else { } else {
var id = getSerializable(EXTRA_IDENTITY) as? BitmessageAddress var id = getSerializable(EXTRA_IDENTITY) as? BitmessageAddress
if (context != null && (id == null || id.privateKey == null)) { if (context != null && id?.privateKey == null) {
id = Singleton.getIdentity(context!!) id = Singleton.getIdentity(context!!)
} }
if (id?.privateKey != null) { if (id?.privateKey != null) {

View File

@ -31,7 +31,6 @@ import ch.dissem.apps.abit.listener.ListSelectionListener
import ch.dissem.apps.abit.repository.AndroidLabelRepository.Companion.LABEL_ARCHIVE import ch.dissem.apps.abit.repository.AndroidLabelRepository.Companion.LABEL_ARCHIVE
import ch.dissem.apps.abit.service.Singleton import ch.dissem.apps.abit.service.Singleton
import ch.dissem.apps.abit.service.Singleton.currentLabel import ch.dissem.apps.abit.service.Singleton.currentLabel
import ch.dissem.apps.abit.synchronization.SyncAdapter
import ch.dissem.apps.abit.util.NetworkUtils import ch.dissem.apps.abit.util.NetworkUtils
import ch.dissem.apps.abit.util.Preferences import ch.dissem.apps.abit.util.Preferences
import ch.dissem.apps.abit.util.getColor import ch.dissem.apps.abit.util.getColor
@ -145,11 +144,6 @@ class MainActivity : AppCompatActivity(), ListSelectionListener<Serializable> {
ComposeMessageActivity.launchReplyTo(this, item) ComposeMessageActivity.launchReplyTo(this, item)
} }
if (Preferences.useTrustedNode(this)) {
SyncAdapter.startSync(this)
} else {
SyncAdapter.stopSync(this)
}
if (drawer.isDrawerOpen) { if (drawer.isDrawerOpen) {
MaterialShowcaseView.Builder(this) MaterialShowcaseView.Builder(this)
.setMaskColour(R.color.colorPrimary) .setMaskColour(R.color.colorPrimary)
@ -179,8 +173,6 @@ class MainActivity : AppCompatActivity(), ListSelectionListener<Serializable> {
.setDelay(1000) .setDelay(1000)
.show() .show()
} }
SyncAdapter.startSync(this)
} }
private fun <F> changeList(listFragment: F) where F : Fragment, F : ListHolder<*> { private fun <F> changeList(listFragment: F) where F : Fragment, F : ListHolder<*> {
@ -259,14 +251,13 @@ class MainActivity : AppCompatActivity(), ListSelectionListener<Serializable> {
nodeSwitch = SwitchDrawerItem() nodeSwitch = SwitchDrawerItem()
.withIdentifier(ID_NODE_SWITCH) .withIdentifier(ID_NODE_SWITCH)
.withName(R.string.full_node) .withName(R.string.online)
.withIcon(CommunityMaterial.Icon.cmd_cloud_outline) .withIcon(CommunityMaterial.Icon.cmd_cloud_outline)
.withChecked(Preferences.isFullNodeActive(this)) .withChecked(Preferences.isOnline(this))
.withOnCheckedChangeListener { _, _, isChecked -> .withOnCheckedChangeListener { _, _, isChecked ->
Preferences.setOnline(this, isChecked)
if (isChecked) { if (isChecked) {
NetworkUtils.enableNode(this@MainActivity) NetworkUtils.enableNode(this, true)
} else {
NetworkUtils.disableNode(this@MainActivity)
} }
} }
@ -369,10 +360,8 @@ class MainActivity : AppCompatActivity(), ListSelectionListener<Serializable> {
} }
override fun onResume() { override fun onResume() {
updateUnread()
if (Preferences.isFullNodeActive(this) && Preferences.isConnectionAllowed(this@MainActivity)) {
NetworkUtils.enableNode(this, false) NetworkUtils.enableNode(this, false)
} updateUnread()
Singleton.getMessageListener(this).resetNotification() Singleton.getMessageListener(this).resetNotification()
currentLabel.addObserver(this) { label -> currentLabel.addObserver(this) { label ->
if (label != null && label.id is Long) { if (label != null && label.id is Long) {
@ -578,15 +567,6 @@ class MainActivity : AppCompatActivity(), ListSelectionListener<Serializable> {
private var instance: WeakReference<MainActivity>? = null private var instance: WeakReference<MainActivity>? = null
fun updateNodeSwitch() {
apply {
runOnUiThread {
nodeSwitch.withChecked(Preferences.isFullNodeActive(this))
drawer.updateStickyFooterItem(nodeSwitch)
}
}
}
/** /**
* Runs the given code in the main activity context, if it currently exists. Otherwise, * Runs the given code in the main activity context, if it currently exists. Otherwise,
* it's ignored. * it's ignored.

View File

@ -134,6 +134,9 @@ class MessageListFragment : Fragment(), ListHolder<Label> {
} }
private fun doUpdateList(label: Label?) { private fun doUpdateList(label: Label?) {
// If the menu item isn't available yet, we should wait - the method will be called again once it's
// initialized.
emptyTrashMenuItem?.let { menuItem ->
val mainActivity = activity as? MainActivity val mainActivity = activity as? MainActivity
swipeableMessageAdapter?.clear(label) swipeableMessageAdapter?.clear(label)
if (label == null) { if (label == null) {
@ -141,7 +144,7 @@ class MessageListFragment : Fragment(), ListHolder<Label> {
swipeableMessageAdapter?.notifyDataSetChanged() swipeableMessageAdapter?.notifyDataSetChanged()
return return
} }
emptyTrashMenuItem?.isVisible = label.type == Label.Type.TRASH menuItem.isVisible = label.type == Label.Type.TRASH
mainActivity?.apply { mainActivity?.apply {
if ("archive" == label.toString()) { if ("archive" == label.toString()) {
updateTitle(getString(R.string.archive)) updateTitle(getString(R.string.archive))
@ -152,6 +155,7 @@ class MessageListFragment : Fragment(), ListHolder<Label> {
loadMoreItems() loadMoreItems()
} }
}
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
@ -296,6 +300,7 @@ class MessageListFragment : Fragment(), ListHolder<Label> {
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.message_list, menu) inflater.inflate(R.menu.message_list, menu)
emptyTrashMenuItem = menu.findItem(R.id.empty_trash) emptyTrashMenuItem = menu.findItem(R.id.empty_trash)
currentLabel.value?.let { doUpdateList(it) }
super.onCreateOptionsMenu(menu, inflater) super.onCreateOptionsMenu(menu, inflater)
} }

View File

@ -33,9 +33,6 @@ import android.widget.Toast
import ch.dissem.apps.abit.service.BatchProcessorService import ch.dissem.apps.abit.service.BatchProcessorService
import ch.dissem.apps.abit.service.SimpleJob import ch.dissem.apps.abit.service.SimpleJob
import ch.dissem.apps.abit.service.Singleton import ch.dissem.apps.abit.service.Singleton
import ch.dissem.apps.abit.synchronization.SyncAdapter
import ch.dissem.apps.abit.util.Constants.PREFERENCE_SERVER_POW
import ch.dissem.apps.abit.util.Constants.PREFERENCE_TRUSTED_NODE
import ch.dissem.apps.abit.util.Exports import ch.dissem.apps.abit.util.Exports
import ch.dissem.apps.abit.util.NetworkUtils import ch.dissem.apps.abit.util.NetworkUtils
import ch.dissem.apps.abit.util.Preferences import ch.dissem.apps.abit.util.Preferences
@ -51,8 +48,7 @@ import java.util.*
/** /**
* @author Christian Basler * @author Christian Basler
*/ */
class SettingsFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedPreferenceChangeListener, class SettingsFragment : PreferenceFragmentCompat(), PreferenceFragmentCompat.OnPreferenceStartScreenCallback {
PreferenceFragmentCompat.OnPreferenceStartScreenCallback {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.preferences, rootKey) setPreferencesFromResource(R.xml.preferences, rootKey)
@ -187,24 +183,6 @@ class SettingsFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedP
} }
} }
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
when (key) {
PREFERENCE_SERVER_POW -> toggleSyncServerPOW(sharedPreferences)
}
}
private fun toggleSyncServerPOW(sharedPreferences: SharedPreferences) {
val node = sharedPreferences.getString(PREFERENCE_TRUSTED_NODE, null)
if (node != null) {
val ctx = context ?: throw IllegalStateException("No context available")
if (sharedPreferences.getBoolean(PREFERENCE_SERVER_POW, false)) {
SyncAdapter.startPowSync(ctx)
} else {
SyncAdapter.stopPowSync(ctx)
}
}
}
private val connection = object : ServiceConnection { private val connection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName, service: IBinder) { override fun onServiceConnected(name: ComponentName, service: IBinder) {
if (service is BatchProcessorService.BatchBinder) { if (service is BatchProcessorService.BatchBinder) {
@ -250,11 +228,7 @@ class SettingsFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedP
private fun connectivityChangeListener() = private fun connectivityChangeListener() =
OnPreferenceChangeListener { _, _ -> OnPreferenceChangeListener { _, _ ->
context?.let { ctx -> context?.let { ctx -> NetworkUtils.scheduleNodeStart(ctx) }
if (Preferences.isFullNodeActive(ctx)) {
NetworkUtils.scheduleNodeStart(ctx)
}
}
true true
} }

View File

@ -1,48 +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.adapter
import android.content.Context
import android.preference.PreferenceManager
import ch.dissem.bitmessage.InternalContext
import ch.dissem.bitmessage.ports.ProofOfWorkEngine
/**
* Switches between two [ProofOfWorkEngine]s depending on the configuration.
*
* @author Christian Basler
*/
class SwitchingProofOfWorkEngine(
private val ctx: Context,
private val preference: String,
private val option: ProofOfWorkEngine,
private val fallback: ProofOfWorkEngine
) : ProofOfWorkEngine, InternalContext.ContextHolder {
override fun calculateNonce(initialHash: ByteArray, target: ByteArray, callback: ProofOfWorkEngine.Callback) {
val preferences = PreferenceManager.getDefaultSharedPreferences(ctx)
if (preferences.getBoolean(preference, false)) {
option.calculateNonce(initialHash, target, callback)
} else {
fallback.calculateNonce(initialHash, target, callback)
}
}
override fun setContext(context: InternalContext) = listOf(option, fallback)
.filterIsInstance<InternalContext.ContextHolder>()
.forEach { it.setContext(context) }
}

View File

@ -49,7 +49,7 @@ class SelectEncodingDialogFragment : AppCompatDialogFragment() {
when (encoding) { when (encoding) {
SIMPLE -> radioGroup.check(R.id.simple) SIMPLE -> radioGroup.check(R.id.simple)
EXTENDED -> radioGroup.check(R.id.extended) EXTENDED -> radioGroup.check(R.id.extended)
else -> LOG.warn("Unexpected encoding: " + encoding) else -> LOG.warn("Unexpected encoding: $encoding")
} }
ok.setOnClickListener(View.OnClickListener { ok.setOnClickListener(View.OnClickListener {
encoding = when (radioGroup.checkedRadioButtonId) { encoding = when (radioGroup.checkedRadioButtonId) {

View File

@ -81,7 +81,7 @@ class MessageListener(ctx: Context) : BitmessageContext.Listener.WithContext {
} }
} }
fun updateConversation(plaintext: Plaintext) { private fun updateConversation(plaintext: Plaintext) {
if (emulateConversations && plaintext.encoding != Plaintext.Encoding.EXTENDED) { if (emulateConversations && plaintext.encoding != Plaintext.Encoding.EXTENDED) {
conversationService.getSubject(listOf(plaintext))?.let { subject -> conversationService.getSubject(listOf(plaintext))?.let { subject ->
plaintext.conversationId = UUID.nameUUIDFromBytes(subject.toByteArray()) plaintext.conversationId = UUID.nameUUIDFromBytes(subject.toByteArray())

View File

@ -1,83 +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.pow
import android.content.Context
import ch.dissem.apps.abit.service.Singleton
import ch.dissem.apps.abit.synchronization.SyncAdapter
import ch.dissem.apps.abit.util.Preferences
import ch.dissem.bitmessage.InternalContext
import ch.dissem.bitmessage.extensions.CryptoCustomMessage
import ch.dissem.bitmessage.extensions.pow.ProofOfWorkRequest
import ch.dissem.bitmessage.extensions.pow.ProofOfWorkRequest.Request.CALCULATE
import ch.dissem.bitmessage.ports.ProofOfWorkEngine
import ch.dissem.bitmessage.utils.Singleton.cryptography
import org.slf4j.LoggerFactory
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
/**
* @author Christian Basler
*/
class ServerPowEngine(private val ctx: Context) : ProofOfWorkEngine, InternalContext.ContextHolder {
private lateinit var context: InternalContext
private val pool: ExecutorService
init {
pool = Executors.newCachedThreadPool { r ->
val thread = Executors.defaultThreadFactory().newThread(r)
thread.priority = Thread.MIN_PRIORITY
thread
}
}
override fun calculateNonce(initialHash: ByteArray, target: ByteArray, callback: ProofOfWorkEngine.Callback) =
pool.execute {
val identity = Singleton.getIdentity(ctx) ?: throw RuntimeException("No Identity for calculating POW")
val request = ProofOfWorkRequest(identity, initialHash,
CALCULATE, target)
SyncAdapter.startPowSync(ctx)
try {
val cryptoMsg = CryptoCustomMessage(request)
cryptoMsg.signAndEncrypt(
identity,
cryptography().createPublicKey(identity.publicDecryptionKey)
)
val node = Preferences.getTrustedNode(ctx)
if (node == null) {
LOG.error("trusted node is not defined")
} else {
context.networkHandler.send(
node,
Preferences.getTrustedNodePort(ctx),
cryptoMsg)
}
} catch (e: Exception) {
LOG.error(e.message, e)
}
}
override fun setContext(context: InternalContext) {
this.context = context
}
companion object {
private val LOG = LoggerFactory.getLogger(ServerPowEngine::class.java)
}
}

View File

@ -23,8 +23,9 @@ import android.content.Intent
import android.content.IntentFilter import android.content.IntentFilter
import android.net.ConnectivityManager import android.net.ConnectivityManager
import android.os.Handler 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.notification.NetworkNotification
import ch.dissem.apps.abit.notification.NetworkNotification.Companion.NETWORK_NOTIFICATION_ID
import ch.dissem.apps.abit.util.Preferences import ch.dissem.apps.abit.util.Preferences
import ch.dissem.bitmessage.BitmessageContext import ch.dissem.bitmessage.BitmessageContext
import ch.dissem.bitmessage.utils.Property import ch.dissem.bitmessage.utils.Property
@ -74,7 +75,6 @@ class BitmessageService : Service() {
if (!isRunning) { if (!isRunning) {
running = true running = true
notification.connecting() notification.connecting()
startForeground(NETWORK_NOTIFICATION_ID, notification.notification)
if (!bmc.isRunning()) { if (!bmc.isRunning()) {
bmc.startup() bmc.startup()
} }

View File

@ -2,8 +2,6 @@ package ch.dissem.apps.abit.service
import android.app.job.JobParameters import android.app.job.JobParameters
import android.app.job.JobService import android.app.job.JobService
import android.os.Build
import android.support.annotation.RequiresApi
import ch.dissem.apps.abit.util.NetworkUtils import ch.dissem.apps.abit.util.NetworkUtils
import ch.dissem.apps.abit.util.Preferences import ch.dissem.apps.abit.util.Preferences
@ -14,11 +12,11 @@ import ch.dissem.apps.abit.util.Preferences
* *
* And stops it when the preconditions for the job (unmetered network) aren't met anymore. * And stops it when the preconditions for the job (unmetered network) aren't met anymore.
*/ */
class StartupNodeOnWifiService : JobService() { class NodeStartupService : JobService() {
override fun onStartJob(params: JobParameters?): Boolean { override fun onStartJob(params: JobParameters?): Boolean {
val bmc = Singleton.getBitmessageContext(this) val bmc = Singleton.getBitmessageContext(this)
if (Preferences.isFullNodeActive(this) && !bmc.isRunning()) { if (Preferences.isOnline(this) && !bmc.isRunning()) {
NetworkUtils.doStartBitmessageService(applicationContext) NetworkUtils.doStartBitmessageService(applicationContext)
} }
return true return true
@ -28,6 +26,6 @@ class StartupNodeOnWifiService : JobService() {
* Don't actually stop the service, otherwise it will be stopped after 1 or 10 minutes * Don't actually stop the service, otherwise it will be stopped after 1 or 10 minutes
* depending on Android version. * depending on Android version.
*/ */
override fun onStopJob(params: JobParameters?) = Preferences.isFullNodeActive(this) override fun onStopJob(params: JobParameters?) = false
} }

View File

@ -21,11 +21,8 @@ import android.widget.Toast
import ch.dissem.apps.abit.MainActivity import ch.dissem.apps.abit.MainActivity
import ch.dissem.apps.abit.R import ch.dissem.apps.abit.R
import ch.dissem.apps.abit.adapter.SwipeableMessageAdapter import ch.dissem.apps.abit.adapter.SwipeableMessageAdapter
import ch.dissem.apps.abit.adapter.SwitchingProofOfWorkEngine
import ch.dissem.apps.abit.listener.MessageListener import ch.dissem.apps.abit.listener.MessageListener
import ch.dissem.apps.abit.pow.ServerPowEngine
import ch.dissem.apps.abit.repository.* import ch.dissem.apps.abit.repository.*
import ch.dissem.apps.abit.util.Constants
import ch.dissem.apps.abit.util.Observable import ch.dissem.apps.abit.util.Observable
import ch.dissem.bitmessage.BitmessageContext import ch.dissem.bitmessage.BitmessageContext
import ch.dissem.bitmessage.cryptography.sc.SpongyCryptography import ch.dissem.bitmessage.cryptography.sc.SpongyCryptography
@ -107,11 +104,7 @@ object Singleton {
TTL.pubkey = 2 * DAY TTL.pubkey = 2 * DAY
val ctx = context.applicationContext val ctx = context.applicationContext
val sqlHelper = SqlHelper(ctx) val sqlHelper = SqlHelper(ctx)
proofOfWorkEngine = SwitchingProofOfWorkEngine( proofOfWorkEngine = ServicePowEngine(ctx)
ctx, Constants.PREFERENCE_SERVER_POW,
ServerPowEngine(ctx),
ServicePowEngine(ctx)
)
cryptography = SpongyCryptography() cryptography = SpongyCryptography()
nodeRegistry = AndroidNodeRegistry(sqlHelper) nodeRegistry = AndroidNodeRegistry(sqlHelper)
inventory = AndroidInventory(sqlHelper) inventory = AndroidInventory(sqlHelper)
@ -138,8 +131,6 @@ object Singleton {
fun getAddressRepository(ctx: Context) = getBitmessageContext(ctx).addresses as AndroidAddressRepository fun getAddressRepository(ctx: Context) = getBitmessageContext(ctx).addresses as AndroidAddressRepository
fun getProofOfWorkRepository(ctx: Context) = powRepo ?: getBitmessageContext(ctx).internals.proofOfWorkRepository
fun getIdentity(ctx: Context): BitmessageAddress? = fun getIdentity(ctx: Context): BitmessageAddress? =
init<BitmessageAddress?>(ctx, { identity }, { identity = it }) { bmc -> init<BitmessageAddress?>(ctx, { identity }, { identity = it }) { bmc ->
val identities = bmc.addresses.getIdentities() val identities = bmc.addresses.getIdentities()

View File

@ -3,6 +3,7 @@ package ch.dissem.apps.abit.service
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.Intent.ACTION_BOOT_COMPLETED
import ch.dissem.apps.abit.util.NetworkUtils import ch.dissem.apps.abit.util.NetworkUtils
import ch.dissem.apps.abit.util.Preferences import ch.dissem.apps.abit.util.Preferences
@ -11,10 +12,8 @@ import ch.dissem.apps.abit.util.Preferences
*/ */
class StartServiceReceiver : BroadcastReceiver() { class StartServiceReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent?) { override fun onReceive(context: Context, intent: Intent?) {
if (intent?.action == "android.intent.action.BOOT_COMPLETED") { if (intent?.action == ACTION_BOOT_COMPLETED && Preferences.isOnline(context)) {
if (Preferences.isFullNodeActive(context)) {
NetworkUtils.enableNode(context, false) NetworkUtils.enableNode(context, false)
} }
} }
} }
}

View File

@ -1,62 +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.synchronization
import android.accounts.AbstractAccountAuthenticator
import android.accounts.Account
import android.accounts.AccountAuthenticatorResponse
import android.accounts.NetworkErrorException
import android.content.Context
import android.os.Bundle
/**
* Implement AbstractAccountAuthenticator and stub out all
* of its methods
*/
class Authenticator(context: Context) : AbstractAccountAuthenticator(context) {
override fun editProperties(r: AccountAuthenticatorResponse, s: String) =
throw UnsupportedOperationException("Editing properties is not supported")
// Don't add additional accounts
@Throws(NetworkErrorException::class)
override fun addAccount(r: AccountAuthenticatorResponse, s: String, s2: String, strings: Array<String>, bundle: Bundle) = null
// Ignore attempts to confirm credentials
@Throws(NetworkErrorException::class)
override fun confirmCredentials(r: AccountAuthenticatorResponse, account: Account, bundle: Bundle) = null
@Throws(NetworkErrorException::class)
override fun getAuthToken(r: AccountAuthenticatorResponse, account: Account, s: String, bundle: Bundle) =
throw UnsupportedOperationException("Getting an authentication token is not supported")
override fun getAuthTokenLabel(s: String) =
throw UnsupportedOperationException("Getting a label for the auth token is not supported")
@Throws(NetworkErrorException::class)
override fun updateCredentials(r: AccountAuthenticatorResponse, account: Account, s: String, bundle: Bundle) =
throw UnsupportedOperationException("Updating user credentials is not supported")
@Throws(NetworkErrorException::class)
override fun hasFeatures(r: AccountAuthenticatorResponse, account: Account, strings: Array<String>) =
throw UnsupportedOperationException("Checking features for the account is not supported")
companion object {
val ACCOUNT_SYNC = Account("Bitmessage", "ch.dissem.bitmessage")
val ACCOUNT_POW = Account("Proof of Work ", "ch.dissem.bitmessage")
}
}

View File

@ -1,42 +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.synchronization
import android.app.Service
import android.content.Intent
/**
* A bound Service that instantiates the authenticator
* when started.
*/
class AuthenticatorService : Service() {
/**
* Instance field that stores the authenticator object
*/
private var authenticator: Authenticator? = null
override fun onCreate() {
// Create a new authenticator object
authenticator = Authenticator(this)
}
/*
* When the system binds to this Service to make the RPC call
* return the authenticator's IBinder.
*/
override fun onBind(intent: Intent) = authenticator?.iBinder
}

View File

@ -1,72 +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.synchronization
import android.content.ContentProvider
import android.content.ContentValues
import android.net.Uri
/*
* Define an implementation of ContentProvider that stubs out
* all methods
*/
class StubProvider : ContentProvider() {
/**
* Always return true, indicating that the
* provider loaded correctly.
*/
override fun onCreate() = true
/**
* Return no type for MIME type
*/
override fun getType(uri: Uri) = null
/**
* query() always returns no results
*/
override fun query(
uri: Uri,
projection: Array<String>?,
selection: String?,
selectionArgs: Array<String>?,
sortOrder: String?) = null
/**
* insert() always returns null (no URI)
*/
override fun insert(uri: Uri, values: ContentValues?) = null
/**
* delete() always returns "no rows affected" (0)
*/
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?) = 0
/**
* update() always returns "no rows affected" (0)
*/
override fun update(
uri: Uri,
values: ContentValues?,
selection: String?,
selectionArgs: Array<String>?) = 0
companion object {
const val AUTHORITY = "ch.dissem.apps.abit.provider"
}
}

View File

@ -1,188 +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.synchronization
import android.accounts.Account
import android.accounts.AccountManager
import android.content.*
import android.os.Bundle
import ch.dissem.apps.abit.service.Singleton
import ch.dissem.apps.abit.synchronization.Authenticator.Companion.ACCOUNT_POW
import ch.dissem.apps.abit.synchronization.Authenticator.Companion.ACCOUNT_SYNC
import ch.dissem.apps.abit.synchronization.StubProvider.Companion.AUTHORITY
import ch.dissem.apps.abit.util.NetworkUtils
import ch.dissem.apps.abit.util.Preferences
import ch.dissem.bitmessage.exception.DecryptionFailedException
import ch.dissem.bitmessage.extensions.CryptoCustomMessage
import ch.dissem.bitmessage.extensions.pow.ProofOfWorkRequest
import ch.dissem.bitmessage.extensions.pow.ProofOfWorkRequest.Request.CALCULATE
import ch.dissem.bitmessage.extensions.pow.ProofOfWorkRequest.Request.COMPLETE
import ch.dissem.bitmessage.utils.Singleton.cryptography
import org.slf4j.LoggerFactory
import java.io.IOException
/**
* Sync Adapter to synchronize with the Bitmessage network - fetches
* new objects and then disconnects.
*/
class SyncAdapter(context: Context, autoInitialize: Boolean) : AbstractThreadedSyncAdapter(context, autoInitialize) {
private val bmc = Singleton.getBitmessageContext(context)
override fun onPerformSync(
account: Account,
extras: Bundle,
authority: String,
provider: ContentProviderClient,
syncResult: SyncResult
) {
try {
if (account == ACCOUNT_SYNC) {
if (Preferences.isConnectionAllowed(context)) {
syncData()
}
} else if (account == ACCOUNT_POW) {
syncPOW()
} else {
syncResult.stats.numAuthExceptions++
}
} catch (e: IOException) {
syncResult.stats.numIoExceptions++
} catch (e: DecryptionFailedException) {
syncResult.stats.numAuthExceptions++
}
}
private fun syncData() {
// If the Bitmessage context acts as a full node, synchronization isn't necessary
if (bmc.isRunning()) {
LOG.info("Synchronization skipped, Abit is acting as a full node")
return
}
val trustedNode = Preferences.getTrustedNode(context)
if (trustedNode == null) {
// As Abit tends to get killed by the system, let's leverage the sync mechanism to start it again:
NetworkUtils.scheduleNodeStart(context)
return
}
LOG.info("Synchronization started")
bmc.synchronize(
trustedNode,
Preferences.getTrustedNodePort(context),
Preferences.getTimeoutInSeconds(context),
true
)
LOG.info("Synchronization finished")
}
private fun syncPOW() {
val identity = Singleton.getIdentity(context)
if (identity == null) {
LOG.info("No identity available - skipping POW synchronization")
return
}
val trustedNode = Preferences.getTrustedNode(context)
if (trustedNode == null) {
LOG.info("Trusted node not available, disabling POW synchronization")
stopPowSync(context)
return
}
// If the Bitmessage context acts as a full node, synchronization isn't necessary
LOG.info("Looking for completed POW")
val privateKey =
identity.privateKey?.privateEncryptionKey ?: throw IllegalStateException("Identity without private key")
val signingKey = cryptography().createPublicKey(identity.publicDecryptionKey)
val reader = ProofOfWorkRequest.Reader(identity)
val powRepo = Singleton.getProofOfWorkRepository(context)
val items = powRepo.getItems()
for (initialHash in items) {
val (objectMessage, nonceTrialsPerByte, extraBytes) = powRepo.getItem(initialHash)
val target = cryptography().getProofOfWorkTarget(objectMessage, nonceTrialsPerByte, extraBytes)
val cryptoMsg = CryptoCustomMessage(
ProofOfWorkRequest(identity, initialHash, CALCULATE, target)
)
cryptoMsg.signAndEncrypt(identity, signingKey)
val response = bmc.send(
trustedNode,
Preferences.getTrustedNodePort(context),
cryptoMsg
)
if (response.isError) {
LOG.error("Server responded with error: ${String(response.getData())}")
} else {
val (_, _, request, data) = CryptoCustomMessage.read(response, reader).decrypt(privateKey)
if (request == COMPLETE) {
bmc.internals.proofOfWorkService.onNonceCalculated(initialHash, data)
}
}
}
if (items.isEmpty()) {
stopPowSync(context)
}
LOG.info("Synchronization finished")
}
companion object {
private val LOG = LoggerFactory.getLogger(SyncAdapter::class.java)
private const val SYNC_FREQUENCY = 15 * 60L // seconds
fun startSync(ctx: Context) {
// Create account, if it's missing. (Either first run, or user has deleted account.)
val account = addAccount(ctx, ACCOUNT_SYNC)
// Recommend a schedule for automatic synchronization. The system may modify this based
// on other scheduled syncs and network utilization.
ContentResolver.addPeriodicSync(account, AUTHORITY, Bundle(), SYNC_FREQUENCY)
}
fun stopSync(ctx: Context) {
// Create account, if it's missing. (Either first run, or user has deleted account.)
val account = addAccount(ctx, ACCOUNT_SYNC)
ContentResolver.removePeriodicSync(account, AUTHORITY, Bundle())
}
fun startPowSync(ctx: Context) {
// Create account, if it's missing. (Either first run, or user has deleted account.)
val account = addAccount(ctx, ACCOUNT_POW)
// Recommend a schedule for automatic synchronization. The system may modify this based
// on other scheduled syncs and network utilization.
ContentResolver.addPeriodicSync(account, AUTHORITY, Bundle(), SYNC_FREQUENCY)
}
fun stopPowSync(ctx: Context) {
// Create account, if it's missing. (Either first run, or user has deleted account.)
val account = addAccount(ctx, ACCOUNT_POW)
ContentResolver.removePeriodicSync(account, AUTHORITY, Bundle())
}
private fun addAccount(ctx: Context, account: Account): Account {
if (AccountManager.get(ctx).addAccountExplicitly(account, null, null)) {
// Inform the system that this account supports sync
ContentResolver.setIsSyncable(account, AUTHORITY, 1)
// Inform the system that this account is eligible for auto sync when the network is up
ContentResolver.setSyncAutomatically(account, AUTHORITY, true)
}
return account
}
}
}

View File

@ -1,50 +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.synchronization
import android.app.Service
import android.content.Intent
/**
* Define a Service that returns an IBinder for the
* sync adapter class, allowing the sync adapter framework to call
* onPerformSync().
*/
class SyncService : Service() {
/**
* Instantiate the sync adapter object.
*/
override fun onCreate() = synchronized(syncAdapterLock) {
if (syncAdapter == null) {
syncAdapter = SyncAdapter(this, true)
}
}
/**
* Return an object that allows the system to invoke
* the sync adapter.
*/
override fun onBind(intent: Intent) = syncAdapter?.syncAdapterBinder
companion object {
// Storage for an instance of the sync adapter
private var syncAdapter: SyncAdapter? = null
// Object to use as a thread-safe lock
private val syncAdapterLock = Any()
}
}

View File

@ -22,13 +22,10 @@ import java.util.regex.Pattern
* @author Christian Basler * @author Christian Basler
*/ */
object Constants { object Constants {
const val PREFERENCE_ONLINE = "online"
const val PREFERENCE_WIFI_ONLY = "wifi_only" const val PREFERENCE_WIFI_ONLY = "wifi_only"
const val PREFERENCE_REQUIRE_CHARGING = "require_charging" const val PREFERENCE_REQUIRE_CHARGING = "require_charging"
const val PREFERENCE_EMULATE_CONVERSATIONS = "emulate_conversations" const val PREFERENCE_EMULATE_CONVERSATIONS = "emulate_conversations"
const val PREFERENCE_TRUSTED_NODE = "trusted_node"
const val PREFERENCE_SYNC_TIMEOUT = "sync_timeout"
const val PREFERENCE_SERVER_POW = "server_pow"
const val PREFERENCE_FULL_NODE = "full_node"
const val PREFERENCE_REQUEST_ACK = "request_acknowledgments" const val PREFERENCE_REQUEST_ACK = "request_acknowledgments"
const val PREFERENCE_POW_AVERAGE = "average_pow_time_ms" const val PREFERENCE_POW_AVERAGE = "average_pow_time_ms"
const val PREFERENCE_POW_COUNT = "pow_count" const val PREFERENCE_POW_COUNT = "pow_count"

View File

@ -6,17 +6,15 @@ import android.app.job.JobScheduler
import android.content.ComponentName import android.content.ComponentName
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.support.v4.content.ContextCompat import android.os.Build
import ch.dissem.apps.abit.dialog.FullNodeDialogActivity import ch.dissem.apps.abit.dialog.FullNodeDialogActivity
import ch.dissem.apps.abit.service.BitmessageService import ch.dissem.apps.abit.service.BitmessageService
import ch.dissem.apps.abit.service.StartupNodeOnWifiService import ch.dissem.apps.abit.service.NodeStartupService
object NetworkUtils { object NetworkUtils {
fun enableNode(ctx: Context, ask: Boolean = true) { fun enableNode(ctx: Context, ask: Boolean = true) {
Preferences.setFullNodeActive(ctx, true)
if (Preferences.isConnectionAllowed(ctx) || !ask) { if (Preferences.isConnectionAllowed(ctx) || !ask) {
scheduleNodeStart(ctx) scheduleNodeStart(ctx)
} else { } else {
@ -31,25 +29,29 @@ object NetworkUtils {
} }
fun doStartBitmessageService(ctx: Context) { fun doStartBitmessageService(ctx: Context) {
ContextCompat.startForegroundService(ctx, Intent(ctx, BitmessageService::class.java)) ctx.startService(Intent(ctx, BitmessageService::class.java))
} }
fun disableNode(ctx: Context) { fun disableNode(ctx: Context) {
Preferences.setFullNodeActive(ctx, false)
ctx.stopService(Intent(ctx, BitmessageService::class.java)) ctx.stopService(Intent(ctx, BitmessageService::class.java))
} }
fun scheduleNodeStart(ctx: Context) { fun scheduleNodeStart(ctx: Context) {
val jobScheduler = ctx.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler val jobScheduler = ctx.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler
val serviceComponent = ComponentName(ctx, StartupNodeOnWifiService::class.java) val serviceComponent = ComponentName(ctx, NodeStartupService::class.java)
val builder = JobInfo.Builder(0, serviceComponent) val builder = JobInfo.Builder(0, serviceComponent)
if (Preferences.isWifiOnly(ctx)) { when {
Preferences.isWifiOnly(ctx) ->
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED) 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)) { if (Preferences.requireCharging(ctx)) {
builder.setRequiresCharging(true) builder.setRequiresCharging(true)
} }
builder.setBackoffCriteria(0L, JobInfo.BACKOFF_POLICY_LINEAR) builder.setPeriodic(15 * 60 * 1000L)
builder.setPersisted(true) builder.setPersisted(true)
jobScheduler.schedule(builder.build()) jobScheduler.schedule(builder.build())
} }

View File

@ -8,11 +8,11 @@ import kotlin.properties.Delegates
class Observable<T>(value: T) { class Observable<T>(value: T) {
private val observers = mutableMapOf<Any, (T) -> Unit>() private val observers = mutableMapOf<Any, (T) -> Unit>()
var value: T by Delegates.observable(value, { _, old, new -> var value: T by Delegates.observable(value) { _, old, new ->
if (old != new) { if (old != new) {
observers.values.forEach { it.invoke(new) } observers.values.forEach { it.invoke(new) }
} }
}) }
/** /**
* The key will make sure the observer can easily be removed. Usually the key should be either * The key will make sure the observer can easily be removed. Usually the key should be either

View File

@ -17,26 +17,20 @@
package ch.dissem.apps.abit.util package ch.dissem.apps.abit.util
import android.content.Context import android.content.Context
import ch.dissem.apps.abit.R import android.content.Intent
import ch.dissem.apps.abit.notification.ErrorNotification import android.content.IntentFilter
import android.os.BatteryManager
import android.os.Build
import ch.dissem.apps.abit.util.Constants.PREFERENCE_EMULATE_CONVERSATIONS import ch.dissem.apps.abit.util.Constants.PREFERENCE_EMULATE_CONVERSATIONS
import ch.dissem.apps.abit.util.Constants.PREFERENCE_FULL_NODE import ch.dissem.apps.abit.util.Constants.PREFERENCE_ONLINE
import ch.dissem.apps.abit.util.Constants.PREFERENCE_REQUEST_ACK import ch.dissem.apps.abit.util.Constants.PREFERENCE_REQUEST_ACK
import ch.dissem.apps.abit.util.Constants.PREFERENCE_REQUIRE_CHARGING import ch.dissem.apps.abit.util.Constants.PREFERENCE_REQUIRE_CHARGING
import ch.dissem.apps.abit.util.Constants.PREFERENCE_SYNC_TIMEOUT
import ch.dissem.apps.abit.util.Constants.PREFERENCE_TRUSTED_NODE
import ch.dissem.apps.abit.util.Constants.PREFERENCE_WIFI_ONLY import ch.dissem.apps.abit.util.Constants.PREFERENCE_WIFI_ONLY
import org.jetbrains.anko.batteryManager import org.jetbrains.anko.batteryManager
import org.jetbrains.anko.connectivityManager import org.jetbrains.anko.connectivityManager
import org.jetbrains.anko.defaultSharedPreferences import org.jetbrains.anko.defaultSharedPreferences
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import java.io.File import java.io.File
import java.io.IOException
import java.net.InetAddress
import android.os.BatteryManager
import android.content.Intent
import android.content.IntentFilter
import android.os.Build
/** /**
@ -45,50 +39,6 @@ import android.os.Build
object Preferences { object Preferences {
private val LOG = LoggerFactory.getLogger(Preferences::class.java) private val LOG = LoggerFactory.getLogger(Preferences::class.java)
fun useTrustedNode(ctx: Context): Boolean {
val trustedNode = getPreference(ctx, PREFERENCE_TRUSTED_NODE) ?: return false
return trustedNode.trim { it <= ' ' }.isNotEmpty()
}
/**
* Warning, this method might do a network call and therefore can't be called from
* the UI thread.
*/
@Throws(IOException::class)
fun getTrustedNode(ctx: Context): InetAddress? {
var trustedNode: String = getPreference(ctx, PREFERENCE_TRUSTED_NODE) ?: return null
trustedNode = trustedNode.trim { it <= ' ' }
if (trustedNode.isEmpty()) return null
if (trustedNode.matches("^(?![0-9a-fA-F]*:[0-9a-fA-F]*:).*(:[0-9]+)$".toRegex())) {
val index = trustedNode.lastIndexOf(':')
trustedNode = trustedNode.substring(0, index)
}
return InetAddress.getByName(trustedNode)
}
fun getTrustedNodePort(ctx: Context): Int {
var trustedNode: String = getPreference(ctx, PREFERENCE_TRUSTED_NODE) ?: return 8444
trustedNode = trustedNode.trim { it <= ' ' }
if (trustedNode.matches("^(?![0-9a-fA-F]*:[0-9a-fA-F]*:).*(:[0-9]+)$".toRegex())) {
val index = trustedNode.lastIndexOf(':')
val portString = trustedNode.substring(index + 1)
try {
return Integer.parseInt(portString)
} catch (e: NumberFormatException) {
ErrorNotification(ctx)
.setError(R.string.error_invalid_sync_port, portString)
.show()
}
}
return 8444
}
fun getTimeoutInSeconds(ctx: Context): Long = getPreference(ctx, PREFERENCE_SYNC_TIMEOUT)?.toLong() ?: 120
private fun getPreference(ctx: Context, name: String): String? = ctx.defaultSharedPreferences.getString(name, null)
fun isConnectionAllowed(ctx: Context) = isAllowedForWiFi(ctx) && isAllowedForCharging(ctx) fun isConnectionAllowed(ctx: Context) = isAllowedForWiFi(ctx) && isAllowedForCharging(ctx)
private fun isAllowedForWiFi(ctx: Context) = !isWifiOnly(ctx) || !ctx.connectivityManager.isActiveNetworkMetered private fun isAllowedForWiFi(ctx: Context) = !isWifiOnly(ctx) || !ctx.connectivityManager.isActiveNetworkMetered
@ -113,25 +63,9 @@ object Preferences {
fun requireCharging(ctx: Context) = ctx.defaultSharedPreferences.getBoolean(PREFERENCE_REQUIRE_CHARGING, true) fun requireCharging(ctx: Context) = ctx.defaultSharedPreferences.getBoolean(PREFERENCE_REQUIRE_CHARGING, true)
fun setRequireCharging(ctx: Context, status: Boolean) {
ctx.defaultSharedPreferences.edit()
.putBoolean(PREFERENCE_REQUIRE_CHARGING, status)
.apply()
}
fun isEmulateConversations(ctx: Context) = fun isEmulateConversations(ctx: Context) =
ctx.defaultSharedPreferences.getBoolean(PREFERENCE_EMULATE_CONVERSATIONS, true) ctx.defaultSharedPreferences.getBoolean(PREFERENCE_EMULATE_CONVERSATIONS, true)
fun isFullNodeActive(ctx: Context) =
ctx.defaultSharedPreferences.getBoolean(PREFERENCE_FULL_NODE, false)
fun setFullNodeActive(ctx: Context, status: Boolean) {
ctx.defaultSharedPreferences.edit()
.putBoolean(PREFERENCE_FULL_NODE, status)
.apply()
}
fun getExportDirectory(ctx: Context) = File(ctx.filesDir, "exports") fun getExportDirectory(ctx: Context) = File(ctx.filesDir, "exports")
fun requestAcknowledgements(ctx: Context) = ctx.defaultSharedPreferences.getBoolean(PREFERENCE_REQUEST_ACK, true) fun requestAcknowledgements(ctx: Context) = ctx.defaultSharedPreferences.getBoolean(PREFERENCE_REQUEST_ACK, true)
@ -150,4 +84,18 @@ object Preferences {
} }
} }
} }
fun isOnline(ctx: Context) = ctx.defaultSharedPreferences.getBoolean(PREFERENCE_ONLINE, true)
fun setOnline(ctx: Context, status: Boolean) {
ctx.defaultSharedPreferences.edit()
.putBoolean(PREFERENCE_ONLINE, status)
.apply()
if (status) {
NetworkUtils.enableNode(ctx, true)
} else {
NetworkUtils.disableNode(ctx)
}
}
} }

View File

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="#ffffff">
<item
android:id="@android:id/mask"
android:drawable="@android:color/white"/>
</ripple>

View File

@ -30,8 +30,6 @@
<string name="reply">رد</string> <string name="reply">رد</string>
<string name="delete">حذف</string> <string name="delete">حذف</string>
<string name="empty_trash">أفرغ المهملات</string> <string name="empty_trash">أفرغ المهملات</string>
<string name="trusted_node">عقدة موثوقة</string>
<string name="trusted_node_summary">استخدام العقدة في التزامن</string>
<string name="write_message">كتابة رسالة</string> <string name="write_message">كتابة رسالة</string>
<string name="full_node">عقدة كاملة</string> <string name="full_node">عقدة كاملة</string>
<string name="send">إرسال</string> <string name="send">إرسال</string>
@ -51,15 +49,10 @@
<string name="mark_unread">حدد كمقروء</string> <string name="mark_unread">حدد كمقروء</string>
<string name="archive">أرشيف</string> <string name="archive">أرشيف</string>
<string name="stream_number">بث #%d</string> <string name="stream_number">بث #%d</string>
<string name="sync_timeout">انتهاء مهلة التزامن</string>
<string name="sync_timeout_summary">مهلة الإتصال بالثواني</string>
<string name="proof_of_work_text_n" tools:ignore="PluralsCandidate">جاري إثبات العمل لإرسال الرسالة (%1$d في قائمة الانتظار)</string> <string name="proof_of_work_text_n" tools:ignore="PluralsCandidate">جاري إثبات العمل لإرسال الرسالة (%1$d في قائمة الانتظار)</string>
<string name="error_invalid_sync_port">إعدادات منفذ التزامن غير صالحة</string>
<string name="compose_body_hint">كتابة رسالة</string> <string name="compose_body_hint">كتابة رسالة</string>
<string name="contacts_and_subscriptions">جهات اتصال</string> <string name="contacts_and_subscriptions">جهات اتصال</string>
<string name="subscribed">تم الاشتراك</string> <string name="subscribed">تم الاشتراك</string>
<string name="server_pow">خادم إثبات العمل</string>
<string name="server_pow_summary">عقدة موثوقة تقوم بإثبات العمل</string>
<string name="full_node_warning">تشغيل عقدة Bitmessage كاملة يستهلك الكثير من البيانات، مما قد يكون مكلفًا لبيانات الهاتف، هل أنت متأكد أنك تريد تشغيل عقدة كاملة؟</string> <string name="full_node_warning">تشغيل عقدة Bitmessage كاملة يستهلك الكثير من البيانات، مما قد يكون مكلفًا لبيانات الهاتف، هل أنت متأكد أنك تريد تشغيل عقدة كاملة؟</string>
<string name="about">عن Abit</string> <string name="about">عن Abit</string>
<string name="about_summary">التبعيات مفتوحة المصدر.</string> <string name="about_summary">التبعيات مفتوحة المصدر.</string>

View File

@ -35,10 +35,6 @@
<string name="archive">Archiv</string> <string name="archive">Archiv</string>
<string name="empty_trash">Papierkorb leeren</string> <string name="empty_trash">Papierkorb leeren</string>
<string name="stream_number">Stream %d</string> <string name="stream_number">Stream %d</string>
<string name="trusted_node">Vertrauenswürdiger Knoten</string>
<string name="trusted_node_summary">Diese Adresse wird für die Synchronisation verwendet</string>
<string name="sync_timeout">Zeitbeschränkung der Synchronisierung</string>
<string name="sync_timeout_summary">Timeout in Sekunden</string>
<string name="write_message">Schreiben</string> <string name="write_message">Schreiben</string>
<string name="full_node">Aktiver Knoten</string> <string name="full_node">Aktiver Knoten</string>
<string name="send">Senden</string> <string name="send">Senden</string>
@ -47,12 +43,9 @@
<string name="proof_of_work_title">Proof of Work</string> <string name="proof_of_work_title">Proof of Work</string>
<string name="proof_of_work_text_0">Arbeite am Versenden</string> <string name="proof_of_work_text_0">Arbeite am Versenden</string>
<string name="proof_of_work_text_n">Arbeite am Versenden (%1$d in Warteschlange)</string> <string name="proof_of_work_text_n">Arbeite am Versenden (%1$d in Warteschlange)</string>
<string name="error_invalid_sync_port">Ungültiger Port in den Synchronisationseinstellungen: %s</string>
<string name="compose_body_hint">Nachricht schreiben</string> <string name="compose_body_hint">Nachricht schreiben</string>
<string name="contacts_and_subscriptions">Kontakte</string> <string name="contacts_and_subscriptions">Kontakte</string>
<string name="subscribed">Abonniert</string> <string name="subscribed">Abonniert</string>
<string name="server_pow">Server POW</string>
<string name="server_pow_summary">Der vertrauenswürdige Knoten macht den Proof of Work</string>
<string name="full_node_warning">Ein aktiver Bitmessage-Knoten muss viel hoch- und herunterladen, was auf einem mobilen Netzwerk teuer sein kann. Soll tatsächlich ein aktiver Knoten gestartet werden?</string> <string name="full_node_warning">Ein aktiver Bitmessage-Knoten muss viel hoch- und herunterladen, was auf einem mobilen Netzwerk teuer sein kann. Soll tatsächlich ein aktiver Knoten gestartet werden?</string>
<string name="about">Über Abit</string> <string name="about">Über Abit</string>
<string name="about_summary">Opensource Abhängigkeiten.</string> <string name="about_summary">Opensource Abhängigkeiten.</string>
@ -139,4 +132,17 @@ Als Alternative kann in den Einstellungen ein vertrauenswürdiger Knoten konfigu
<string name="encoding_extended">erweitert</string> <string name="encoding_extended">erweitert</string>
<string name="emulate_conversations">Konversation erraten</string> <string name="emulate_conversations">Konversation erraten</string>
<string name="emulate_conversations_summary">Benutze Betreff um zu erraten welche Nachrichten zusammengehören. Die Reihenfolge stimmt häufig nicht.</string> <string name="emulate_conversations_summary">Benutze Betreff um zu erraten welche Nachrichten zusammengehören. Die Reihenfolge stimmt häufig nicht.</string>
<string name="online">Online</string>
<string name="ok">OK</string>
<string name="unknown">Unbekannt</string>
<string name="require_charging_summary">Nur verbinden wenn das Gerät geladen wird.</string>
<string name="require_charging">Laden erforderlich</string>
<string name="emulate_conversations_batch">Bestehende Nachrichten werden nach Betreff gruppiert</string>
<string name="emulate_conversations_initialize">Bestehende Nachrichten nach Betreff gruppieren</string>
<string name="preference_group_advanced">Erweitert</string>
<string name="preference_group_network_and_performance">Netzwerk &amp; Performanz</string>
<string name="preference_group_network_and_performance_summary">Feineinstellungen für Netzwerk und Protokoll-Details</string>
<string name="preference_group_user_experience">Verhalten</string>
<string name="preference_group_user_experience_summary">Ändern, wie Nachrichten dargestellt werden</string>
<string name="bitmessage_service_description">Hält die Verbindung zum Bitmessage-Netzwerk.</string>
</resources> </resources>

View File

@ -36,9 +36,6 @@
<string name="mark_unread">Marquer non lu</string> <string name="mark_unread">Marquer non lu</string>
<string name="archive">Archive</string> <string name="archive">Archive</string>
<string name="stream_number">"Flux nº%d"</string> <string name="stream_number">"Flux nº%d"</string>
<string name="trusted_node">Nœud de confiance</string>
<string name="trusted_node_summary">Cette adresse est utilisée pour la synchronisation</string>
<string name="sync_timeout">Limitation du temps de synchronisation</string>
<string name="write_message">Écrire un message</string> <string name="write_message">Écrire un message</string>
<string name="full_node">Nœud actif</string> <string name="full_node">Nœud actif</string>
<string name="send">Transmission</string> <string name="send">Transmission</string>
@ -47,7 +44,6 @@
<string name="proof_of_work_title">Preuve de travail</string> <string name="proof_of_work_title">Preuve de travail</string>
<string name="proof_of_work_text_0">Faire du travail pour envoyer la message</string> <string name="proof_of_work_text_0">Faire du travail pour envoyer la message</string>
<string name="proof_of_work_text_n" tools:ignore="PluralsCandidate">Faire du travail pour envoyer la message (%1$d en file d\'attente)</string> <string name="proof_of_work_text_n" tools:ignore="PluralsCandidate">Faire du travail pour envoyer la message (%1$d en file d\'attente)</string>
<string name="error_invalid_sync_port">Port non valide dans les paramètres de synchronisation : %s</string>
<string name="compose_body_hint">Écrire un message</string> <string name="compose_body_hint">Écrire un message</string>
<string name="contacts_and_subscriptions">Contacts</string> <string name="contacts_and_subscriptions">Contacts</string>
<string name="subscribed">Souscrit</string> <string name="subscribed">Souscrit</string>
@ -94,9 +90,6 @@
<string name="use_mobile_network">Utiliser le réseau mobile</string> <string name="use_mobile_network">Utiliser le réseau mobile</string>
<string name="personal_message">Message</string> <string name="personal_message">Message</string>
<string name="empty_trash">Vider les ordures</string> <string name="empty_trash">Vider les ordures</string>
<string name="sync_timeout_summary">Délai d\'expiration en secondes</string>
<string name="server_pow">POW sur le serveur</string>
<string name="server_pow_summary">Le nœud de confiance fait la preuve de travail</string>
<string name="share">Partager</string> <string name="share">Partager</string>
<string name="compose_message">Composer</string> <string name="compose_message">Composer</string>
<string name="no_identity_warning">Veuillez réessayer dès qu\'une identité est disponible.</string> <string name="no_identity_warning">Veuillez réessayer dès qu\'une identité est disponible.</string>
@ -134,4 +127,9 @@
<string name="broadcasts">Diffusions</string> <string name="broadcasts">Diffusions</string>
<string name="encoding_simple">simple</string> <string name="encoding_simple">simple</string>
<string name="encoding_extended">étendu</string> <string name="encoding_extended">étendu</string>
<string name="online">En ligne</string>
<string name="unknown">Inconnue</string>
<string name="require_charging">Exigence d\'une charge</string>
<string name="require_charging_summary">Connecter uniquement lorsque l\'appareil est branché.</string>
<string name="bitmessage_service_description">Garde la connexion au réseau bitmessage.</string>
</resources> </resources>

View File

@ -153,4 +153,7 @@ As an alternative you could configure a trusted node in the settings, but as of
<string name="require_charging_summary">Only connect when device is plugged in</string> <string name="require_charging_summary">Only connect when device is plugged in</string>
<string name="unknown">Unknown</string> <string name="unknown">Unknown</string>
<string name="ok">OK</string> <string name="ok">OK</string>
<string name="online">Online</string>
<string name="warning_low_memory">Low memory!</string>
<string name="bitmessage_service_description">Keeps the connection to the bitmessage network.</string>
</resources> </resources>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="ch.dissem.bitmessage"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:smallIcon="@mipmap/ic_launcher" />

View File

@ -63,29 +63,6 @@
android:summary="@string/import_data_summary" android:summary="@string/import_data_summary"
android:title="@string/import_data" /> android:title="@string/import_data" />
<PreferenceScreen
android:key="preference_experimental"
android:title="@string/preference_group_experimental"
android:summary="@string/preference_group_experimental_summary"
android:persistent="false">
<EditTextPreference
android:inputType="textUri"
android:key="trusted_node"
android:summary="@string/trusted_node_summary"
android:title="@string/trusted_node" />
<EditTextPreference
android:defaultValue="120"
android:inputType="number"
android:key="sync_timeout"
android:summary="@string/sync_timeout_summary"
android:title="@string/sync_timeout" />
<SwitchPreferenceCompat
android:defaultValue="false"
android:dependency="trusted_node"
android:key="server_pow"
android:summary="@string/server_pow_summary"
android:title="@string/server_pow" />
<Preference <Preference
android:key="status" android:key="status"
android:summary="@string/status_summary" android:summary="@string/status_summary"
@ -93,8 +70,6 @@
</PreferenceScreen> </PreferenceScreen>
</PreferenceScreen>
<Preference <Preference
android:key="about" android:key="about"
android:summary="@string/about_summary" android:summary="@string/about_summary"

View File

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<sync-swipeableMessageAdapter
xmlns:android="http://schemas.android.com/apk/res/android"
android:contentAuthority="ch.dissem.apps.abit.provider"
android:accountType="ch.dissem.bitmessage"
android:userVisible="true"
android:supportsUploading="true"
android:allowParallelSyncs="false"
android:isAlwaysSyncable="true"/>

View File

@ -110,7 +110,7 @@ open class TestBase {
} }
fun loadIdentity(address: String): BitmessageAddress { fun loadIdentity(address: String): BitmessageAddress {
val privateKey = PrivateKey.read(getResource(address + ".privkey")) val privateKey = PrivateKey.read(getResource("$address.privkey"))
val identity = BitmessageAddress(privateKey) val identity = BitmessageAddress(privateKey)
Assert.assertEquals(address, identity.address) Assert.assertEquals(address, identity.address)
return identity return identity