From 24694f205e0080d9a3c16327b41be084bf09bdd8 Mon Sep 17 00:00:00 2001 From: Stephen Paul Weber Date: Tue, 21 Jun 2022 15:56:23 -0500 Subject: [PATCH] Support reported/item tables Right now renders using a GridLayout which means all columns are the same width. No horizontal scrolling or becoming not-a-table for many columns, so big tables will be a disaster. Does render and work though. The strategy here is to actually make each cell an "item" in the RecyclerView instead of each row being an item. Then the layout manager takes care of it. This means that the end-of-row-ness is just because of column count, and not actually enforced at all. It also means that as currently built if any row has a missing field it'll mess up the whole thing. No type directed rendering or anything yet, just dump it out. --- src/cheogram/res/layout/command_page.xml | 4 +- .../res/layout/command_result_cell.xml | 12 +++ .../conversations/entities/Conversation.java | 97 ++++++++++++++++++- 3 files changed, 107 insertions(+), 6 deletions(-) create mode 100644 src/cheogram/res/layout/command_result_cell.xml diff --git a/src/cheogram/res/layout/command_page.xml b/src/cheogram/res/layout/command_page.xml index 667b901203caaaec46f7f03d12affd14d0a4a4b7..ba20cd29614ce5ce0b4a6d1544346357436e7562 100644 --- a/src/cheogram/res/layout/command_page.xml +++ b/src/cheogram/res/layout/command_page.xml @@ -9,10 +9,10 @@ android:id="@+id/form" android:paddingTop="8dp" android:layout_width="match_parent" - android:layout_height="fill_parent" + android:layout_height="match_parent" android:layout_above="@+id/actions" android:orientation="vertical" - app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" /> + app:layoutManager="androidx.recyclerview.widget.GridLayoutManager" /> + + + + + diff --git a/src/main/java/eu/siacs/conversations/entities/Conversation.java b/src/main/java/eu/siacs/conversations/entities/Conversation.java index f219980340e0a72160ffdd1c447caad9afee170b..563169e74f064803acb8ef2721ef0c516294fab0 100644 --- a/src/main/java/eu/siacs/conversations/entities/Conversation.java +++ b/src/main/java/eu/siacs/conversations/entities/Conversation.java @@ -33,7 +33,7 @@ import androidx.databinding.DataBindingUtil; import androidx.databinding.ViewDataBinding; import androidx.viewpager.widget.PagerAdapter; import androidx.recyclerview.widget.RecyclerView; -import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.GridLayoutManager; import androidx.viewpager.widget.ViewPager; import com.google.android.material.tabs.TabLayout; @@ -58,6 +58,7 @@ import eu.siacs.conversations.crypto.PgpDecryptionService; import eu.siacs.conversations.databinding.CommandPageBinding; import eu.siacs.conversations.databinding.CommandNoteBinding; import eu.siacs.conversations.databinding.CommandResultFieldBinding; +import eu.siacs.conversations.databinding.CommandResultCellBinding; import eu.siacs.conversations.databinding.CommandCheckboxFieldBinding; import eu.siacs.conversations.databinding.CommandRadioEditFieldBinding; import eu.siacs.conversations.databinding.CommandSpinnerFieldBinding; @@ -1444,6 +1445,23 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl } } + class ResultCellViewHolder extends ViewHolder { + public ResultCellViewHolder(CommandResultCellBinding binding) { super(binding); } + + @Override + public void bind(Element field) { + Column col = (Column) field; + + if (col.item == null) { + binding.text.setTextAppearance(binding.getRoot().getContext(), R.style.TextAppearance_Conversations_Subhead); + binding.text.setText(col.reported.getAttribute("label")); + } else { + binding.text.setTextAppearance(binding.getRoot().getContext(), R.style.TextAppearance_Conversations_Body1); + binding.text.setText(col.item.findChildContent("value", "jabber:x:data")); + } + } + } + class CheckboxFieldViewHolder extends ViewHolder implements CompoundButton.OnCheckedChangeListener { public CheckboxFieldViewHolder(CommandCheckboxFieldBinding binding) { super(binding); @@ -1693,6 +1711,17 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl } } + class Column extends Element { + protected Element reported; + protected Element item; + + Column(Element reported, Element item) { + super("x", "x:column"); + this.reported = reported; + this.item = item; + } + } + final int TYPE_ERROR = 1; final int TYPE_NOTE = 2; final int TYPE_WEB = 3; @@ -1701,18 +1730,22 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl final int TYPE_CHECKBOX_FIELD = 6; final int TYPE_SPINNER_FIELD = 7; final int TYPE_RADIO_EDIT_FIELD = 8; + final int TYPE_RESULT_CELL = 9; protected String mTitle; protected CommandPageBinding mBinding = null; protected IqPacket response = null; protected Element responseElement = null; + protected Element reported = null; protected SparseArray viewTypes = new SparseArray<>(); protected XmppConnectionService xmppConnectionService; protected ArrayAdapter actionsAdapter; + protected GridLayoutManager layoutManager; CommandSession(String title, XmppConnectionService xmppConnectionService) { mTitle = title; this.xmppConnectionService = xmppConnectionService; + setupLayoutManager(); actionsAdapter = new ArrayAdapter(xmppConnectionService, R.layout.simple_list_item) { @Override public View getView(int position, View convertView, ViewGroup parent) { @@ -1745,9 +1778,11 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl public void updateWithResponse(IqPacket iq) { this.responseElement = null; + this.reported = null; this.response = iq; this.viewTypes.clear(); this.actionsAdapter.clear(); + layoutManager.setSpanCount(1); Element command = iq.findChild("command", "http://jabber.org/protocol/commands"); if (iq.getType() == IqPacket.TYPE.RESULT && command != null) { @@ -1769,6 +1804,8 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl if (el.getAttribute("type").equals("result") || el.getAttribute("type").equals("form")) { this.responseElement = el; + this.reported = el.findChild("reported", "jabber:x:data"); + layoutManager.setSpanCount(this.reported == null ? 1 : this.reported.getChildren().size()); } break; } @@ -1822,6 +1859,11 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl if (type != null && type.equals("hidden")) continue; } + if (el.getName().equals("reported") || el.getName().equals("item")) { + i += el.getChildren().size(); + continue; + } + i++; } return i; @@ -1843,6 +1885,34 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl if (type != null && type.equals("hidden")) continue; } + if (el.getName().equals("reported") || el.getName().equals("item")) { + int col = 0; + for (Element subel : el.getChildren()) { + if (i < position) { + i++; + col++; + continue; + } + + Element reportedField = null; + if (reported != null) { + int rCol = 0; + for (Element field : reported.getChildren()) { + if (!field.getName().equals("field") || !field.getNamespace().equals("jabber:x:data")) continue; + if (rCol < col) { + rCol++; + continue; + } + reportedField = field; + break; + } + } + return new Column(reportedField, el.getName().equals("item") ? subel : null); + } + + i--; + } + if (i < position) { i++; continue; @@ -1906,6 +1976,9 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl return TYPE_TEXT_FIELD; } } + if (item instanceof Column) { + return TYPE_RESULT_CELL; + } return -1; } else { return TYPE_ERROR; @@ -1931,6 +2004,10 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl CommandResultFieldBinding binding = DataBindingUtil.inflate(LayoutInflater.from(container.getContext()), R.layout.command_result_field, container, false); return new ResultFieldViewHolder(binding); } + case TYPE_RESULT_CELL: { + CommandResultCellBinding binding = DataBindingUtil.inflate(LayoutInflater.from(container.getContext()), R.layout.command_result_cell, container, false); + return new ResultCellViewHolder(binding); + } case TYPE_CHECKBOX_FIELD: { CommandCheckboxFieldBinding binding = DataBindingUtil.inflate(LayoutInflater.from(container.getContext()), R.layout.command_checkbox_field, container, false); return new CheckboxFieldViewHolder(binding); @@ -1998,12 +2075,24 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl return false; } - public void setBinding(CommandPageBinding b) { - mBinding = b; - mBinding.form.setLayoutManager(new LinearLayoutManager(mPager.getContext()) { + protected GridLayoutManager setupLayoutManager() { + layoutManager = new GridLayoutManager(mPager.getContext(), layoutManager == null ? 1 : layoutManager.getSpanCount()) { @Override public boolean canScrollVertically() { return getItemCount() > 1; } + }; + layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { + @Override + public int getSpanSize(int position) { + if (getItemViewType(position) != TYPE_RESULT_CELL) return layoutManager.getSpanCount(); + return 1; + } }); + return layoutManager; + } + + public void setBinding(CommandPageBinding b) { + mBinding = b; + mBinding.form.setLayoutManager(setupLayoutManager()); mBinding.form.setAdapter(this); mBinding.actions.setAdapter(actionsAdapter); mBinding.actions.setOnItemClickListener((parent, v, pos, id) -> {