diff --git a/app/build.gradle b/app/build.gradle
index 0bd40c7..7c77450 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -1,5 +1,6 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
apply plugin: 'idea'
ext {
@@ -19,8 +20,8 @@ android {
applicationId "ch.dissem.apps." + appName.toLowerCase()
minSdkVersion 19
targetSdkVersion 25
- versionCode 14
- versionName "1.0-beta14"
+ versionCode 15
+ versionName "1.0-beta15"
multiDexEnabled true
}
compileOptions {
@@ -43,6 +44,7 @@ ext.supportVersion = '25.3.1'
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
+ compile "org.jetbrains.anko:anko:$anko_version"
compile "com.android.support:appcompat-v7:$supportVersion"
compile "com.android.support:preference-v7:$supportVersion"
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index fb086cc..e9e5a28 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -134,6 +134,17 @@
android:exported="false"
android:syncable="true"/>
+
+
+
+
+
() {
- private Context ctx = getActivity().getApplicationContext();
-
- @Override
- protected void onPreExecute() {
- cleanup.setEnabled(false);
- Toast.makeText(ctx, R.string.cleanup_notification_start, Toast
- .LENGTH_SHORT).show();
- }
-
- @Override
- protected Void doInBackground(Void... voids) {
- BitmessageContext bmc = Singleton.getBitmessageContext(ctx);
- bmc.cleanup();
- bmc.internals().getNodeRegistry().clear();
- return null;
- }
-
- @Override
- protected void onPostExecute(Void aVoid) {
- Toast.makeText(
- ctx,
- R.string.cleanup_notification_end,
- Toast.LENGTH_LONG
- ).show();
- cleanup.setEnabled(true);
- }
- }.execute();
- return true;
- }
- });
-
- Preference status = findPreference("status");
- status.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
- @Override
- public boolean onPreferenceClick(Preference preference) {
- MainActivity activity = (MainActivity) getActivity();
- if (activity.hasDetailPane()) {
- activity.setDetailView(new StatusFragment());
- } else {
- startActivity(new Intent(getActivity(), StatusActivity.class));
- }
- return true;
- }
- });
- }
-
- @Override
- public void onAttach(Context ctx) {
- super.onAttach(ctx);
- if (ctx instanceof MainActivity){
- ((MainActivity) ctx).getFloatingActionButton().hide();
- }
- PreferenceManager.getDefaultSharedPreferences(ctx)
- .registerOnSharedPreferenceChangeListener(this);
-
- if (ctx instanceof MainActivity) {
- ((MainActivity) ctx).updateTitle(getString(R.string.settings));
- }
- }
-
- @Override
- public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
- switch (key) {
- case PREFERENCE_TRUSTED_NODE: {
- String node = sharedPreferences.getString(PREFERENCE_TRUSTED_NODE, null);
- if (node != null) {
- SyncAdapter.startSync(getActivity());
- } else {
- SyncAdapter.stopSync(getActivity());
- }
- break;
- }
- case PREFERENCE_SERVER_POW: {
- String node = sharedPreferences.getString(PREFERENCE_TRUSTED_NODE, null);
- if (node != null) {
- if (sharedPreferences.getBoolean(PREFERENCE_SERVER_POW, false)) {
- SyncAdapter.startPowSync(getActivity());
- } else {
- SyncAdapter.stopPowSync(getActivity());
- }
- }
- break;
- }
- }
- }
-}
diff --git a/app/src/main/java/ch/dissem/apps/abit/SettingsFragment.kt b/app/src/main/java/ch/dissem/apps/abit/SettingsFragment.kt
new file mode 100644
index 0000000..63b090e
--- /dev/null
+++ b/app/src/main/java/ch/dissem/apps/abit/SettingsFragment.kt
@@ -0,0 +1,185 @@
+/*
+ * 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
+
+import android.content.Context
+import android.content.Intent
+import android.content.SharedPreferences
+import android.os.Bundle
+import android.preference.PreferenceManager
+import android.support.v4.content.FileProvider.getUriForFile
+import android.support.v7.preference.Preference
+import android.support.v7.preference.PreferenceFragmentCompat
+import android.widget.Toast
+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.Preferences
+import ch.dissem.bitmessage.exports.ContactExport
+import ch.dissem.bitmessage.exports.MessageExport
+import ch.dissem.bitmessage.utils.UnixTime
+import com.mikepenz.aboutlibraries.Libs
+import com.mikepenz.aboutlibraries.LibsBuilder
+import org.jetbrains.anko.doAsync
+import org.jetbrains.anko.support.v4.indeterminateProgressDialog
+import org.jetbrains.anko.uiThread
+import org.slf4j.LoggerFactory
+import java.io.File
+import java.io.FileOutputStream
+import java.util.zip.ZipEntry
+import java.util.zip.ZipOutputStream
+
+
+/**
+ * @author Christian Basler
+ */
+class SettingsFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedPreferenceChangeListener {
+
+ override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
+ addPreferencesFromResource(R.xml.preferences)
+
+ findPreference("about")?.onPreferenceClickListener = Preference.OnPreferenceClickListener {
+ val libsBuilder = LibsBuilder()
+ .withActivityTitle(activity.getString(R.string.about))
+ .withActivityStyle(Libs.ActivityStyle.LIGHT_DARK_TOOLBAR)
+ .withAboutIconShown(true)
+ .withAboutVersionShown(true)
+ .withAboutDescription(getString(R.string.about_app))
+ val activity = activity as MainActivity
+ if (activity.hasDetailPane()) {
+ activity.setDetailView(libsBuilder.supportFragment())
+ } else {
+ libsBuilder.start(getActivity())
+ }
+ return@OnPreferenceClickListener true
+ }
+ val cleanup = findPreference("cleanup")
+ cleanup?.onPreferenceClickListener = Preference.OnPreferenceClickListener {
+ val ctx = activity.applicationContext
+ cleanup.isEnabled = false
+ Toast.makeText(ctx, R.string.cleanup_notification_start, Toast
+ .LENGTH_SHORT).show()
+
+ doAsync {
+ val bmc = Singleton.getBitmessageContext(ctx)
+ bmc.cleanup()
+ bmc.internals.nodeRegistry.clear()
+ Preferences.cleanupExportDirectory(ctx)
+
+ uiThread {
+ Toast.makeText(
+ ctx,
+ R.string.cleanup_notification_end,
+ Toast.LENGTH_LONG
+ ).show()
+ cleanup.isEnabled = true
+ }
+ }
+ return@OnPreferenceClickListener true
+ }
+
+ findPreference("export")?.onPreferenceClickListener = Preference.OnPreferenceClickListener {
+ val dialog = indeterminateProgressDialog(R.string.export_data_summary, R.string.export_data)
+ doAsync {
+ val exportDirectory = Preferences.getExportDirectory(context)
+ exportDirectory.mkdirs()
+ val temp = File(exportDirectory, "export-${UnixTime.now}.zip")
+ ZipOutputStream(FileOutputStream(temp)).use { zip ->
+ zip.putNextEntry(ZipEntry("contacts.json"))
+ val addressRepo = Singleton.getAddressRepository(context)
+ val exportContacts = ContactExport.exportContacts(addressRepo.getContacts())
+ zip.write(
+ exportContacts.toJsonString(true).toByteArray()
+ )
+ zip.closeEntry()
+
+ val messageRepo = Singleton.getMessageRepository(context)
+ zip.putNextEntry(ZipEntry("labels.json"))
+ val exportLabels = MessageExport.exportLabels(messageRepo.getLabels())
+ zip.write(
+ exportLabels.toJsonString(true).toByteArray()
+ )
+ zip.closeEntry()
+ zip.putNextEntry(ZipEntry("messages.json"))
+ val exportMessages = MessageExport.exportMessages(messageRepo.getAllMessages())
+ zip.write(
+ exportMessages.toJsonString(true).toByteArray()
+ )
+ zip.closeEntry()
+ }
+
+ val contentUri = getUriForFile(context, "ch.dissem.apps.abit.fileprovider", temp)
+ val intent = Intent(android.content.Intent.ACTION_SEND)
+ intent.type = "application/zip"
+ intent.putExtra(Intent.EXTRA_SUBJECT, "abit-export.zip")
+ intent.putExtra(Intent.EXTRA_STREAM, contentUri)
+ startActivityForResult(Intent.createChooser(intent, ""), 0)
+ uiThread {
+ dialog.dismiss()
+ }
+ }
+ return@OnPreferenceClickListener true
+ }
+
+ findPreference("status").onPreferenceClickListener = Preference.OnPreferenceClickListener {
+ val activity = activity as MainActivity
+ if (activity.hasDetailPane()) {
+ activity.setDetailView(StatusFragment())
+ } else {
+ startActivity(Intent(getActivity(), StatusActivity::class.java))
+ }
+ return@OnPreferenceClickListener true
+ }
+ }
+
+ override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+ Preferences.cleanupExportDirectory(context)
+ }
+
+ override fun onAttach(ctx: Context?) {
+ super.onAttach(ctx)
+ (ctx as? MainActivity)?.floatingActionButton?.hide()
+ PreferenceManager.getDefaultSharedPreferences(ctx)
+ .registerOnSharedPreferenceChangeListener(this)
+
+ (ctx as? MainActivity)?.updateTitle(getString(R.string.settings))
+ }
+
+ override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
+ when (key) {
+ PREFERENCE_TRUSTED_NODE -> {
+ val node = sharedPreferences.getString(PREFERENCE_TRUSTED_NODE, null)
+ if (node != null) {
+ SyncAdapter.startSync(activity)
+ } else {
+ SyncAdapter.stopSync(activity)
+ }
+ }
+ PREFERENCE_SERVER_POW -> {
+ val node = sharedPreferences.getString(PREFERENCE_TRUSTED_NODE, null)
+ if (node != null) {
+ if (sharedPreferences.getBoolean(PREFERENCE_SERVER_POW, false)) {
+ SyncAdapter.startPowSync(activity)
+ } else {
+ SyncAdapter.stopPowSync(activity)
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/ch/dissem/apps/abit/notification/ProofOfWorkNotification.java b/app/src/main/java/ch/dissem/apps/abit/notification/ProofOfWorkNotification.java
deleted file mode 100644
index 73657e2..0000000
--- a/app/src/main/java/ch/dissem/apps/abit/notification/ProofOfWorkNotification.java
+++ /dev/null
@@ -1,63 +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.notification;
-
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.Intent;
-import android.support.v7.app.NotificationCompat;
-
-import ch.dissem.apps.abit.MainActivity;
-import ch.dissem.apps.abit.R;
-
-/**
- * Ongoing notification while proof of work is in progress.
- */
-public class ProofOfWorkNotification extends AbstractNotification {
- public static final int ONGOING_NOTIFICATION_ID = 3;
-
- public ProofOfWorkNotification(Context ctx) {
- super(ctx);
- update(0);
- }
-
- @Override
- protected int getNotificationId() {
- return ONGOING_NOTIFICATION_ID;
- }
-
- public ProofOfWorkNotification update(int numberOfItems) {
- NotificationCompat.Builder builder = new NotificationCompat.Builder(ctx);
-
- Intent showMessageIntent = new Intent(ctx, MainActivity.class);
- PendingIntent pendingIntent = PendingIntent.getActivity(ctx, 0, showMessageIntent,
- PendingIntent.FLAG_UPDATE_CURRENT);
-
- builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
- .setUsesChronometer(true)
- .setOngoing(true)
- .setSmallIcon(R.drawable.ic_notification_proof_of_work)
- .setContentTitle(ctx.getString(R.string.proof_of_work_title))
- .setContentText(numberOfItems == 0
- ? ctx.getString(R.string.proof_of_work_text_0)
- : ctx.getString(R.string.proof_of_work_text_n, numberOfItems))
- .setContentIntent(pendingIntent);
-
- notification = builder.build();
- return this;
- }
-}
diff --git a/app/src/main/java/ch/dissem/apps/abit/notification/ProofOfWorkNotification.kt b/app/src/main/java/ch/dissem/apps/abit/notification/ProofOfWorkNotification.kt
new file mode 100644
index 0000000..5076097
--- /dev/null
+++ b/app/src/main/java/ch/dissem/apps/abit/notification/ProofOfWorkNotification.kt
@@ -0,0 +1,65 @@
+/*
+ * 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.notification
+
+import android.app.PendingIntent
+import android.content.Context
+import android.content.Intent
+import android.support.v7.app.NotificationCompat
+
+import ch.dissem.apps.abit.MainActivity
+import ch.dissem.apps.abit.R
+
+/**
+ * Ongoing notification while proof of work is in progress.
+ */
+class ProofOfWorkNotification(ctx: Context) : AbstractNotification(ctx) {
+
+ init {
+ update(0)
+ }
+
+ override fun getNotificationId(): Int {
+ return ONGOING_NOTIFICATION_ID
+ }
+
+ fun update(numberOfItems: Int): ProofOfWorkNotification {
+ val builder = NotificationCompat.Builder(ctx)
+
+ val showMessageIntent = Intent(ctx, MainActivity::class.java)
+ val pendingIntent = PendingIntent.getActivity(ctx, 0, showMessageIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT)
+
+ builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
+ .setUsesChronometer(true)
+ .setOngoing(true)
+ .setSmallIcon(R.drawable.ic_notification_proof_of_work)
+ .setContentTitle(ctx.getString(R.string.proof_of_work_title))
+ .setContentText(if (numberOfItems == 0)
+ ctx.getString(R.string.proof_of_work_text_0)
+ else
+ ctx.getString(R.string.proof_of_work_text_n, numberOfItems))
+ .setContentIntent(pendingIntent)
+
+ notification = builder.build()
+ return this
+ }
+
+ companion object {
+ @JvmField val ONGOING_NOTIFICATION_ID = 3
+ }
+}
diff --git a/app/src/main/java/ch/dissem/apps/abit/service/ProofOfWorkService.java b/app/src/main/java/ch/dissem/apps/abit/service/ProofOfWorkService.java
deleted file mode 100644
index e171edf..0000000
--- a/app/src/main/java/ch/dissem/apps/abit/service/ProofOfWorkService.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright 2016 Christian Basler
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package ch.dissem.apps.abit.service;
-
-import android.app.Service;
-import android.content.Intent;
-import android.os.Binder;
-import android.os.IBinder;
-import android.support.annotation.Nullable;
-
-import java.util.LinkedList;
-import java.util.Queue;
-
-import ch.dissem.apps.abit.notification.ProofOfWorkNotification;
-import ch.dissem.bitmessage.ports.MultiThreadedPOWEngine;
-import ch.dissem.bitmessage.ports.ProofOfWorkEngine;
-
-import static ch.dissem.apps.abit.notification.ProofOfWorkNotification.ONGOING_NOTIFICATION_ID;
-
-/**
- * The Proof of Work Service makes sure POW is done in a foreground process, so it shouldn't be
- * killed by the system before the nonce is found.
- */
-public class ProofOfWorkService extends Service {
- // Object to use as a thread-safe lock
- private static final ProofOfWorkEngine engine = new MultiThreadedPOWEngine();
- private static final Queue queue = new LinkedList<>();
- private static boolean calculating;
- private ProofOfWorkNotification notification;
-
- @Override
- public void onCreate() {
- notification = new ProofOfWorkNotification(this);
- }
-
- @Nullable
- @Override
- public IBinder onBind(Intent intent) {
- return new PowBinder(this);
- }
-
- public static class PowBinder extends Binder {
- private final ProofOfWorkService service;
- private final ProofOfWorkNotification notification;
-
- private PowBinder(ProofOfWorkService service) {
- this.service = service;
- this.notification = service.notification;
- }
-
- void process(PowItem item) {
- synchronized (queue) {
- service.startService(new Intent(service, ProofOfWorkService.class));
- service.startForeground(ONGOING_NOTIFICATION_ID,
- notification.getNotification());
- if (!calculating) {
- calculating = true;
- service.calculateNonce(item);
- } else {
- queue.add(item);
- notification.update(queue.size()).show();
- }
- }
- }
- }
-
-
- static class PowItem {
- private final byte[] initialHash;
- private final byte[] targetValue;
- private final ProofOfWorkEngine.Callback callback;
-
- PowItem(byte[] initialHash, byte[] targetValue, ProofOfWorkEngine.Callback callback) {
- this.initialHash = initialHash;
- this.targetValue = targetValue;
- this.callback = callback;
- }
- }
-
- private void calculateNonce(final PowItem item) {
- engine.calculateNonce(item.initialHash, item.targetValue, new ProofOfWorkEngine.Callback() {
- @Override
- public void onNonceCalculated(byte[] initialHash, byte[] nonce) {
- try {
- item.callback.onNonceCalculated(initialHash, nonce);
- } finally {
- PowItem next;
- synchronized (queue) {
- next = queue.poll();
- if (next == null) {
- calculating = false;
- stopForeground(true);
- stopSelf();
- } else {
- notification.update(queue.size()).show();
- }
- }
- if (next != null) {
- calculateNonce(next);
- }
- }
- }
- });
- }
-}
diff --git a/app/src/main/java/ch/dissem/apps/abit/service/ProofOfWorkService.kt b/app/src/main/java/ch/dissem/apps/abit/service/ProofOfWorkService.kt
new file mode 100644
index 0000000..0f3f6c5
--- /dev/null
+++ b/app/src/main/java/ch/dissem/apps/abit/service/ProofOfWorkService.kt
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2016 Christian Basler
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.dissem.apps.abit.service
+
+import android.app.Service
+import android.content.Intent
+import android.os.Binder
+import android.os.IBinder
+import ch.dissem.apps.abit.notification.ProofOfWorkNotification
+import ch.dissem.apps.abit.notification.ProofOfWorkNotification.Companion.ONGOING_NOTIFICATION_ID
+import ch.dissem.apps.abit.util.PowStats
+import ch.dissem.bitmessage.ports.MultiThreadedPOWEngine
+import ch.dissem.bitmessage.ports.ProofOfWorkEngine
+import java.util.*
+
+/**
+ * The Proof of Work Service makes sure POW is done in a foreground process, so it shouldn't be
+ * killed by the system before the nonce is found.
+ */
+class ProofOfWorkService : Service() {
+ private lateinit var notification: ProofOfWorkNotification
+
+ override fun onCreate() {
+ notification = ProofOfWorkNotification(this)
+ }
+
+ override fun onBind(intent: Intent): IBinder? {
+ return PowBinder(this)
+ }
+
+ class PowBinder internal constructor(private val service: ProofOfWorkService) : Binder() {
+ private val notification: ProofOfWorkNotification
+
+ init {
+ this.notification = service.notification
+ }
+
+ fun process(item: PowItem) {
+ synchronized(queue) {
+ service.startService(Intent(service, ProofOfWorkService::class.java))
+ service.startForeground(ONGOING_NOTIFICATION_ID,
+ notification.notification)
+ if (!calculating) {
+ calculating = true
+ service.calculateNonce(item)
+ } else {
+ queue.add(item)
+ notification.update(queue.size).show()
+ }
+ }
+ }
+ }
+
+
+ data class PowItem(val initialHash: ByteArray, val targetValue: ByteArray, val callback: ProofOfWorkEngine.Callback)
+
+ private fun calculateNonce(item: PowItem) {
+ val startTime = System.currentTimeMillis()
+ engine.calculateNonce(item.initialHash, item.targetValue, object : ProofOfWorkEngine.Callback {
+ override fun onNonceCalculated(initialHash: ByteArray, nonce: ByteArray) {
+ val time = System.currentTimeMillis() - startTime
+ PowStats.addPow(this@ProofOfWorkService, time, item.targetValue)
+ try {
+ item.callback.onNonceCalculated(initialHash, nonce)
+ } finally {
+ var next: PowItem? = null
+ synchronized(queue) {
+ next = queue.poll()
+ if (next == null) {
+ calculating = false
+ stopForeground(true)
+ stopSelf()
+ } else {
+ notification.update(queue.size).show()
+ }
+ }
+ next?.let { calculateNonce(it) }
+ }
+ }
+ })
+ }
+
+ companion object {
+ // Object to use as a thread-safe lock
+ private val engine = MultiThreadedPOWEngine()
+ private val queue = LinkedList()
+ private var calculating: Boolean = false
+ }
+}
diff --git a/app/src/main/java/ch/dissem/apps/abit/util/Constants.java b/app/src/main/java/ch/dissem/apps/abit/util/Constants.java
index 66fa847..5180a4d 100644
--- a/app/src/main/java/ch/dissem/apps/abit/util/Constants.java
+++ b/app/src/main/java/ch/dissem/apps/abit/util/Constants.java
@@ -27,6 +27,8 @@ public class Constants {
public static final String PREFERENCE_SYNC_TIMEOUT = "sync_timeout";
public static final String PREFERENCE_SERVER_POW = "server_pow";
public static final String PREFERENCE_FULL_NODE = "full_node";
+ public static final String PREFERENCE_POW_AVERAGE = "average_pow_time_ms";
+ public static final String PREFERENCE_POW_COUNT = "pow_count";
public static final String BITMESSAGE_URL_SCHEMA = "bitmessage:";
public static final Pattern BITMESSAGE_ADDRESS_PATTERN = Pattern.compile("\\bBM-[a-zA-Z0-9]+\\b");
diff --git a/app/src/main/java/ch/dissem/apps/abit/util/PowStats.kt b/app/src/main/java/ch/dissem/apps/abit/util/PowStats.kt
new file mode 100644
index 0000000..25892d4
--- /dev/null
+++ b/app/src/main/java/ch/dissem/apps/abit/util/PowStats.kt
@@ -0,0 +1,31 @@
+package ch.dissem.apps.abit.util
+
+import android.content.Context
+
+/**
+ * Created by chrigu on 02.08.17.
+ */
+object PowStats {
+ var powUnitTime: Long = 0
+ var powCount: Long = 0
+
+ @JvmStatic
+ fun getExpectedPowTime(ctx: Context, target: ByteArray): Long {
+// val preferences = PreferenceManager.getDefaultSharedPreferences(ctx)
+// return preferences.getLong(Constants.PREFERENCE_POW_AVERAGE, 0L)
+ return 0
+ }
+
+// fun updatePowTelemetry(ctx: Context, averagePowTime: Long, powCount: Long) {
+// val preferences = PreferenceManager.getDefaultSharedPreferences(ctx)
+// preferences.edit()
+// .putLong(Constants.PREFERENCE_POW_AVERAGE, averagePowTime)
+// .putLong(Constants.PREFERENCE_POW_COUNT, powCount)
+// .apply()
+// }
+
+ @JvmStatic
+ fun addPow(ctx: Context, time: Long, target: ByteArray) {
+ powCount++
+ }
+}
diff --git a/app/src/main/java/ch/dissem/apps/abit/util/Preferences.kt b/app/src/main/java/ch/dissem/apps/abit/util/Preferences.kt
index 4f03bfe..a37a79e 100644
--- a/app/src/main/java/ch/dissem/apps/abit/util/Preferences.kt
+++ b/app/src/main/java/ch/dissem/apps/abit/util/Preferences.kt
@@ -22,6 +22,8 @@ import ch.dissem.apps.abit.R
import ch.dissem.apps.abit.listener.WifiReceiver
import ch.dissem.apps.abit.notification.ErrorNotification
import ch.dissem.apps.abit.util.Constants.*
+import org.slf4j.LoggerFactory
+import java.io.File
import java.io.IOException
import java.net.InetAddress
@@ -29,6 +31,8 @@ import java.net.InetAddress
* @author Christian Basler
*/
object Preferences {
+ private val LOG = LoggerFactory.getLogger(Preferences::class.java)
+
@JvmStatic
fun useTrustedNode(ctx: Context): Boolean {
val trustedNode = getPreference(ctx, PREFERENCE_TRUSTED_NODE) ?: return false
@@ -112,4 +116,25 @@ object Preferences {
val preferences = PreferenceManager.getDefaultSharedPreferences(ctx)
preferences.edit().putBoolean(PREFERENCE_FULL_NODE, status).apply()
}
+
+ @JvmStatic
+ fun getExportDirectory(ctx: Context): File {
+ return File(ctx.filesDir, "exports")
+ }
+
+ @JvmStatic
+ fun cleanupExportDirectory(ctx: Context) {
+ val exportDirectory = getExportDirectory(ctx)
+ if (exportDirectory.exists()) {
+ exportDirectory.listFiles().forEach { file ->
+ try {
+ if (!file.delete()) {
+ file.deleteOnExit()
+ }
+ } catch (e: Exception) {
+ LOG.debug(e.message, e)
+ }
+ }
+ }
+ }
}
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 2b3e45b..31d36d9 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -119,4 +119,6 @@ Als Alternative kann in den Einstellungen ein vertrauenswürdiger Knoten konfigu
Veraltete Inventareinträge werden entfernt
Auf W-LAN warten
Bitte Empfänger angeben
+ Export
+ Alle Nachrichten und Kontakte exportieren (aber keine Identitäten)
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index cf12e2b..2349c1d 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -118,4 +118,6 @@ As an alternative you could configure a trusted node in the settings, but as of
Cleanup finished
Wait for Wi-Fi
Please set a recipient
+ Export
+ Export all messages and contacts (but not identities)
diff --git a/app/src/main/res/xml/file_paths.xml b/app/src/main/res/xml/file_paths.xml
new file mode 100644
index 0000000..8fee7f3
--- /dev/null
+++ b/app/src/main/res/xml/file_paths.xml
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index 47ca097..c86a5fa 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -41,6 +41,11 @@
android:title="@string/cleanup"
android:summary="@string/cleanup_summary"
/>
+