EnterJidDialog.java

  1package eu.siacs.conversations.ui;
  2
  3import android.app.Activity;
  4import android.app.Dialog;
  5import android.databinding.DataBindingUtil;
  6import android.os.Bundle;
  7import android.support.annotation.NonNull;
  8import android.support.v4.app.DialogFragment;
  9import android.support.v7.app.AlertDialog;
 10import android.text.Editable;
 11import android.text.TextWatcher;
 12import android.view.View;
 13import android.widget.ArrayAdapter;
 14
 15import java.util.ArrayList;
 16import java.util.Arrays;
 17import java.util.Collection;
 18import java.util.List;
 19
 20import eu.siacs.conversations.Config;
 21import eu.siacs.conversations.R;
 22import eu.siacs.conversations.databinding.EnterJidDialogBinding;
 23import eu.siacs.conversations.ui.adapter.KnownHostsAdapter;
 24import eu.siacs.conversations.ui.interfaces.OnBackendConnected;
 25import eu.siacs.conversations.ui.util.DelayedHintHelper;
 26import rocks.xmpp.addr.Jid;
 27
 28public class EnterJidDialog extends DialogFragment implements OnBackendConnected, TextWatcher {
 29
 30
 31	private static final List<String> SUSPICIOUS_DOMAINS = Arrays.asList("conference","muc","room","rooms","chat");
 32
 33	private OnEnterJidDialogPositiveListener mListener = null;
 34
 35	private static final String TITLE_KEY = "title";
 36	private static final String POSITIVE_BUTTON_KEY = "positive_button";
 37	private static final String PREFILLED_JID_KEY = "prefilled_jid";
 38	private static final String ACCOUNT_KEY = "account";
 39	private static final String ALLOW_EDIT_JID_KEY = "allow_edit_jid";
 40	private static final String ACCOUNTS_LIST_KEY = "activated_accounts_list";
 41	private static final String SANITY_CHECK_JID = "sanity_check_jid";
 42
 43	private KnownHostsAdapter knownHostsAdapter;
 44
 45	private EnterJidDialogBinding binding;
 46	private AlertDialog dialog;
 47	private boolean sanityCheckJid = false;
 48
 49
 50	private boolean issuedWarning = false;
 51
 52	public static EnterJidDialog newInstance(final List<String> activatedAccounts,
 53	                                         final String title, final String positiveButton,
 54	                                         final String prefilledJid, final String account,
 55											 boolean allowEditJid, final boolean sanity_check_jid) {
 56		EnterJidDialog dialog = new EnterJidDialog();
 57		Bundle bundle = new Bundle();
 58		bundle.putString(TITLE_KEY, title);
 59		bundle.putString(POSITIVE_BUTTON_KEY, positiveButton);
 60		bundle.putString(PREFILLED_JID_KEY, prefilledJid);
 61		bundle.putString(ACCOUNT_KEY, account);
 62		bundle.putBoolean(ALLOW_EDIT_JID_KEY, allowEditJid);
 63		bundle.putStringArrayList(ACCOUNTS_LIST_KEY, (ArrayList<String>) activatedAccounts);
 64		bundle.putBoolean(SANITY_CHECK_JID, sanity_check_jid);
 65		dialog.setArguments(bundle);
 66		return dialog;
 67	}
 68
 69	@Override
 70	public void onActivityCreated(Bundle savedInstanceState) {
 71		super.onActivityCreated(savedInstanceState);
 72		setRetainInstance(true);
 73	}
 74
 75	@Override
 76	public void onStart() {
 77		super.onStart();
 78		final Activity activity = getActivity();
 79		if (activity instanceof XmppActivity && ((XmppActivity) activity).xmppConnectionService != null) {
 80			refreshKnownHosts();
 81		}
 82	}
 83
 84	@NonNull
 85	@Override
 86	public Dialog onCreateDialog(Bundle savedInstanceState) {
 87		final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
 88		builder.setTitle(getArguments().getString(TITLE_KEY));
 89		binding = DataBindingUtil.inflate(getActivity().getLayoutInflater(), R.layout.enter_jid_dialog, null, false);
 90		this.knownHostsAdapter = new KnownHostsAdapter(getActivity(), R.layout.simple_list_item);
 91		binding.jid.setAdapter(this.knownHostsAdapter);
 92		binding.jid.addTextChangedListener(this);
 93		String prefilledJid = getArguments().getString(PREFILLED_JID_KEY);
 94		if (prefilledJid != null) {
 95			binding.jid.append(prefilledJid);
 96			if (!getArguments().getBoolean(ALLOW_EDIT_JID_KEY)) {
 97				binding.jid.setFocusable(false);
 98				binding.jid.setFocusableInTouchMode(false);
 99				binding.jid.setClickable(false);
100				binding.jid.setCursorVisible(false);
101			}
102		}
103		sanityCheckJid = getArguments().getBoolean(SANITY_CHECK_JID, false);
104
105		DelayedHintHelper.setHint(R.string.account_settings_example_jabber_id, binding.jid);
106
107		String account = getArguments().getString(ACCOUNT_KEY);
108		if (account == null) {
109			StartConversationActivity.populateAccountSpinner(getActivity(), getArguments().getStringArrayList(ACCOUNTS_LIST_KEY), binding.account);
110		} else {
111			ArrayAdapter<String> adapter = new ArrayAdapter<>(getActivity(),
112					R.layout.simple_list_item,
113					new String[]{account});
114			binding.account.setEnabled(false);
115			adapter.setDropDownViewResource(R.layout.simple_list_item);
116			binding.account.setAdapter(adapter);
117		}
118
119
120
121		builder.setView(binding.getRoot());
122		builder.setNegativeButton(R.string.cancel, null);
123		builder.setPositiveButton(getArguments().getString(POSITIVE_BUTTON_KEY), null);
124		this.dialog = builder.create();
125
126		View.OnClickListener dialogOnClick = v -> {
127			handleEnter(binding, account);
128		};
129
130		binding.jid.setOnEditorActionListener((v, actionId, event) -> {
131			handleEnter(binding, account);
132			return true;
133		});
134
135		dialog.show();
136		dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(dialogOnClick);
137		return dialog;
138	}
139
140	private void handleEnter(EnterJidDialogBinding binding, String account) {
141		final Jid accountJid;
142		if (!binding.account.isEnabled() && account == null) {
143			return;
144		}
145		try {
146			if (Config.DOMAIN_LOCK != null) {
147				accountJid = Jid.of((String) binding.account.getSelectedItem(), Config.DOMAIN_LOCK, null);
148			} else {
149				accountJid = Jid.of((String) binding.account.getSelectedItem());
150			}
151		} catch (final IllegalArgumentException e) {
152			return;
153		}
154		final Jid contactJid;
155		try {
156			contactJid = Jid.of(binding.jid.getText().toString());
157		} catch (final IllegalArgumentException e) {
158			binding.jid.setError(getActivity().getString(R.string.invalid_jid));
159			return;
160		}
161
162		if (!issuedWarning && sanityCheckJid) {
163			if (contactJid.isDomainJid()) {
164				binding.jid.setError(getActivity().getString(R.string.this_looks_like_a_domain));
165				dialog.getButton(AlertDialog.BUTTON_POSITIVE).setText(R.string.add_anway);
166				issuedWarning = true;
167				return;
168			}
169			if (suspiciousSubDomain(contactJid.getDomain())) {
170				binding.jid.setError(getActivity().getString(R.string.this_looks_like_channel));
171				dialog.getButton(AlertDialog.BUTTON_POSITIVE).setText(R.string.add_anway);
172				issuedWarning = true;
173				return;
174			}
175		}
176
177		if (mListener != null) {
178			try {
179				if (mListener.onEnterJidDialogPositive(accountJid, contactJid)) {
180					dialog.dismiss();
181				}
182			} catch (JidError error) {
183				binding.jid.setError(error.toString());
184			}
185		}
186	}
187
188	public void setOnEnterJidDialogPositiveListener(OnEnterJidDialogPositiveListener listener) {
189		this.mListener = listener;
190	}
191
192	@Override
193	public void onBackendConnected() {
194		refreshKnownHosts();
195	}
196
197	private void refreshKnownHosts() {
198		Activity activity = getActivity();
199		if (activity instanceof XmppActivity) {
200			Collection<String> hosts = ((XmppActivity) activity).xmppConnectionService.getKnownHosts();
201			this.knownHostsAdapter.refresh(hosts);
202		}
203	}
204
205	@Override
206	public void beforeTextChanged(CharSequence s, int start, int count, int after) {
207
208	}
209
210	@Override
211	public void onTextChanged(CharSequence s, int start, int before, int count) {
212
213	}
214
215	@Override
216	public void afterTextChanged(Editable s) {
217		if (issuedWarning) {
218			dialog.getButton(AlertDialog.BUTTON_POSITIVE).setText(R.string.add);
219			issuedWarning = false;
220		}
221	}
222
223	public interface OnEnterJidDialogPositiveListener {
224		boolean onEnterJidDialogPositive(Jid account, Jid contact) throws EnterJidDialog.JidError;
225	}
226
227	public static class JidError extends Exception {
228		final String msg;
229
230		public JidError(final String msg) {
231			this.msg = msg;
232		}
233
234		public String toString() {
235			return msg;
236		}
237	}
238
239	@Override
240	public void onDestroyView() {
241		Dialog dialog = getDialog();
242		if (dialog != null && getRetainInstance()) {
243			dialog.setDismissMessage(null);
244		}
245		super.onDestroyView();
246	}
247
248	private static boolean suspiciousSubDomain(String domain) {
249		final String[] parts = domain.split("\\.");
250		return parts.length >= 3 && SUSPICIOUS_DOMAINS.contains(parts[0]);
251	}
252}