Exports, some Kotlin stuff, and version 1.0-beta15
This commit is contained in:
parent
898c49802b
commit
e79bfdb244
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
185
app/src/main/java/ch/dissem/apps/abit/SettingsFragment.kt
Normal file
185
app/src/main/java/ch/dissem/apps/abit/SettingsFragment.kt
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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");
|
||||
|
31
app/src/main/java/ch/dissem/apps/abit/util/PowStats.kt
Normal file
31
app/src/main/java/ch/dissem/apps/abit/util/PowStats.kt
Normal 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++
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
4
app/src/main/res/xml/file_paths.xml
Normal file
4
app/src/main/res/xml/file_paths.xml
Normal 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>
|
@ -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"
|
||||
|
@ -6,6 +6,7 @@ configurations.all {
|
||||
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.1.3-2'
|
||||
ext.anko_version = '0.10.1'
|
||||
repositories {
|
||||
jcenter()
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user