Merge branch 'jabber-iq-gateway2'
* jabber-iq-gateway2: Improve example hinting Move GONE/VISIBLE to the controller instead of the model Show identity category gateway even without jabber:iq:gateway prompt Sort gateways Switch to AndroidX LinearLayoutManager Parse phone numbers using local settings before asking gateway Try all gateway translations options Change input type based on gateway type Load gateways into UI Use a RecyclerView for list of gateway options Fetch jabber:iq:gateway prompt
This commit is contained in:
commit
6269ccc054
|
@ -94,6 +94,7 @@ 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 urlFile('https://cloudflare-ipfs.com/ipfs/QmeqMiLxHi8AAjXobxr3QTfa1bSSLyAu86YviAqQnjxCjM/libwebrtc.aar', 'libwebrtc.aar')
|
||||
// INSERT
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -145,6 +145,7 @@ import eu.siacs.conversations.xml.Namespace;
|
|||
import eu.siacs.conversations.xmpp.Jid;
|
||||
import eu.siacs.conversations.xmpp.OnBindListener;
|
||||
import eu.siacs.conversations.xmpp.OnContactStatusChanged;
|
||||
import eu.siacs.conversations.xmpp.OnGatewayResult;
|
||||
import eu.siacs.conversations.xmpp.OnIqPacketReceived;
|
||||
import eu.siacs.conversations.xmpp.OnKeyStatusUpdated;
|
||||
import eu.siacs.conversations.xmpp.OnMessageAcknowledged;
|
||||
|
@ -4655,6 +4656,24 @@ public class XmppConnectionService extends Service {
|
|||
}
|
||||
}
|
||||
|
||||
public void fetchFromGateway(Account account, final Jid jid, final String input, final OnGatewayResult callback) {
|
||||
IqPacket request = new IqPacket(input == null ? IqPacket.TYPE.GET : IqPacket.TYPE.SET);
|
||||
request.setTo(jid);
|
||||
Element query = request.query("jabber:iq:gateway");
|
||||
if (input != null) {
|
||||
Element prompt = query.addChild("prompt");
|
||||
prompt.setContent(input);
|
||||
}
|
||||
sendIqPacket(account, request, (Account acct, IqPacket packet) -> {
|
||||
if (packet.getType() == IqPacket.TYPE.RESULT) {
|
||||
callback.onGatewayResult(packet.query().findChildContent(input == null ? "prompt" : "jid"), null);
|
||||
} else {
|
||||
Element error = packet.findChild("error");
|
||||
callback.onGatewayResult(null, error == null ? null : error.findChildContent("text"));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void fetchCaps(Account account, final Jid jid, final Presence presence) {
|
||||
final Pair<String, String> key = new Pair<>(presence.getHash(), presence.getVer());
|
||||
final ServiceDiscoveryResult disco = getCachedServiceDiscoveryResult(key);
|
||||
|
|
|
@ -2,31 +2,51 @@ 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 androidx.recyclerview.widget.LinearLayoutManager;
|
||||
|
||||
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 io.michaelrocks.libphonenumber.android.NumberParseException;
|
||||
|
||||
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.utils.PhoneNumberUtilWrapper;
|
||||
import eu.siacs.conversations.xmpp.Jid;
|
||||
import eu.siacs.conversations.xmpp.OnGatewayResult;
|
||||
|
||||
public class EnterJidDialog extends DialogFragment implements OnBackendConnected, TextWatcher {
|
||||
|
||||
|
@ -51,6 +71,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 +150,39 @@ public class EnterJidDialog extends DialogFragment implements OnBackendConnected
|
|||
binding.account.setAdapter(adapter);
|
||||
}
|
||||
|
||||
binding.gatewayList.setLayoutManager(new LinearLayoutManager(getActivity(), LinearLayoutManager.HORIZONTAL, false));
|
||||
binding.gatewayList.setAdapter(gatewayListAdapter);
|
||||
gatewayListAdapter.setOnEmpty(() -> binding.gatewayList.setVisibility(View.GONE));
|
||||
gatewayListAdapter.setOnNonEmpty(() -> binding.gatewayList.setVisibility(View.VISIBLE));
|
||||
|
||||
binding.account.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView accountSpinner, View view, int position, long id) {
|
||||
XmppActivity context = (XmppActivity) getActivity();
|
||||
if (context.xmppConnectionService == null || accountJid() == null) return;
|
||||
|
||||
gatewayListAdapter.clear();
|
||||
final Account account = context.xmppConnectionService.findAccountByJid(accountJid());
|
||||
|
||||
for (final Contact contact : account.getRoster().getContacts()) {
|
||||
if (contact.showInRoster() && (contact.getPresences().anyIdentity("gateway", null) || contact.getPresences().anySupport("jabber:iq:gateway"))) {
|
||||
context.xmppConnectionService.fetchFromGateway(account, contact.getJid(), null, (final String prompt, String errorMessage) -> {
|
||||
if (prompt == null && !contact.getPresences().anyIdentity("gateway", null)) return;
|
||||
|
||||
context.runOnUiThread(() -> {
|
||||
gatewayListAdapter.add(contact, prompt);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView accountSpinner) {
|
||||
gatewayListAdapter.clear();
|
||||
}
|
||||
});
|
||||
|
||||
builder.setView(binding.getRoot());
|
||||
builder.setNegativeButton(R.string.cancel, null);
|
||||
builder.setPositiveButton(getArguments().getString(POSITIVE_BUTTON_KEY), null);
|
||||
|
@ -150,59 +204,92 @@ public class EnterJidDialog extends DialogFragment implements OnBackendConnected
|
|||
return dialog;
|
||||
}
|
||||
|
||||
protected Jid accountJid() {
|
||||
try {
|
||||
if (Config.DOMAIN_LOCK != null) {
|
||||
return Jid.ofEscaped((String) binding.account.getSelectedItem(), Config.DOMAIN_LOCK, null);
|
||||
} else {
|
||||
return Jid.ofEscaped((String) binding.account.getSelectedItem());
|
||||
}
|
||||
} catch (final IllegalArgumentException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void handleEnter(EnterJidDialogBinding binding, String account) {
|
||||
final Jid accountJid;
|
||||
if (!binding.account.isEnabled() && account == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (Config.DOMAIN_LOCK != null) {
|
||||
accountJid =
|
||||
Jid.ofEscaped(
|
||||
(String) binding.account.getSelectedItem(),
|
||||
Config.DOMAIN_LOCK,
|
||||
null);
|
||||
} else {
|
||||
accountJid = Jid.ofEscaped((String) binding.account.getSelectedItem());
|
||||
}
|
||||
} catch (final IllegalArgumentException e) {
|
||||
return;
|
||||
}
|
||||
final Jid contactJid;
|
||||
try {
|
||||
contactJid = Jid.ofEscaped(binding.jid.getText().toString());
|
||||
} catch (final IllegalArgumentException e) {
|
||||
binding.jidLayout.setError(getActivity().getString(R.string.invalid_jid));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!issuedWarning && sanityCheckJid) {
|
||||
if (contactJid.isDomainJid()) {
|
||||
binding.jidLayout.setError(
|
||||
getActivity().getString(R.string.this_looks_like_a_domain));
|
||||
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setText(R.string.add_anway);
|
||||
issuedWarning = true;
|
||||
return;
|
||||
}
|
||||
if (suspiciousSubDomain(contactJid.getDomain().toEscapedString())) {
|
||||
binding.jidLayout.setError(
|
||||
getActivity().getString(R.string.this_looks_like_channel));
|
||||
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setText(R.string.add_anway);
|
||||
issuedWarning = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (mListener != null) {
|
||||
try {
|
||||
if (mListener.onEnterJidDialogPositive(accountJid, contactJid)) {
|
||||
dialog.dismiss();
|
||||
final Jid accountJid = accountJid();
|
||||
final OnGatewayResult finish = (final String jidString, final String errorMessage) -> {
|
||||
getActivity().runOnUiThread(() -> {
|
||||
if (errorMessage != null) {
|
||||
binding.jidLayout.setError(errorMessage);
|
||||
return;
|
||||
}
|
||||
} catch (JidError error) {
|
||||
binding.jidLayout.setError(error.toString());
|
||||
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setText(R.string.add);
|
||||
issuedWarning = false;
|
||||
}
|
||||
if (jidString == null) {
|
||||
binding.jidLayout.setError(getActivity().getString(R.string.invalid_jid));
|
||||
return;
|
||||
}
|
||||
|
||||
final Jid contactJid;
|
||||
try {
|
||||
contactJid = Jid.ofEscaped(jidString);
|
||||
} catch (final IllegalArgumentException e) {
|
||||
binding.jidLayout.setError(getActivity().getString(R.string.invalid_jid));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!issuedWarning && sanityCheckJid) {
|
||||
if (contactJid.isDomainJid()) {
|
||||
binding.jidLayout.setError(getActivity().getString(R.string.this_looks_like_a_domain));
|
||||
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setText(R.string.add_anway);
|
||||
issuedWarning = true;
|
||||
return;
|
||||
}
|
||||
if (suspiciousSubDomain(contactJid.getDomain().toEscapedString())) {
|
||||
binding.jidLayout.setError(getActivity().getString(R.string.this_looks_like_channel));
|
||||
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setText(R.string.add_anway);
|
||||
issuedWarning = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (mListener != null) {
|
||||
try {
|
||||
if (mListener.onEnterJidDialogPositive(accountJid, contactJid)) {
|
||||
dialog.dismiss();
|
||||
}
|
||||
} catch (JidError error) {
|
||||
binding.jidLayout.setError(error.toString());
|
||||
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setText(R.string.add);
|
||||
issuedWarning = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Pair<String,Pair<Jid,Presence>> p = gatewayListAdapter.getSelected();
|
||||
final String type = gatewayListAdapter.getSelectedType();
|
||||
|
||||
// Resolve based on local settings before submission
|
||||
if (type.equals("pstn") || type.equals("sms")) {
|
||||
try {
|
||||
binding.jid.setText(PhoneNumberUtilWrapper.normalize(getActivity(), binding.jid.getText().toString()));
|
||||
} catch (NumberParseException | NullPointerException e) { }
|
||||
}
|
||||
|
||||
if (p == null) {
|
||||
finish.onGatewayResult(binding.jid.getText().toString(), null);
|
||||
} else if (p.first != null) { // Gateway already responsed to jabber:iq:gateway once
|
||||
final Account acct = ((XmppActivity) getActivity()).xmppConnectionService.findAccountByJid(accountJid);
|
||||
((XmppActivity) getActivity()).xmppConnectionService.fetchFromGateway(acct, p.second.first, binding.jid.getText().toString(), finish);
|
||||
} else if (p.second.first.isDomainJid() && p.second.second.getServiceDiscoveryResult().getFeatures().contains("jid\\20escaping")) {
|
||||
finish.onGatewayResult(Jid.ofLocalAndDomain(binding.jid.getText().toString(), p.second.first.getDomain().toString()).toString(), null);
|
||||
} else if (p.second.first.isDomainJid()) {
|
||||
finish.onGatewayResult(Jid.ofLocalAndDomain(binding.jid.getText().toString().replace("@", "%"), p.second.first.getDomain().toString()).toString(), null);
|
||||
} else {
|
||||
finish.onGatewayResult(null, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -276,4 +363,201 @@ 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;
|
||||
protected Runnable onEmpty = () -> {};
|
||||
protected Runnable onNonEmpty = () -> {};
|
||||
|
||||
@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) {
|
||||
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);
|
||||
|
||||
if(binding.jid.hasFocus()) {
|
||||
binding.jid.setHint(R.string.account_settings_example_jabber_id);
|
||||
} else {
|
||||
DelayedHintHelper.setHint(R.string.account_settings_example_jabber_id, binding.jid);
|
||||
}
|
||||
} else {
|
||||
binding.jid.setThreshold(999999); // do not autocomplete
|
||||
binding.jid.setHint(null);
|
||||
binding.jid.setOnFocusChangeListener((v, hasFocus) -> {});
|
||||
binding.jidLayout.setHint(this.gateways.get(i-1).second);
|
||||
|
||||
String type = getType(i);
|
||||
if (type.equals("pstn") || type.equals("sms")) {
|
||||
binding.jid.setInputType(InputType.TYPE_CLASS_PHONE);
|
||||
} else if (type.equals("email") || type.equals("sip")) {
|
||||
binding.jid.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS);
|
||||
|
||||
if(binding.jid.hasFocus()) {
|
||||
binding.jid.setHint(R.string.account_settings_example_jabber_id);
|
||||
} else {
|
||||
DelayedHintHelper.setHint(R.string.account_settings_example_jabber_id, binding.jid);
|
||||
}
|
||||
} else {
|
||||
binding.jid.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
|
||||
}
|
||||
}
|
||||
|
||||
notifyItemChanged(old);
|
||||
notifyItemChanged(i);
|
||||
}
|
||||
|
||||
public String getLabel(Contact gateway) {
|
||||
String type = getType(gateway);
|
||||
if (type != null) return type;
|
||||
|
||||
return gateway.getDisplayName();
|
||||
}
|
||||
|
||||
public String getLabel(int i) {
|
||||
if (i == 0) return null;
|
||||
|
||||
return getLabel(this.gateways.get(i-1).first);
|
||||
}
|
||||
|
||||
public String getType(int i) {
|
||||
if (i == 0) return null;
|
||||
|
||||
return getType(this.gateways.get(i-1).first);
|
||||
}
|
||||
|
||||
public String getType(Contact gateway) {
|
||||
for(Presence p : gateway.getPresences().getPresences()) {
|
||||
ServiceDiscoveryResult.Identity id;
|
||||
if(p.getServiceDiscoveryResult() != null && (id = p.getServiceDiscoveryResult().getIdentity("gateway", null)) != null) {
|
||||
return id.getType();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getSelectedType() {
|
||||
return getType(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 setOnEmpty(Runnable r) {
|
||||
onEmpty = r;
|
||||
}
|
||||
|
||||
public void setOnNonEmpty(Runnable r) {
|
||||
onNonEmpty = r;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
gateways.clear();
|
||||
onEmpty.run();
|
||||
notifyDataSetChanged();
|
||||
setSelected(0);
|
||||
}
|
||||
|
||||
public void add(Contact gateway, String prompt) {
|
||||
if (getItemCount() < 2) onNonEmpty.run();
|
||||
this.gateways.add(new Pair<>(gateway, prompt));
|
||||
Collections.sort(this.gateways, (x, y) -> getLabel(x.first).compareTo(getLabel(y.first)));
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
package eu.siacs.conversations.xmpp;
|
||||
|
||||
public interface OnGatewayResult {
|
||||
// if prompt is null, there was an error
|
||||
// errorText may or may not be set
|
||||
public void onGatewayResult(String prompt, String errorText);
|
||||
}
|
|
@ -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"
|
||||
|
|
|
@ -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>
|
Loading…
Reference in New Issue