~singpolyma/cheogram-android

1a7c428e2fac394513a9908bdd9ca3dbcc0efae5 — Stephen Paul Weber a month ago ee5ae57
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.
M src/cheogram/res/layout/command_page.xml => src/cheogram/res/layout/command_page.xml +2 -2
@@ 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" />

        <GridView
            android:id="@+id/actions"

A src/cheogram/res/layout/command_result_cell.xml => src/cheogram/res/layout/command_result_cell.xml +12 -0
@@ 0,0 1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <TextView
        android:id="@+id/text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textAppearance="@style/TextAppearance.Conversations.Body1"
        android:textColor="?attr/edit_text_color" />

</layout>

M src/main/java/eu/siacs/conversations/entities/Conversation.java => src/main/java/eu/siacs/conversations/entities/Conversation.java +93 -4
@@ 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<CommandResultCellBinding> {
                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<CommandCheckboxFieldBinding> 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<Integer> viewTypes = new SparseArray<>();
            protected XmppConnectionService xmppConnectionService;
            protected ArrayAdapter<String> actionsAdapter;
            protected GridLayoutManager layoutManager;

            CommandSession(String title, XmppConnectionService xmppConnectionService) {
                mTitle = title;
                this.xmppConnectionService = xmppConnectionService;
                setupLayoutManager();
                actionsAdapter = new ArrayAdapter<String>(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) -> {