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" /> +