diff --git a/app/build.gradle b/app/build.gradle index aa7ff0c..72bea9f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -50,7 +50,7 @@ android { } //ext.jabitVersion = '2.0.4' -ext.jabitVersion = 'development-SNAPSHOT' +ext.jabitVersion = 'feature-refactoring-SNAPSHOT' ext.supportVersion = '27.0.1' dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) diff --git a/app/src/main/java/ch/dissem/apps/abit/SettingsFragment.kt b/app/src/main/java/ch/dissem/apps/abit/SettingsFragment.kt index c7dcbbc..0cc5dca 100644 --- a/app/src/main/java/ch/dissem/apps/abit/SettingsFragment.kt +++ b/app/src/main/java/ch/dissem/apps/abit/SettingsFragment.kt @@ -47,86 +47,93 @@ class SettingsFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedP override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { addPreferencesFromResource(R.xml.preferences) - findPreference("about")?.onPreferenceClickListener = Preference.OnPreferenceClickListener { - (activity as? MainActivity)?.let { activity -> - 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)) - if (activity.hasDetailPane) { - activity.setDetailView(libsBuilder.supportFragment()) - } else { - libsBuilder.start(activity) - } - } - return@OnPreferenceClickListener true - } + findPreference("about")?.onPreferenceClickListener = aboutClickListener() val cleanup = findPreference("cleanup") - cleanup?.onPreferenceClickListener = Preference.OnPreferenceClickListener { - val ctx = activity?.applicationContext ?: throw IllegalStateException("Context not available") - cleanup.isEnabled = false - Toast.makeText(ctx, R.string.cleanup_notification_start, Toast.LENGTH_SHORT).show() + cleanup?.onPreferenceClickListener = cleanupClickListener(cleanup) + findPreference("export")?.onPreferenceClickListener = exportClickListener() + findPreference("import")?.onPreferenceClickListener = importClickListener() + findPreference("status").onPreferenceClickListener = statusClickListener() + } - doAsync { - val bmc = Singleton.getBitmessageContext(ctx) - bmc.internals.nodeRegistry.clear() - bmc.cleanup() - 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 ctx = context ?: throw IllegalStateException("No context available") - - indeterminateProgressDialog(R.string.export_data_summary, R.string.export_data).apply { - doAsync { - val exportDirectory = Preferences.getExportDirectory(ctx) - exportDirectory.mkdirs() - val file = Exports.exportData(exportDirectory, ctx) - val contentUri = getUriForFile(ctx, "ch.dissem.apps.abit.fileprovider", file) - 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, ""), WRITE_EXPORT_REQUEST_CODE) - uiThread { - dismiss() - } - } - } - return@OnPreferenceClickListener true - } - - findPreference("import")?.onPreferenceClickListener = Preference.OnPreferenceClickListener { - val intent = Intent(Intent.ACTION_OPEN_DOCUMENT) - intent.addCategory(Intent.CATEGORY_OPENABLE) - intent.type = "application/zip" - - startActivityForResult(intent, READ_IMPORT_REQUEST_CODE) - return@OnPreferenceClickListener true - } - - findPreference("status").onPreferenceClickListener = Preference.OnPreferenceClickListener { - val activity = activity as MainActivity + private fun aboutClickListener() = Preference.OnPreferenceClickListener { + (activity as? MainActivity)?.let { activity -> + 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)) if (activity.hasDetailPane) { - activity.setDetailView(StatusFragment()) + activity.setDetailView(libsBuilder.supportFragment()) } else { - startActivity() + libsBuilder.start(activity) } - return@OnPreferenceClickListener true } + return@OnPreferenceClickListener true + } + + private fun cleanupClickListener(cleanup: Preference) = Preference.OnPreferenceClickListener { + val ctx = activity?.applicationContext ?: throw IllegalStateException("Context not available") + cleanup.isEnabled = false + Toast.makeText(ctx, R.string.cleanup_notification_start, Toast.LENGTH_SHORT).show() + + doAsync { + val bmc = Singleton.getBitmessageContext(ctx) + bmc.internals.nodeRegistry.clear() + bmc.cleanup() + Preferences.cleanupExportDirectory(ctx) + + uiThread { + Toast.makeText( + ctx, + R.string.cleanup_notification_end, + Toast.LENGTH_LONG + ).show() + cleanup.isEnabled = true + } + } + return@OnPreferenceClickListener true + } + + private fun exportClickListener() = Preference.OnPreferenceClickListener { + val ctx = context ?: throw IllegalStateException("No context available") + + indeterminateProgressDialog(R.string.export_data_summary, R.string.export_data).apply { + doAsync { + val exportDirectory = Preferences.getExportDirectory(ctx) + exportDirectory.mkdirs() + val file = Exports.exportData(exportDirectory, ctx) + val contentUri = getUriForFile(ctx, "ch.dissem.apps.abit.fileprovider", file) + 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, ""), WRITE_EXPORT_REQUEST_CODE) + uiThread { + dismiss() + } + } + } + return@OnPreferenceClickListener true + } + + private fun importClickListener() = Preference.OnPreferenceClickListener { + val intent = Intent(Intent.ACTION_OPEN_DOCUMENT) + intent.addCategory(Intent.CATEGORY_OPENABLE) + intent.type = "application/zip" + + startActivityForResult(intent, READ_IMPORT_REQUEST_CODE) + return@OnPreferenceClickListener true + } + + private fun statusClickListener() = Preference.OnPreferenceClickListener { + val activity = activity as MainActivity + if (activity.hasDetailPane) { + activity.setDetailView(StatusFragment()) + } else { + startActivity() + } + return@OnPreferenceClickListener true } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { diff --git a/app/src/main/java/ch/dissem/apps/abit/repository/AndroidAddressRepository.kt b/app/src/main/java/ch/dissem/apps/abit/repository/AndroidAddressRepository.kt index fb843d5..f5d4054 100644 --- a/app/src/main/java/ch/dissem/apps/abit/repository/AndroidAddressRepository.kt +++ b/app/src/main/java/ch/dissem/apps/abit/repository/AndroidAddressRepository.kt @@ -35,19 +35,12 @@ import java.util.* */ class AndroidAddressRepository(private val sql: SqlHelper) : AddressRepository { - override fun findContact(ripeOrTag: ByteArray): BitmessageAddress? { - for (address in find("public_key is null")) { - if (address.version > 3) { - if (Arrays.equals(ripeOrTag, address.tag)) return address - } else { - if (Arrays.equals(ripeOrTag, address.ripe)) return address - } - } - return null - } + override fun findContact(ripeOrTag: ByteArray): BitmessageAddress? = findByRipeOrTag("public_key is null", ripeOrTag) - override fun findIdentity(ripeOrTag: ByteArray): BitmessageAddress? { - for (address in find("private_key is not null")) { + override fun findIdentity(ripeOrTag: ByteArray): BitmessageAddress? = findByRipeOrTag("private_key is not null", ripeOrTag) + + private fun findByRipeOrTag(where: String, ripeOrTag: ByteArray): BitmessageAddress? { + for (address in find(where)) { if (address.version > 3) { if (Arrays.equals(ripeOrTag, address.tag)) return address } else { @@ -126,24 +119,27 @@ class AndroidAddressRepository(private val sql: SqlHelper) : AddressRepository { } private fun getAddress(c: Cursor): BitmessageAddress { - val address: BitmessageAddress val privateKeyBytes = c.getBlob(c.getColumnIndex(COLUMN_PRIVATE_KEY)) - if (privateKeyBytes != null) { - address = BitmessageAddress(PrivateKey.read(ByteArrayInputStream(privateKeyBytes))) - } else { - address = BitmessageAddress(c.getString(c.getColumnIndex(COLUMN_ADDRESS))) - c.getBlob(c.getColumnIndex(COLUMN_PUBLIC_KEY))?.let { publicKeyBytes -> - val pubkey = Factory.readPubkey(address.version, address.stream, - ByteArrayInputStream(publicKeyBytes), publicKeyBytes.size, - false) - address.pubkey = if (address.version == 4L && pubkey is V3Pubkey) { - V4Pubkey(pubkey) - } else { - pubkey + val address = privateKeyBytes?.let { + BitmessageAddress(PrivateKey.read(ByteArrayInputStream(privateKeyBytes))) + } ?: + BitmessageAddress(c.getString(c.getColumnIndex(COLUMN_ADDRESS))).also { address -> + c.getBlob(c.getColumnIndex(COLUMN_PUBLIC_KEY))?.let { publicKeyBytes -> + Factory.readPubkey( + version = address.version, stream = address.stream, + input = ByteArrayInputStream(publicKeyBytes), length = publicKeyBytes.size, + encrypted = false + ).let { + address.pubkey = if (address.version == 4L && it is V3Pubkey) { + V4Pubkey(it) + } else { + it + } + } } } - } + address.alias = c.getString(c.getColumnIndex(COLUMN_ALIAS)) address.isChan = c.getInt(c.getColumnIndex(COLUMN_CHAN)) == 1 address.isSubscribed = c.getInt(c.getColumnIndex(COLUMN_SUBSCRIBED)) == 1 @@ -171,18 +167,7 @@ class AndroidAddressRepository(private val sql: SqlHelper) : AddressRepository { private fun update(address: BitmessageAddress) { val db = sql.writableDatabase // Create a new map of values, where column names are the keys - val values = ContentValues() - address.alias?.let { values.put(COLUMN_ALIAS, it) } - address.pubkey?.let { pubkey -> - val out = ByteArrayOutputStream() - pubkey.writeUnencrypted(out) - values.put(COLUMN_PUBLIC_KEY, out.toByteArray()) - } - address.privateKey?.let { values.put(COLUMN_PRIVATE_KEY, Encode.bytes(it)) } - if (address.isChan) { - values.put(COLUMN_CHAN, true) - } - values.put(COLUMN_SUBSCRIBED, address.isSubscribed) + val values = getContentValues(address) val update = db.update(TABLE_NAME, values, "address=?", arrayOf(address.address)) if (update < 0) { @@ -193,20 +178,10 @@ class AndroidAddressRepository(private val sql: SqlHelper) : AddressRepository { private fun insert(address: BitmessageAddress) { val db = sql.writableDatabase // Create a new map of values, where column names are the keys - val values = ContentValues() + val values = getContentValues(address) values.put(COLUMN_ADDRESS, address.address) values.put(COLUMN_VERSION, address.version) - values.put(COLUMN_ALIAS, address.alias) - address.pubkey?.let { pubkey -> - val out = ByteArrayOutputStream() - pubkey.writeUnencrypted(out) - values.put(COLUMN_PUBLIC_KEY, out.toByteArray()) - } ?: { - values.put(COLUMN_PUBLIC_KEY, null as ByteArray?) - }.invoke() - address.privateKey?.let { values.put(COLUMN_PRIVATE_KEY, Encode.bytes(it)) } values.put(COLUMN_CHAN, address.isChan) - values.put(COLUMN_SUBSCRIBED, address.isSubscribed) val insert = db.insert(TABLE_NAME, null, values) if (insert < 0) { @@ -214,6 +189,22 @@ class AndroidAddressRepository(private val sql: SqlHelper) : AddressRepository { } } + private fun getContentValues(address: BitmessageAddress): ContentValues { + val values = ContentValues() + address.alias?.let { values.put(COLUMN_ALIAS, it) } + address.pubkey?.let { pubkey -> + val out = ByteArrayOutputStream() + pubkey.writer().writeUnencrypted(out) + values.put(COLUMN_PUBLIC_KEY, out.toByteArray()) + } + address.privateKey?.let { values.put(COLUMN_PRIVATE_KEY, Encode.bytes(it)) } + if (address.isChan) { + values.put(COLUMN_CHAN, true) + } + values.put(COLUMN_SUBSCRIBED, address.isSubscribed) + return values + } + override fun remove(address: BitmessageAddress) { val db = sql.writableDatabase db.delete(TABLE_NAME, "address = ?", arrayOf(address.address)) diff --git a/app/src/main/java/ch/dissem/apps/abit/util/Drawables.kt b/app/src/main/java/ch/dissem/apps/abit/util/Drawables.kt index a7c26e8..1aa4863 100644 --- a/app/src/main/java/ch/dissem/apps/abit/util/Drawables.kt +++ b/app/src/main/java/ch/dissem/apps/abit/util/Drawables.kt @@ -73,7 +73,7 @@ object Drawables { address.pubkey?.apply { link.append(if (address.alias == null) '?' else '&') val pubkey = ByteArrayOutputStream() - writeUnencrypted(pubkey) + writer().writeUnencrypted(pubkey) link.append("pubkey=").append(Base64.encodeToString(pubkey.toByteArray(), URL_SAFE or NO_WRAP)) }