From 0bbc399365e5889b306b7c3de15cb6524dfc473d Mon Sep 17 00:00:00 2001 From: Stephen Paul Weber Date: Wed, 25 Jan 2023 17:14:30 -0500 Subject: [PATCH] Show table as list of cards when too wide If a table has too many columns then showing them all next to each other is awful and squishy. So instead, when too wide based on current screen size, show as a list of cards. The cards are themselves a grid of two columns so that they aren't too giantly tall, and they re-use the result field rendering for each field in that card. Changes on redraw such that rotating to landscape can take you to table view and rotating back to portrait can take you back to list of cards. --- src/cheogram/res/layout/command_item_card.xml | 22 ++++ .../conversations/entities/Conversation.java | 101 +++++++++++++++--- 2 files changed, 108 insertions(+), 15 deletions(-) create mode 100644 src/cheogram/res/layout/command_item_card.xml diff --git a/src/cheogram/res/layout/command_item_card.xml b/src/cheogram/res/layout/command_item_card.xml new file mode 100644 index 0000000000000000000000000000000000000000..2f034d4b2906f0610ab54f131393d127bdef961f --- /dev/null +++ b/src/cheogram/res/layout/command_item_card.xml @@ -0,0 +1,22 @@ + + + + + + + + diff --git a/src/main/java/eu/siacs/conversations/entities/Conversation.java b/src/main/java/eu/siacs/conversations/entities/Conversation.java index ac0a28ecc6850efc69bab9cba0d05799c1758a29..fb5ade65bc7528411a381f32a27482708f9a5033 100644 --- a/src/main/java/eu/siacs/conversations/entities/Conversation.java +++ b/src/main/java/eu/siacs/conversations/entities/Conversation.java @@ -21,6 +21,7 @@ import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.AdapterView; import android.widget.CompoundButton; +import android.widget.GridLayout; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; @@ -72,6 +73,7 @@ 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.CommandItemCardBinding; import eu.siacs.conversations.databinding.CommandCheckboxFieldBinding; import eu.siacs.conversations.databinding.CommandProgressBarBinding; import eu.siacs.conversations.databinding.CommandRadioEditFieldBinding; @@ -1588,6 +1590,38 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl } } + class ItemCardViewHolder extends ViewHolder { + public ItemCardViewHolder(CommandItemCardBinding binding) { super(binding); } + + @Override + public void bind(Item item) { + for (Field field : reported) { + CommandResultFieldBinding row = DataBindingUtil.inflate(LayoutInflater.from(binding.getRoot().getContext()), R.layout.command_result_field, binding.fields, false); + GridLayout.LayoutParams param = new GridLayout.LayoutParams(); + param.columnSpec = GridLayout.spec(GridLayout.UNDEFINED, GridLayout.FILL, 1f); + param.width = 0; + row.getRoot().setLayoutParams(param); + binding.fields.addView(row.getRoot()); + for (Element el : item.el.getChildren()) { + if (el.getName().equals("field") && el.getNamespace().equals("jabber:x:data") && el.getAttribute("var") != null && el.getAttribute("var").equals(field.getVar())) { + for (String label : field.getLabel().asSet()) { + el.setAttribute("label", label); + } + for (String desc : field.getDesc().asSet()) { + el.setAttribute("desc", desc); + } + for (String type : field.getType().asSet()) { + el.setAttribute("type", type); + } + Element validate = field.el.findChild("validate", "http://jabber.org/protocol/xdata-validate"); + if (validate != null) el.addChild(validate); + new ResultFieldViewHolder(row).bind(new Field(el, -1)); + } + } + } + } + } + class CheckboxFieldViewHolder extends ViewHolder implements CompoundButton.OnCheckedChangeListener { public CheckboxFieldViewHolder(CommandCheckboxFieldBinding binding) { super(binding); @@ -2043,6 +2077,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl final int TYPE_RESULT_CELL = 9; final int TYPE_PROGRESSBAR = 10; final int TYPE_SEARCH_LIST_FIELD = 11; + final int TYPE_ITEM_CARD = 12; protected boolean loading = false; protected Timer loadingTimer = new Timer(); @@ -2130,7 +2165,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl if (el.getAttribute("type").equals("result") || el.getAttribute("type").equals("form")) { this.responseElement = el; setupReported(el.findChild("reported", "jabber:x:data")); - layoutManager.setSpanCount(this.reported == null ? 1 : this.reported.size()); + if (mBinding != null) mBinding.form.setLayoutManager(setupLayoutManager()); } break; } @@ -2205,7 +2240,12 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl } if (el.getName().equals("reported") || el.getName().equals("item")) { - if (reported != null) i += reported.size(); + if ((layoutManager == null ? 1 : layoutManager.getSpanCount()) < 2) { + if (el.getName().equals("reported")) continue; + i += 1; + } else { + if (reported != null) i += reported.size(); + } continue; } @@ -2236,21 +2276,29 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl Cell cell = null; if (reported != null) { - if (reported.size() > position - i) { - Field reportedField = reported.get(position - i); - Element itemField = null; - if (el.getName().equals("item")) { - for (Element subel : el.getChildren()) { - if (subel.getAttribute("var").equals(reportedField.getVar())) { - itemField = subel; - break; + if ((layoutManager == null ? 1 : layoutManager.getSpanCount()) < 2) { + if (el.getName().equals("reported")) continue; + if (i == position) { + items.put(position, new Item(el, TYPE_ITEM_CARD)); + return items.get(position); + } + } else { + if (reported.size() > position - i) { + Field reportedField = reported.get(position - i); + Element itemField = null; + if (el.getName().equals("item")) { + for (Element subel : el.getChildren()) { + if (subel.getAttribute("var").equals(reportedField.getVar())) { + itemField = subel; + break; + } } } + cell = new Cell(reportedField, itemField); + } else { + i += reported.size(); + continue; } - cell = new Cell(reportedField, itemField); - } else { - i += reported.size(); - continue; } } @@ -2301,6 +2349,10 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl CommandResultCellBinding binding = DataBindingUtil.inflate(LayoutInflater.from(container.getContext()), R.layout.command_result_cell, container, false); return new ResultCellViewHolder(binding); } + case TYPE_ITEM_CARD: { + CommandItemCardBinding binding = DataBindingUtil.inflate(LayoutInflater.from(container.getContext()), R.layout.command_item_card, container, false); + return new ItemCardViewHolder(binding); + } case TYPE_CHECKBOX_FIELD: { CommandCheckboxFieldBinding binding = DataBindingUtil.inflate(LayoutInflater.from(container.getContext()), R.layout.command_checkbox_field, container, false); return new CheckboxFieldViewHolder(binding); @@ -2420,7 +2472,26 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl } protected GridLayoutManager setupLayoutManager() { - layoutManager = new GridLayoutManager(mPager.getContext(), layoutManager == null ? 1 : layoutManager.getSpanCount()); + int spanCount = 1; + + if (reported != null && mPager != null) { + float screenWidth = mPager.getContext().getResources().getDisplayMetrics().widthPixels; + TextPaint paint = ((TextView) LayoutInflater.from(mPager.getContext()).inflate(R.layout.command_result_cell, null)).getPaint(); + float tableHeaderWidth = reported.stream().reduce( + 0f, + (total, field) -> total + StaticLayout.getDesiredWidth(field.getLabel().or("--------"), paint), + (a, b) -> a + b + ); + + spanCount = tableHeaderWidth > 0.75 * screenWidth ? 1 : this.reported.size(); + } + + if (layoutManager != null && layoutManager.getSpanCount() != spanCount) { + items.clear(); + notifyDataSetChanged(); + } + + layoutManager = new GridLayoutManager(mPager.getContext(), spanCount); layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { @Override public int getSpanSize(int position) {