🔥 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:
parent
87bc01701c
commit
3767d976c8
@ -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" />
|
||||||
|
|
||||||
|
@ -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) {
|
||||||
|
@ -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.
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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) }
|
|
||||||
}
|
|
@ -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) {
|
||||||
|
@ -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())
|
||||||
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
|
||||||
}
|
}
|
@ -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()
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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")
|
|
||||||
}
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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()
|
|
||||||
}
|
|
||||||
}
|
|
@ -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"
|
||||||
|
@ -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())
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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>
|
|
@ -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>
|
||||||
|
@ -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 & 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>
|
||||||
|
@ -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>
|
||||||
|
@ -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>
|
||||||
|
@ -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" />
|
|
@ -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"
|
||||||
|
@ -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"/>
|
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user