~singpolyma/cheogram-android

49cbe7448e175329ea876bd450bbf52f65e7fe5c — Stephen Paul Weber 1 year, 2 months ago d29b338
Use a RecyclerView for list of gateway options

If there are no gateways in the current account's roster, just show the
old "Jabber ID" label.  Otherwise show a list of toggle buttons.  These
buttons show the type of their gateway and change the JID input to
not autocomplete and to show a hint matching the jabber:id:gateway
prompt.

This is just UI, submit behaviour is unchanged. List is not yet populated by
this code.
M build.gradle => build.gradle +2 -0
@@ 94,6 94,8 @@ dependencies {
    implementation 'com.google.guava:guava:30.1.1-android'
    implementation 'io.michaelrocks:libphonenumber-android:8.12.36'
    implementation 'io.github.nishkarsh:android-permissions:2.1.6'
    implementation 'androidx.recyclerview:recyclerview:1.1.0'
    implementation 'org.solovyev.android.views:linear-layout-manager:0.5@aar'
    implementation urlFile('https://cloudflare-ipfs.com/ipfs/QmeqMiLxHi8AAjXobxr3QTfa1bSSLyAu86YviAqQnjxCjM/libwebrtc.aar', 'libwebrtc.aar')
    // INSERT
}

M src/main/java/eu/siacs/conversations/entities/ServiceDiscoveryResult.java => src/main/java/eu/siacs/conversations/entities/ServiceDiscoveryResult.java +7 -3
@@ 168,15 168,19 @@ public class ServiceDiscoveryResult {
		return this.features;
	}

	public boolean hasIdentity(String category, String type) {
	public Identity getIdentity(String category, String type) {
		for (Identity id : this.getIdentities()) {
			if ((category == null || id.getCategory().equals(category)) &&
					(type == null || id.getType().equals(type))) {
				return true;
				return id;
			}
		}

		return false;
		return null;
	}

	public boolean hasIdentity(String category, String type) {
		return getIdentity(category, type) != null;
	}

	public String getExtendedDiscoInformation(String formType, String name) {

M src/main/java/eu/siacs/conversations/ui/EnterJidDialog.java => src/main/java/eu/siacs/conversations/ui/EnterJidDialog.java +174 -5
@@ 2,31 2,48 @@ package eu.siacs.conversations.ui;

import android.app.Activity;
import android.app.Dialog;
import android.content.DialogInterface.OnClickListener;
import android.content.DialogInterface;
import android.os.Bundle;
import android.text.Editable;
import android.text.InputType;
import android.text.TextWatcher;
import android.util.Pair;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import android.widget.ToggleButton;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.DialogFragment;
import androidx.recyclerview.widget.RecyclerView;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.solovyev.android.views.llm.LinearLayoutManager;

import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.databinding.EnterJidDialogBinding;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Presence;
import eu.siacs.conversations.entities.ServiceDiscoveryResult;
import eu.siacs.conversations.ui.adapter.KnownHostsAdapter;
import eu.siacs.conversations.ui.interfaces.OnBackendConnected;
import eu.siacs.conversations.ui.util.DelayedHintHelper;
import eu.siacs.conversations.xmpp.Jid;
import eu.siacs.conversations.xmpp.OnGatewayPromptResult;

public class EnterJidDialog extends DialogFragment implements OnBackendConnected, TextWatcher {



@@ 51,6 68,7 @@ public class EnterJidDialog extends DialogFragment implements OnBackendConnected
    private boolean sanityCheckJid = false;

    private boolean issuedWarning = false;
    private GatewayListAdapter gatewayListAdapter = new GatewayListAdapter();

    public static EnterJidDialog newInstance(
            final List<String> activatedAccounts,


@@ 129,6 147,9 @@ public class EnterJidDialog extends DialogFragment implements OnBackendConnected
            binding.account.setAdapter(adapter);
        }

        binding.gatewayList.setLayoutManager(new LinearLayoutManager(getActivity(), LinearLayoutManager.HORIZONTAL, false));
        binding.gatewayList.setAdapter(gatewayListAdapter);

        builder.setView(binding.getRoot());
        builder.setNegativeButton(R.string.cancel, null);
        builder.setPositiveButton(getArguments().getString(POSITIVE_BUTTON_KEY), null);


@@ 157,11 178,7 @@ public class EnterJidDialog extends DialogFragment implements OnBackendConnected
        }
        try {
            if (Config.DOMAIN_LOCK != null) {
                accountJid =
                        Jid.ofEscaped(
                                (String) binding.account.getSelectedItem(),
                                Config.DOMAIN_LOCK,
                                null);
                accountJid = Jid.ofEscaped((String) binding.account.getSelectedItem(), Config.DOMAIN_LOCK, null);
            } else {
                accountJid = Jid.ofEscaped((String) binding.account.getSelectedItem());
            }


@@ 276,4 293,156 @@ public class EnterJidDialog extends DialogFragment implements OnBackendConnected
        final String[] parts = domain.split("\\.");
        return parts.length >= 3 && SUSPICIOUS_DOMAINS.contains(parts[0]);
    }

    protected class GatewayListAdapter extends RecyclerView.Adapter<GatewayListAdapter.ViewHolder> {
        protected class ViewHolder extends RecyclerView.ViewHolder {
            protected ToggleButton button;
            protected int index;

            public ViewHolder(View view, int i) {
                super(view);
                this.button = (ToggleButton) view.findViewById(R.id.button);
                setIndex(i);
                button.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        button.setChecked(true); // Force visual not to flap to unchecked
                        setSelected(index);
                    }
                });
            }

            public void setIndex(int i) {
                this.index = i;
                button.setChecked(selected == i);
            }

            public void useButton(int res) {
                button.setText(res);
                button.setTextOff(button.getText());
                button.setTextOn(button.getText());
                button.setChecked(selected == this.index);
                binding.gatewayList.setVisibility(View.VISIBLE);
                button.setVisibility(View.VISIBLE);
            }

            public void useButton(String txt) {
                button.setTextOff(txt);
                button.setTextOn(txt);
                button.setChecked(selected == this.index);
                binding.gatewayList.setVisibility(View.VISIBLE);
                button.setVisibility(View.VISIBLE);
            }
        }

        protected List<Pair<Contact,String>> gateways = new ArrayList();
        protected int selected = 0;

        @Override
        public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
            View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.enter_jid_dialog_gateway_list_item, null);
            return new ViewHolder(view, i);
        }

        @Override
        public void onBindViewHolder(ViewHolder viewHolder, int i) {
            viewHolder.setIndex(i);

            if(i == 0) {
                if(getItemCount() < 2) {
                    binding.gatewayList.setVisibility(View.GONE);
                } else {
                    viewHolder.useButton(R.string.account_settings_jabber_id);
                }
            } else {
                viewHolder.useButton(getLabel(i));
            }
        }

        @Override
        public int getItemCount() {
            return this.gateways.size() + 1;
        }

        public void setSelected(int i) {
            int old = this.selected;
            this.selected = i;

            if(i == 0) {
                binding.jid.setThreshold(1);
                binding.jid.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS | InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE);
                binding.jidLayout.setHint(R.string.account_settings_jabber_id);
            } else {
                binding.jid.setThreshold(999999); // do not autocomplete
                binding.jid.setInputType(InputType.TYPE_CLASS_TEXT);
                binding.jidLayout.setHint(this.gateways.get(i-1).second);
                binding.jid.setHint(null);
                binding.jid.setOnFocusChangeListener((v, hasFocus) -> {});
            }

            notifyItemChanged(old);
            notifyItemChanged(i);
        }

        public String getLabel(int i) {
            if (i == 0) return null;

            for(Presence p : this.gateways.get(i-1).first.getPresences().getPresences()) {
                ServiceDiscoveryResult.Identity id;
                if(p.getServiceDiscoveryResult() != null && (id = p.getServiceDiscoveryResult().getIdentity("gateway", null)) != null) {
                    return id.getType();
                }
            }

            return gateways.get(i-1).first.getDisplayName();
        }

        public String getSelectedLabel() {
            return getLabel(selected);
        }

        public Pair<String, Pair<Jid,Presence>> getSelected() {
            if(this.selected == 0) {
                return null; // No gateway, just use direct JID entry
            }

            Pair<Contact,String> gateway = this.gateways.get(this.selected - 1);

            Pair<Jid,Presence> presence = null;
            for (Map.Entry<String,Presence> e : gateway.first.getPresences().getPresencesMap().entrySet()) {
                Presence p = e.getValue();
                if (p.getServiceDiscoveryResult() != null) {
                    if (p.getServiceDiscoveryResult().getFeatures().contains("jabber:iq:gateway")) {
                        if (e.getKey().equals("")) {
                            presence = new Pair<>(gateway.first.getJid(), p);
                        } else {
                            presence = new Pair<>(gateway.first.getJid().withResource(e.getKey()), p);
                        }
                        break;
                    }
                    if (p.getServiceDiscoveryResult().hasIdentity("gateway", null)) {
                        if (e.getKey().equals("")) {
                            presence = new Pair<>(gateway.first.getJid(), p);
                        } else {
                            presence = new Pair<>(gateway.first.getJid().withResource(e.getKey()), p);
                        }
                    }
                }
            }

            return presence == null ? null : new Pair(gateway.second, presence);
        }

        public void clear() {
            this.gateways.clear();
            notifyDataSetChanged();
            setSelected(0);
        }

        public void add(Contact gateway, String prompt) {
            binding.gatewayList.setVisibility(View.VISIBLE);
            this.gateways.add(new Pair<>(gateway, prompt));
            notifyDataSetChanged();
        }
    }
}

M src/main/res/layout/enter_jid_dialog.xml => src/main/res/layout/enter_jid_dialog.xml +5 -0
@@ 22,6 22,11 @@
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"/>

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/gateway_list"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content" />

        <com.google.android.material.textfield.TextInputLayout
            android:id="@+id/jid_layout"
            android:layout_width="match_parent"

A src/main/res/layout/enter_jid_dialog_gateway_list_item.xml => src/main/res/layout/enter_jid_dialog_gateway_list_item.xml +14 -0
@@ 0,0 1,14 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:orientation="vertical"
              android:paddingRight="5dp">
    <ToggleButton
        android:id="@+id/button"
        android:gravity="center"
        android:visibility="gone"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:textColor="?attr/edit_text_color"
        android:textSize="?attr/TextSizeBody1" />
</LinearLayout>