Add option to select identity when composing a message

This commit is contained in:
Christian Basler 2018-02-14 17:49:39 +01:00
parent f481914a65
commit 1da0674857
6 changed files with 159 additions and 74 deletions

View File

@ -101,9 +101,8 @@ class ComposeMessageFragment : Fragment() {
parent.inventoryVector?.let { parents.add(it) } parent.inventoryVector?.let { parents.add(it) }
} }
} }
} ?: { } ?: throw IllegalStateException("No identity set for ComposeMessageFragment")
throw IllegalStateException("No identity set for ComposeMessageFragment")
}.invoke()
setHasOptionsMenu(true) setHasOptionsMenu(true)
} }
@ -115,37 +114,50 @@ class ComposeMessageFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
if (broadcast) { context?.let { ctx ->
recipient_input.visibility = View.GONE val identities = Singleton.getAddressRepository(ctx).getIdentities()
} else { sender_input.adapter = ContactAdapter(ctx, identities, true)
val adapter = ContactAdapter(context!!) val index = identities.indexOf(Singleton.getIdentity(ctx))
recipient_input.setAdapter(adapter) if (index >= 0) {
recipient_input.onItemClickListener = sender_input.setSelection(index)
AdapterView.OnItemClickListener { _, _, pos, _ -> adapter.getItem(pos) }
recipient_input.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(
parent: AdapterView<*>,
view: View,
position: Int,
id: Long
) {
recipient = adapter.getItem(position)
}
override fun onNothingSelected(parent: AdapterView<*>) =
Unit // leave current selection
} }
recipient?.let { recipient_input.setText(it.toString()) }
}
subject_input.setText(subject)
body_input.setText(content)
when { if (broadcast) {
recipient == null -> recipient_input.requestFocus() recipient_input.visibility = View.GONE
subject.isEmpty() -> subject_input.requestFocus() } else {
else -> { val adapter = ContactAdapter(
body_input.requestFocus() ctx,
body_input.setSelection(0) Singleton.getAddressRepository(ctx).getContacts()
)
recipient_input.setAdapter(adapter)
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
) {
recipient = adapter.getItem(position)
}
override fun onNothingSelected(parent: AdapterView<*>) =
Unit // leave current selection
}
recipient?.let { recipient_input.setText(it.toString()) }
}
subject_input.setText(subject)
body_input.setText(content)
when {
recipient == null -> recipient_input.requestFocus()
subject.isEmpty() -> subject_input.requestFocus()
else -> {
body_input.requestFocus()
body_input.setSelection(0)
}
} }
} }
} }
@ -184,7 +196,7 @@ class ComposeMessageFragment : Fragment() {
private fun build(ctx: Context): Plaintext { private fun build(ctx: Context): Plaintext {
val builder: Plaintext.Builder val builder: Plaintext.Builder
if (broadcast) { if (broadcast) {
builder = Plaintext.Builder(BROADCAST).from(identity) builder = Plaintext.Builder(BROADCAST)
} else { } else {
val inputString = recipient_input.text.toString() val inputString = recipient_input.text.toString()
if (recipient == null || recipient?.toString() != inputString) { if (recipient == null || recipient?.toString() != inputString) {
@ -203,9 +215,10 @@ class ComposeMessageFragment : Fragment() {
} }
builder = Plaintext.Builder(MSG) builder = Plaintext.Builder(MSG)
.from(identity)
.to(recipient) .to(recipient)
} }
val sender = sender_input.selectedItem as? ch.dissem.bitmessage.entity.BitmessageAddress
sender?.let { builder.from(it) }
if (!Preferences.requestAcknowledgements(ctx)) { if (!Preferences.requestAcknowledgements(ctx)) {
builder.preventAck() builder.preventAck()
} }

View File

@ -20,25 +20,22 @@ import android.content.Context
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.BaseAdapter import android.widget.*
import android.widget.Filter
import android.widget.Filterable
import android.widget.ImageView
import android.widget.TextView
import java.util.ArrayList
import ch.dissem.apps.abit.Identicon import ch.dissem.apps.abit.Identicon
import ch.dissem.apps.abit.R import ch.dissem.apps.abit.R
import ch.dissem.apps.abit.service.Singleton
import ch.dissem.bitmessage.entity.BitmessageAddress import ch.dissem.bitmessage.entity.BitmessageAddress
import java.util.*
/** /**
* An adapter for contacts. Can be filtered by alias or address. * An adapter for contacts. Can be filtered by alias or address.
*/ */
class ContactAdapter(ctx: Context) : BaseAdapter(), Filterable { class ContactAdapter(
ctx: Context,
private val originalData: List<BitmessageAddress>,
private val slim: Boolean = false
) :
BaseAdapter(), Filterable {
private val inflater = LayoutInflater.from(ctx) private val inflater = LayoutInflater.from(ctx)
private val originalData = Singleton.getAddressRepository(ctx).getContacts()
private var data: List<BitmessageAddress> = originalData private var data: List<BitmessageAddress> = originalData
override fun getCount() = data.size override fun getCount() = data.size
@ -49,23 +46,33 @@ class ContactAdapter(ctx: Context) : BaseAdapter(), Filterable {
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val viewHolder = if (convertView == null) { val viewHolder = if (convertView == null) {
ViewHolder(inflater.inflate(R.layout.contact_row, parent, false)) ViewHolder(
inflater.inflate(
if (slim) {
R.layout.contact_row_slim
} else {
R.layout.contact_row
},
parent, false
)
)
} else { } else {
convertView.tag as ViewHolder convertView.tag as ViewHolder
} }
val item = getItem(position) val item = getItem(position)
viewHolder.avatar.setImageDrawable(Identicon(item)) viewHolder.avatar.setImageDrawable(Identicon(item))
viewHolder.name.text = item.toString() viewHolder.name.text = item.toString()
viewHolder.address.text = item.address viewHolder.address?.text = item.address
return viewHolder.view return viewHolder.view
} }
override fun getFilter(): Filter = ContactFilter() override fun getFilter(): Filter = ContactFilter()
private inner class ViewHolder(val view: View) { private inner class ViewHolder(val view: View) {
val avatar = view.findViewById<ImageView>(R.id.avatar)!! val avatar: ImageView = view.findViewById(R.id.avatar)
val name = view.findViewById<TextView>(R.id.name)!! val name: TextView = view.findViewById(R.id.name)
val address = view.findViewById<TextView>(R.id.address)!! val address: TextView? = view.findViewById(R.id.address)
init { init {
view.tag = this view.tag = this
@ -83,27 +90,29 @@ class ContactAdapter(ctx: Context) : BaseAdapter(), Filterable {
val newValues = ArrayList<BitmessageAddress>() val newValues = ArrayList<BitmessageAddress>()
originalData originalData
.forEach { value -> .forEach { value ->
value.alias?.toLowerCase()?.let { alias -> value.alias?.toLowerCase()?.let { alias ->
if (alias.startsWith(prefixString)) { if (alias.startsWith(prefixString)) {
newValues.add(value) newValues.add(value)
} else { } else {
val words = alias.split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() val words =
alias.split(" ".toRegex()).dropLastWhile { it.isEmpty() }
.toTypedArray()
for (word in words) { for (word in words) {
if (word.startsWith(prefixString)) { if (word.startsWith(prefixString)) {
newValues.add(value) newValues.add(value)
break break
}
} }
} }
} ?: { }
val address = value.address.toLowerCase() } ?: {
if (address.contains(prefixString)) { val address = value.address.toLowerCase()
newValues.add(value) if (address.contains(prefixString)) {
} newValues.add(value)
}.invoke() }
} }.invoke()
}
results.values = newValues results.values = newValues
results.count = newValues.size results.count = newValues.size

View File

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2015 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.
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/avatar"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_margin="4dp"
android:src="@color/colorAccent"
tools:ignore="ContentDescription" />
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toEndOf="@+id/avatar"
android:ellipsize="end"
android:lines="1"
android:paddingBottom="0dp"
android:paddingEnd="4dp"
android:paddingStart="4dp"
android:paddingTop="0dp"
android:textAppearance="?android:attr/textAppearanceMedium"
tools:text="Name" />
</RelativeLayout>

View File

@ -1,8 +1,22 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"> android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:labelFor="@id/sender_input"
android:padding="4dp"
android:text="@string/from"
android:textColor="#9b9b9b"
android:textSize="12sp" />
<Spinner
android:id="@+id/sender_input"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<android.support.design.widget.TextInputLayout <android.support.design.widget.TextInputLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -15,7 +29,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="@string/to" android:hint="@string/to"
android:inputType="textNoSuggestions" android:inputType="textNoSuggestions"
android:maxLines="1"/> android:maxLines="1" />
</android.support.design.widget.TextInputLayout> </android.support.design.widget.TextInputLayout>
@ -29,7 +43,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="@string/subject" android:hint="@string/subject"
android:inputType="textEmailSubject" android:inputType="textEmailSubject"
android:textAppearance="?android:attr/textAppearanceLarge"/> android:textAppearance="?android:attr/textAppearanceLarge" />
</android.support.design.widget.TextInputLayout> </android.support.design.widget.TextInputLayout>
@ -41,6 +55,6 @@
android:gravity="start|top" android:gravity="start|top"
android:hint="@string/compose_body_hint" android:hint="@string/compose_body_hint"
android:inputType="textMultiLine|textCapSentences" android:inputType="textMultiLine|textCapSentences"
android:scrollbars="vertical"/> android:scrollbars="vertical" />
</LinearLayout> </LinearLayout>

View File

@ -127,4 +127,5 @@ Als Alternative kann in den Einstellungen ein vertrauenswürdiger Knoten konfigu
<string name="import_data">Import</string> <string name="import_data">Import</string>
<string name="import_data_summary">Nachrichten und Kontakte importieren (aber keine Identitäten)</string> <string name="import_data_summary">Nachrichten und Kontakte importieren (aber keine Identitäten)</string>
<string name="select_encoding">Kodierung auswählen</string> <string name="select_encoding">Kodierung auswählen</string>
<string name="from">Von</string>
</resources> </resources>

View File

@ -126,4 +126,5 @@ As an alternative you could configure a trusted node in the settings, but as of
<string name="request_acknowledgements_summary">Acknowledges allow making sure a message was received, but require additional time to send</string> <string name="request_acknowledgements_summary">Acknowledges allow making sure a message was received, but require additional time to send</string>
<string name="got_it">Got it</string> <string name="got_it">Got it</string>
<string name="select_encoding">Select encoding</string> <string name="select_encoding">Select encoding</string>
<string name="from">From</string>
</resources> </resources>