Exports, some Kotlin stuff, and version 1.0-beta15
This commit is contained in:
		| @@ -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" | ||||
|   | ||||
		Reference in New Issue
	
	Block a user