Exports, some Kotlin stuff, and version 1.0-beta15

This commit is contained in:
Christian Basler 2017-08-03 00:00:23 +02:00
parent 898c49802b
commit e79bfdb244
16 changed files with 440 additions and 344 deletions

View File

@ -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"

View File

@ -134,6 +134,17 @@
android:exported="false"
android:syncable="true"/>
<!-- Exports -->
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="ch.dissem.apps.abit.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
<service
android:name=".synchronization.AuthenticatorService"
android:exported="true"

View File

@ -1,160 +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;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceFragmentCompat;
import android.view.View;
import android.widget.Toast;
import com.mikepenz.aboutlibraries.Libs;
import com.mikepenz.aboutlibraries.LibsBuilder;
import ch.dissem.apps.abit.service.Singleton;
import ch.dissem.apps.abit.synchronization.SyncAdapter;
import ch.dissem.bitmessage.BitmessageContext;
import static ch.dissem.apps.abit.util.Constants.PREFERENCE_SERVER_POW;
import static ch.dissem.apps.abit.util.Constants.PREFERENCE_TRUSTED_NODE;
/**
* @author Christian Basler
*/
public class SettingsFragment
extends PreferenceFragmentCompat
implements SharedPreferences.OnSharedPreferenceChangeListener {
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
addPreferencesFromResource(R.xml.preferences);
Preference about = findPreference("about");
about.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
LibsBuilder libsBuilder = new LibsBuilder()
.withActivityTitle(getActivity().getString(R.string.about))
.withActivityStyle(Libs.ActivityStyle.LIGHT_DARK_TOOLBAR)
.withAboutIconShown(true)
.withAboutVersionShown(true)
.withAboutDescription(getString(R.string.about_app));
MainActivity activity = (MainActivity) getActivity();
if (activity.hasDetailPane()) {
activity.setDetailView(libsBuilder.supportFragment());
} else {
libsBuilder.start(getActivity());
}
return true;
}
});
final Preference cleanup = findPreference("cleanup");
cleanup.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
new AsyncTask<Void, Void, Void>() {
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;
}
}
}
}

View File

@ -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)
}
}
}
}
}
}

View File

@ -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;
}
}

View File

@ -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
}
}

View File

@ -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<PowItem> 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);
}
}
}
});
}
}

View File

@ -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<PowItem>()
private var calculating: Boolean = false
}
}

View File

@ -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");

View File

@ -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++
}
}

View File

@ -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)
}
}
}
}
}

View File

@ -119,4 +119,6 @@ Als Alternative kann in den Einstellungen ein vertrauenswürdiger Knoten konfigu
<string name="cleanup_summary">Veraltete Inventareinträge werden entfernt</string>
<string name="wait_for_wifi">Auf W-LAN warten</string>
<string name="error_msg_recipient_missing">Bitte Empfänger angeben</string>
<string name="export_data">Export</string>
<string name="export_data_summary">Alle Nachrichten und Kontakte exportieren (aber keine Identitäten)</string>
</resources>

View File

@ -118,4 +118,6 @@ As an alternative you could configure a trusted node in the settings, but as of
<string name="cleanup_notification_end">Cleanup finished</string>
<string name="wait_for_wifi">Wait for Wi-Fi</string>
<string name="error_msg_recipient_missing">Please set a recipient</string>
<string name="export_data">Export</string>
<string name="export_data_summary">Export all messages and contacts (but not identities)</string>
</resources>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<files-path name="exports" path="exports/"/>
</paths>

View File

@ -41,6 +41,11 @@
android:title="@string/cleanup"
android:summary="@string/cleanup_summary"
/>
<Preference
android:key="export"
android:title="@string/export_data"
android:summary="@string/export_data_summary"
/>
<Preference
android:key="status"
android:summary="@string/status_summary"

View File

@ -6,6 +6,7 @@ configurations.all {
buildscript {
ext.kotlin_version = '1.1.3-2'
ext.anko_version = '0.10.1'
repositories {
jcenter()
}