From f481914a65d513d8526ffcbffc430cad8eb233b9 Mon Sep 17 00:00:00 2001 From: Christian Basler Date: Tue, 13 Feb 2018 07:24:24 +0100 Subject: [PATCH] Implemented basic draft functionality --- .../apps/abit/ComposeMessageActivity.kt | 1 + .../apps/abit/ComposeMessageFragment.kt | 165 ++++++++++++------ .../java/ch/dissem/apps/abit/MainActivity.kt | 136 ++++++++++----- build.gradle | 2 +- 4 files changed, 201 insertions(+), 103 deletions(-) diff --git a/app/src/main/java/ch/dissem/apps/abit/ComposeMessageActivity.kt b/app/src/main/java/ch/dissem/apps/abit/ComposeMessageActivity.kt index dcea268..618504a 100644 --- a/app/src/main/java/ch/dissem/apps/abit/ComposeMessageActivity.kt +++ b/app/src/main/java/ch/dissem/apps/abit/ComposeMessageActivity.kt @@ -53,6 +53,7 @@ class ComposeMessageActivity : AppCompatActivity() { } companion object { + const val EXTRA_DRAFT = "ch.dissem.abit.Message.DRAFT" const val EXTRA_IDENTITY = "ch.dissem.abit.Message.SENDER" const val EXTRA_RECIPIENT = "ch.dissem.abit.Message.RECIPIENT" const val EXTRA_SUBJECT = "ch.dissem.abit.Message.SUBJECT" diff --git a/app/src/main/java/ch/dissem/apps/abit/ComposeMessageFragment.kt b/app/src/main/java/ch/dissem/apps/abit/ComposeMessageFragment.kt index 75755ba..0c00f48 100644 --- a/app/src/main/java/ch/dissem/apps/abit/ComposeMessageFragment.kt +++ b/app/src/main/java/ch/dissem/apps/abit/ComposeMessageFragment.kt @@ -17,6 +17,7 @@ package ch.dissem.apps.abit import android.app.Activity.RESULT_OK +import android.content.Context import android.content.Intent import android.os.Bundle import android.support.v4.app.Fragment @@ -25,6 +26,7 @@ import android.widget.AdapterView import android.widget.Toast import ch.dissem.apps.abit.ComposeMessageActivity.Companion.EXTRA_BROADCAST import ch.dissem.apps.abit.ComposeMessageActivity.Companion.EXTRA_CONTENT +import ch.dissem.apps.abit.ComposeMessageActivity.Companion.EXTRA_DRAFT import ch.dissem.apps.abit.ComposeMessageActivity.Companion.EXTRA_ENCODING import ch.dissem.apps.abit.ComposeMessageActivity.Companion.EXTRA_IDENTITY import ch.dissem.apps.abit.ComposeMessageActivity.Companion.EXTRA_PARENT @@ -38,6 +40,9 @@ import ch.dissem.bitmessage.entity.BitmessageAddress import ch.dissem.bitmessage.entity.Plaintext import ch.dissem.bitmessage.entity.Plaintext.Type.BROADCAST import ch.dissem.bitmessage.entity.Plaintext.Type.MSG +import ch.dissem.bitmessage.entity.valueobject.ExtendedEncoding +import ch.dissem.bitmessage.entity.valueobject.InventoryVector +import ch.dissem.bitmessage.entity.valueobject.Label import ch.dissem.bitmessage.entity.valueobject.extended.Message import kotlinx.android.synthetic.main.fragment_compose_message.* @@ -52,34 +57,49 @@ class ComposeMessageFragment : Fragment() { private var broadcast: Boolean = false private var encoding: Plaintext.Encoding = Plaintext.Encoding.SIMPLE - private var parent: Plaintext? = null + private val parents = mutableListOf() + + private var draft: Plaintext? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - arguments?.let { arguments -> - var id = arguments.getSerializable(EXTRA_IDENTITY) as? BitmessageAddress - if (context != null && (id == null || id.privateKey == null)) { - id = Singleton.getIdentity(context!!) - } - if (id?.privateKey != null) { - identity = id + arguments?.apply { + val draft = getSerializable(EXTRA_DRAFT) as Plaintext? + if (draft != null) { + this@ComposeMessageFragment.draft = draft + identity = draft.from + recipient = draft.to + subject = draft.subject ?: "" + content = draft.text ?: "" + encoding = draft.encoding ?: Plaintext.Encoding.SIMPLE + parents.addAll(draft.parents) } else { - throw IllegalStateException("No identity set for ComposeMessageFragment") - } - broadcast = arguments.getBoolean(EXTRA_BROADCAST, false) - if (arguments.containsKey(EXTRA_RECIPIENT)) { - recipient = arguments.getSerializable(EXTRA_RECIPIENT) as BitmessageAddress - } - if (arguments.containsKey(EXTRA_SUBJECT)) { - subject = arguments.getString(EXTRA_SUBJECT) - } - if (arguments.containsKey(EXTRA_CONTENT)) { - content = arguments.getString(EXTRA_CONTENT) - } - encoding = arguments.getSerializable(EXTRA_ENCODING) as? Plaintext.Encoding ?: Plaintext.Encoding.SIMPLE + var id = getSerializable(EXTRA_IDENTITY) as? BitmessageAddress + if (context != null && (id == null || id.privateKey == null)) { + id = Singleton.getIdentity(context!!) + } + if (id?.privateKey != null) { + identity = id + } else { + throw IllegalStateException("No identity set for ComposeMessageFragment") + } + broadcast = getBoolean(EXTRA_BROADCAST, false) + if (containsKey(EXTRA_RECIPIENT)) { + recipient = getSerializable(EXTRA_RECIPIENT) as BitmessageAddress + } + if (containsKey(EXTRA_SUBJECT)) { + subject = getString(EXTRA_SUBJECT) + } + if (containsKey(EXTRA_CONTENT)) { + content = getString(EXTRA_CONTENT) + } + encoding = getSerializable(EXTRA_ENCODING) as? Plaintext.Encoding ?: + Plaintext.Encoding.SIMPLE - if (arguments.containsKey(EXTRA_PARENT)) { - parent = arguments.getSerializable(EXTRA_PARENT) as Plaintext + if (containsKey(EXTRA_PARENT)) { + val parent = getSerializable(EXTRA_PARENT) as Plaintext + parent.inventoryVector?.let { parents.add(it) } + } } } ?: { throw IllegalStateException("No identity set for ComposeMessageFragment") @@ -87,9 +107,10 @@ class ComposeMessageFragment : Fragment() { setHasOptionsMenu(true) } - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle?): View = - inflater.inflate(R.layout.fragment_compose_message, container, false) + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View = inflater.inflate(R.layout.fragment_compose_message, container, false) override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -99,13 +120,20 @@ class ComposeMessageFragment : Fragment() { } else { val adapter = ContactAdapter(context!!) recipient_input.setAdapter(adapter) - recipient_input.onItemClickListener = AdapterView.OnItemClickListener { _, _, pos, _ -> adapter.getItem(pos) } + recipient_input.onItemClickListener = + AdapterView.OnItemClickListener { _, _, pos, _ -> adapter.getItem(pos) } recipient_input.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { - override fun onItemSelected(parent: AdapterView<*>, view: View, position: Int, id: Long) { + override fun onItemSelected( + parent: AdapterView<*>, + view: View, + position: Int, + id: Long + ) { recipient = adapter.getItem(position) } - override fun onNothingSelected(parent: AdapterView<*>) = Unit // leave current selection + override fun onNothingSelected(parent: AdapterView<*>) = + Unit // leave current selection } recipient?.let { recipient_input.setText(it.toString()) } } @@ -146,16 +174,15 @@ class ComposeMessageFragment : Fragment() { } } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) = if (requestCode == 0 && data != null && resultCode == RESULT_OK) { - encoding = data.getSerializableExtra(EXTRA_ENCODING) as Plaintext.Encoding - } else { - super.onActivityResult(requestCode, resultCode, data) - } + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) = + if (requestCode == 0 && data != null && resultCode == RESULT_OK) { + encoding = data.getSerializableExtra(EXTRA_ENCODING) as Plaintext.Encoding + } else { + super.onActivityResult(requestCode, resultCode, data) + } - private fun send() { + private fun build(ctx: Context): Plaintext { val builder: Plaintext.Builder - val ctx = activity ?: throw IllegalStateException("Fragment is not attached to an activity") - val bmc = Singleton.getBitmessageContext(ctx) if (broadcast) { builder = Plaintext.Builder(BROADCAST).from(identity) } else { @@ -175,42 +202,68 @@ class ComposeMessageFragment : Fragment() { } } - if (recipient == null) { - Toast.makeText(context, R.string.error_msg_recipient_missing, Toast.LENGTH_LONG).show() - return - } builder = Plaintext.Builder(MSG) - .from(identity) - .to(recipient) + .from(identity) + .to(recipient) } if (!Preferences.requestAcknowledgements(ctx)) { builder.preventAck() } when (encoding) { Plaintext.Encoding.SIMPLE -> builder.message( - subject_input.text.toString(), - body_input.text.toString() + subject_input.text.toString(), + body_input.text.toString() ) Plaintext.Encoding.EXTENDED -> builder.message( - Message.Builder() - .subject(subject_input.text.toString()) - .body(body_input.text.toString()) - .addParent(parent) - .build() + ExtendedEncoding( + Message( + subject = subject_input.text.toString(), + body = body_input.text.toString(), + parents = parents, + files = emptyList() + ) + ) ) else -> { Toast.makeText( - ctx, - ctx.getString(R.string.error_unsupported_encoding, encoding), - Toast.LENGTH_LONG + ctx, + ctx.getString(R.string.error_unsupported_encoding, encoding), + Toast.LENGTH_LONG ).show() builder.message( - subject_input.text.toString(), - body_input.text.toString() + subject_input.text.toString(), + body_input.text.toString() ) } } - bmc.send(builder.build()) + draft?.id?.let { builder.id(it) } + return builder.build() + } + + override fun onPause() { + if (draft?.labels?.any { it.type == Label.Type.DRAFT } != false) { + context?.let { ctx -> + draft = build(ctx).also { msg -> + Singleton.labeler.markAsDraft(msg) + Singleton.getMessageRepository(ctx).save(msg) + } + Toast.makeText(ctx, "Message saved as draft", Toast.LENGTH_LONG).show() + } ?: throw IllegalStateException("Context is not available") + } + super.onPause() + } + + private fun send() { + val ctx = activity ?: throw IllegalStateException("Fragment is not attached to an activity") + if (recipient == null) { + Toast.makeText(ctx, R.string.error_msg_recipient_missing, Toast.LENGTH_LONG) + .show() + return + } + build(ctx).let { message -> + draft = message + Singleton.getBitmessageContext(ctx).send(message) + } ctx.finish() } } diff --git a/app/src/main/java/ch/dissem/apps/abit/MainActivity.kt b/app/src/main/java/ch/dissem/apps/abit/MainActivity.kt index 2f1d3aa..6c77f0f 100644 --- a/app/src/main/java/ch/dissem/apps/abit/MainActivity.kt +++ b/app/src/main/java/ch/dissem/apps/abit/MainActivity.kt @@ -146,12 +146,15 @@ class MainActivity : AppCompatActivity(), ListSelectionListener { SyncAdapter.stopSync(this) } if (drawer.isDrawerOpen) { - val lps = RelativeLayout.LayoutParams(ViewGroup - .LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT) - lps.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM) - lps.addRule(RelativeLayout.ALIGN_PARENT_LEFT) - val margin = ((resources.displayMetrics.density * 12) as Number).toInt() - lps.setMargins(margin, margin, margin, margin) + val lps = RelativeLayout.LayoutParams( + ViewGroup + .LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT + ).apply { + addRule(RelativeLayout.ALIGN_PARENT_BOTTOM) + addRule(RelativeLayout.ALIGN_PARENT_LEFT) + val margin = ((resources.displayMetrics.density * 12) as Number).toInt() + setMargins(margin, margin, margin, margin) + } ShowcaseView.Builder(this) .withMaterialShowcase() @@ -192,19 +195,23 @@ class MainActivity : AppCompatActivity(), ListSelectionListener { private fun createDrawer(toolbar: Toolbar) { val profiles = ArrayList>() - profiles.add(ProfileSettingDrawerItem() - .withName(getString(R.string.add_identity)) - .withDescription(getString(R.string.add_identity_summary)) - .withIcon(IconicsDrawable(this, GoogleMaterial.Icon.gmd_add) - .actionBar() - .paddingDp(5) - .colorRes(R.color.icons)) - .withIdentifier(ADD_IDENTITY.toLong()) + profiles.add( + ProfileSettingDrawerItem() + .withName(getString(R.string.add_identity)) + .withDescription(getString(R.string.add_identity_summary)) + .withIcon( + IconicsDrawable(this, GoogleMaterial.Icon.gmd_add) + .actionBar() + .paddingDp(5) + .colorRes(R.color.icons) + ) + .withIdentifier(ADD_IDENTITY.toLong()) ) - profiles.add(ProfileSettingDrawerItem() - .withName(getString(R.string.manage_identity)) - .withIcon(GoogleMaterial.Icon.gmd_settings) - .withIdentifier(MANAGE_IDENTITY.toLong()) + profiles.add( + ProfileSettingDrawerItem() + .withName(getString(R.string.manage_identity)) + .withIcon(GoogleMaterial.Icon.gmd_settings) + .withIdentifier(MANAGE_IDENTITY.toLong()) ) // Create the AccountHeader accountHeader = AccountHeaderBuilder() @@ -212,26 +219,36 @@ class MainActivity : AppCompatActivity(), ListSelectionListener { .withHeaderBackground(R.drawable.header) .withProfiles(profiles) .withOnAccountHeaderProfileImageListener(ProfileImageListener(this)) - .withOnAccountHeaderListener(ProfileSelectionListener(this@MainActivity, supportFragmentManager)) + .withOnAccountHeaderListener( + ProfileSelectionListener( + this@MainActivity, + supportFragmentManager + ) + ) .build() if (profiles.size > 2) { // There's always the add and manage identity items accountHeader.setActiveProfile(profiles[0], true) } val drawerItems = ArrayList>() - drawerItems.add(PrimaryDrawerItem() - .withIdentifier(LABEL_ARCHIVE.id as Long) - .withName(R.string.archive) - .withTag(LABEL_ARCHIVE) - .withIcon(CommunityMaterial.Icon.cmd_archive) + drawerItems.add( + PrimaryDrawerItem() + .withIdentifier(LABEL_ARCHIVE.id as Long) + .withName(R.string.archive) + .withTag(LABEL_ARCHIVE) + .withIcon(CommunityMaterial.Icon.cmd_archive) ) drawerItems.add(DividerDrawerItem()) - drawerItems.add(PrimaryDrawerItem() - .withName(R.string.contacts_and_subscriptions) - .withIcon(GoogleMaterial.Icon.gmd_contacts)) - drawerItems.add(PrimaryDrawerItem() - .withName(R.string.settings) - .withIcon(GoogleMaterial.Icon.gmd_settings)) + drawerItems.add( + PrimaryDrawerItem() + .withName(R.string.contacts_and_subscriptions) + .withIcon(GoogleMaterial.Icon.gmd_contacts) + ) + drawerItems.add( + PrimaryDrawerItem() + .withName(R.string.settings) + .withIcon(GoogleMaterial.Icon.gmd_settings) + ) nodeSwitch = SwitchDrawerItem() .withIdentifier(ID_NODE_SWITCH) @@ -369,7 +386,8 @@ class MainActivity : AppCompatActivity(), ListSelectionListener { // we know that there are 2 setting elements. // Set the new profile above them ;) accountHeader.addProfile( - newProfile, accountHeader.profiles.size - 2) + newProfile, accountHeader.profiles.size - 2 + ) } else { accountHeader.addProfiles(newProfile) } @@ -435,14 +453,31 @@ class MainActivity : AppCompatActivity(), ListSelectionListener { // In two-pane mode, show the detail view in this activity by // adding or replacing the detail fragment using a // fragment transaction. - val arguments = Bundle() - arguments.putSerializable(MessageDetailFragment.ARG_ITEM, item) val fragment = when (item) { - is Plaintext -> MessageDetailFragment() - is BitmessageAddress -> AddressDetailFragment() + is Plaintext -> { + if (item.labels.any { it.type == Label.Type.DRAFT }) { + ComposeMessageFragment().apply { + arguments = Bundle().apply { + putSerializable(ComposeMessageActivity.EXTRA_DRAFT, item) + } + } + } else { + MessageDetailFragment().apply { + arguments = Bundle().apply { + putSerializable(MessageDetailFragment.ARG_ITEM, item) + } + } + } + } + is BitmessageAddress -> { + AddressDetailFragment().apply { + arguments = Bundle().apply { + putSerializable(AddressDetailFragment.ARG_ITEM, item) + } + } + } else -> throw IllegalArgumentException("Plaintext or BitmessageAddress expected, but was ${item::class.simpleName}") } - fragment.arguments = arguments supportFragmentManager.beginTransaction() .replace(R.id.message_detail_container, fragment) .commit() @@ -451,12 +486,21 @@ class MainActivity : AppCompatActivity(), ListSelectionListener { // for the selected item ID. val detailIntent = when (item) { is Plaintext -> { - Intent(this, MessageDetailActivity::class.java) + if (item.labels.any { it.type == Label.Type.DRAFT }) { + Intent(this, ComposeMessageActivity::class.java).apply { + putExtra(ComposeMessageActivity.EXTRA_DRAFT, item) + } + } else { + Intent(this, MessageDetailActivity::class.java).apply { + putExtra(MessageDetailFragment.ARG_ITEM, item) + } + } + } + is BitmessageAddress -> Intent(this, AddressDetailActivity::class.java).apply { + putExtra(AddressDetailFragment.ARG_ITEM, item) } - is BitmessageAddress -> Intent(this, AddressDetailActivity::class.java) else -> throw IllegalArgumentException("Plaintext or BitmessageAddress expected, but was ${item::class.simpleName}") } - detailIntent.putExtra(MessageDetailFragment.ARG_ITEM, item) startActivity(detailIntent) } } @@ -474,15 +518,15 @@ class MainActivity : AppCompatActivity(), ListSelectionListener { } companion object { - val EXTRA_SHOW_MESSAGE = "ch.dissem.abit.ShowMessage" - val EXTRA_SHOW_LABEL = "ch.dissem.abit.ShowLabel" - val EXTRA_REPLY_TO_MESSAGE = "ch.dissem.abit.ReplyToMessage" - val ACTION_SHOW_INBOX = "ch.dissem.abit.ShowInbox" + const val EXTRA_SHOW_MESSAGE = "ch.dissem.abit.ShowMessage" + const val EXTRA_SHOW_LABEL = "ch.dissem.abit.ShowLabel" + const val EXTRA_REPLY_TO_MESSAGE = "ch.dissem.abit.ReplyToMessage" + const val ACTION_SHOW_INBOX = "ch.dissem.abit.ShowInbox" - val ADD_IDENTITY = 1 - val MANAGE_IDENTITY = 2 + const val ADD_IDENTITY = 1 + const val MANAGE_IDENTITY = 2 - private val ID_NODE_SWITCH: Long = 1 + private const val ID_NODE_SWITCH: Long = 1 private var instance: WeakReference? = null diff --git a/build.gradle b/build.gradle index 1a6c091..9232626 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.kotlin_version = '1.2.20' + ext.kotlin_version = '1.2.21' ext.anko_version = '0.10.4' repositories { jcenter()