From d04e2e99f3d1e091cd6a5bdb2a26d1757ce542ae Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Thu, 11 Apr 2019 08:57:20 +0200 Subject: [PATCH 01/33] use mediated invite on reinvite if user is not member --- .../conversations/ui/util/MucDetailsContextMenuHelper.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/eu/siacs/conversations/ui/util/MucDetailsContextMenuHelper.java b/src/main/java/eu/siacs/conversations/ui/util/MucDetailsContextMenuHelper.java index dc1f9cea0..04198e781 100644 --- a/src/main/java/eu/siacs/conversations/ui/util/MucDetailsContextMenuHelper.java +++ b/src/main/java/eu/siacs/conversations/ui/util/MucDetailsContextMenuHelper.java @@ -179,7 +179,11 @@ public final class MucDetailsContextMenuHelper { activity.privateMsgInMuc(conversation, user.getName()); return true; case R.id.invite: - activity.xmppConnectionService.directInvite(conversation, jid); + if (user.getAffiliation().ranks(MucOptions.Affiliation.MEMBER)) { + activity.xmppConnectionService.directInvite(conversation, jid); + } else { + activity.xmppConnectionService.invite(conversation, jid); + } return true; default: return false; From 98734353aa171460d0cf51a3b6aeb7674a883df5 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Wed, 17 Apr 2019 09:56:09 +0200 Subject: [PATCH 02/33] pulled translations from transifex --- src/main/res/values-de/strings.xml | 4 ++-- src/main/res/values-eu/strings.xml | 5 +++++ src/main/res/values-fr/strings.xml | 8 ++++---- src/main/res/values-it/strings.xml | 1 + src/main/res/values-pl/strings.xml | 1 + 5 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/main/res/values-de/strings.xml b/src/main/res/values-de/strings.xml index 78016e0bc..25f030385 100644 --- a/src/main/res/values-de/strings.xml +++ b/src/main/res/values-de/strings.xml @@ -706,7 +706,7 @@ Mittel Groß Die Nachricht wurde nicht für dieses Gerät verschlüsselt. - Die OMEMO-Nachricht konnte nicht entschlüsselt werden. + OMEMO-Nachricht konnte nicht entschlüsselt werden. rückgängig Standort teilen ist deaktiviert Position fixieren @@ -756,7 +756,7 @@ Teilnehmer anzeigen Teilnehmer Medienbrowser - Die Datei wurde aufgrund von Sicherheitsverletzungen ausgelassen. + Datei wurde aufgrund von Sicherheitsverletzungen ausgelassen. Videoqualität Geringere Qualität ermöglicht kleinere Dateien Mittel (360p) diff --git a/src/main/res/values-eu/strings.xml b/src/main/res/values-eu/strings.xml index 49bc34486..b7fa0461f 100644 --- a/src/main/res/values-eu/strings.xml +++ b/src/main/res/values-eu/strings.xml @@ -502,6 +502,7 @@ Conversationsek kanpoko biltegirako sarbidea behar du Conversationsek kamerarako sarbidea behar du Kontaktuekin sinkronizatu + Conversationsek zerbitzariko zure kontaktuen zerrenda eta zure helbide liburu lokala uztartu nahi ditu haien izenak eta argazkiak erakusteko.\n\nConversationsek zure kontaktuak modu lokalean soilik irakurri eta uztartuko ditu, zure zerbitzarira kargatu gabe.\n\nJarraian baimenak eskatuko zaizkizu zure kontaktuetara sartu ahal izateko. Mezu guztiak jakinarazi Jakinarazi aipatua izaterakoan soilik Jakinarazpenak ezgaituta @@ -524,7 +525,9 @@ Segurtasun akatsa: fitxategi sarrera baliogabea Ez da aplikaziorik aurkitu URIa partekatzeko URIa honekin partekatu... + XMPP hornitzaleez independiente den protokolo bat da. Bezero hau aukeratzen duzun edozein XMPP zerbitzariarekin erabili dezakezu.\nHala ere zure erosotasunerako conversations.im¹-en, Conversationsekin bereziki erabiltzeko egokia den hornitzaile batean, kontu bat sortzea erraz egin dugu. Onartu eta jarraitu + conversations.im-en kontu bat sortzeko prozesuan zehar gidatuko zaitugu.\nconversations.im¹ hornitzaile bezala aukeratzerakoan beste hornitzaileen erabiltzaileekin komunikatzeko gai izango zara haiei zure XMPP helbide osoa emanez. Zure XMPP helbide osoa hau izango da: %s Kontua sortu Nire hornitzale propioa erabili @@ -817,6 +820,7 @@ Babes-kopia berrezarri Berrezarri Sartu %s kontuaren pasahitza babes-kopia berrezartzeko. + Ez erabili babes-kopiak berrezartzeko ezaugarria instalazio bat klonatzeko (aldi berean exekutatzeko). Babes-kopia bat berrezartzea migrazioetarako edo jatorrizko gailua galdu duzunerako da soilik. Ezin izan da babes-kopia berrezarri. Ezin izan da babes-kopia desenkriptatu. Pasahitza zuzena da? Babes-kopiak egin eta berrezarri @@ -848,4 +852,5 @@ Baimenak kudeatu Parte-hartzaileak bilatu Fitxategia handiegia da + Erantsi diff --git a/src/main/res/values-fr/strings.xml b/src/main/res/values-fr/strings.xml index c9fd64763..b666441eb 100644 --- a/src/main/res/values-fr/strings.xml +++ b/src/main/res/values-fr/strings.xml @@ -229,7 +229,7 @@ Supprimer le favori Détruire le chat en groupe Détruire le canal - Voulez-vous vraiment détruire cette discussion de groupe? \ N \ nAvertissement: la discussion de groupe sera complètement supprimée du serveur. + Voulez-vous vraiment détruire cette discussion de groupe?\n\nAvertissement: la discussion de groupe sera complètement supprimée du serveur. Êtes-vous sûr de vouloir détruire ce canal public? \ On \ Avertissement: le canal sera complètement supprimé du serveur. Impossible de détruire le chat en groupe Impossible de détruire le canal @@ -502,8 +502,8 @@ Conversations a besoin d\'accéder au stockage externe Conversations a besoin d\'accéder à l\'appareil photo Synchroniser avec contacts - Conversations veut faire correspondre votre liste de contacts côté serveur avec votre carnet d\'adresses local pour afficher les noms complets et les avatars de vos contacts. \ N \ nLes conversions liront uniquement vos contacts et les feront correspondre localement sans les télécharger sur votre serveur. \ N \ nVous allez maintenant être invité à donner la permission d\'accéder à vos contacts. -
Nous ne stockerons pas de copie de ces numéros. \ N \ nPour plus d\'informations, lisez notre politique de confidentialité.

maintenant être invité à donner la permission d\'accéder à vos contacts.]]>
+ Conversations veut faire correspondre votre liste de contacts côté serveur avec votre carnet d\'adresses local pour afficher les noms complets et les avatars de vos contacts.\n\nLes conversions liront uniquement vos contacts et les feront correspondre localement sans les télécharger sur votre serveur.\n\nVous allez maintenant être invité à donner la permission d\'accéder à vos contacts. +
Nous ne stockerons pas de copie de ces numéros.\n\nPour plus d\'informations, lisez notre politique de confidentialité.

maintenant être invité à donner la permission d\'accéder à vos contacts.]]>
Notifier pour tous les messages Notifier seulement en cas de mention Notifications désactivées @@ -529,7 +529,7 @@ XMPP est un réseau de messagerie instantanée indépendant du fournisseur. Vous pouvez utiliser ce client avec le serveur XMPP de votre choix. \ NCependant, pour votre commodité, nous avons facilité la création d\'un compte pour les conversations.im¹; un fournisseur spécialement adapté à l\'utilisation avec Conversations.
Vous vous inscrivez avec votre numéro de téléphone et Quicksy va automatiquement, en fonction des numéros de votre carnet d’adresses, vous suggérer d’éventuels contacts.

en vous inscrivant, vous acceptez notre politique de confidentialité.]]>
Accepter & continuer - Nous vous guiderons tout au long du processus de création d\'un compte sur conversations.im.¹ \ nLorsque vous sélectionnerez conversations.im en tant que fournisseur, vous pourrez communiquer avec les utilisateurs d\'autres fournisseurs en leur fournissant votre adresse XMPP complète. + Nous vous guiderons tout au long du processus de création d\'un compte sur conversations.im.¹\nLorsque vous sélectionnerez conversations.im en tant que fournisseur, vous pourrez communiquer avec les utilisateurs d\'autres fournisseurs en leur fournissant votre adresse XMPP complète. Votre adresse XMPP complète sera : %s Créer un compte Utiliser votre propre fournisseur diff --git a/src/main/res/values-it/strings.xml b/src/main/res/values-it/strings.xml index c9411d91e..6d3cf7ee2 100644 --- a/src/main/res/values-it/strings.xml +++ b/src/main/res/values-it/strings.xml @@ -854,4 +854,5 @@ Gestisci i privilegi Cerca partecipanti File troppo grande + Allega diff --git a/src/main/res/values-pl/strings.xml b/src/main/res/values-pl/strings.xml index 2d4c5b196..a624d6052 100644 --- a/src/main/res/values-pl/strings.xml +++ b/src/main/res/values-pl/strings.xml @@ -872,4 +872,5 @@ Administrator twojego serwera będzie mógł czytać twoje wiadomości, ale moż Zarządzaj uprawnieniami Wyszukaj uczestników Plik jest zbyt duży + Dołącz From 39bc0674059b6717cd74f4ea23c763d093dcaad4 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Wed, 17 Apr 2019 12:49:36 +0200 Subject: [PATCH 03/33] execute some dismiss error notification and reset error in background --- .../siacs/conversations/services/XmppConnectionService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index fa3e524a5..41e3ff8d9 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -920,7 +920,7 @@ public class XmppConnectionService extends Service { } } if (account.setShowErrorNotification(true)) { - databaseBackend.updateAccount(account); + mDatabaseWriterExecutor.execute(() -> databaseBackend.updateAccount(account)); } } mNotificationService.updateErrorNotification(); @@ -931,7 +931,7 @@ public class XmppConnectionService extends Service { if (account.hasErrorStatus()) { Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": dismissing error notification"); if (account.setShowErrorNotification(false)) { - databaseBackend.updateAccount(account); + mDatabaseWriterExecutor.execute(() -> databaseBackend.updateAccount(account)); } } } From 7a825231fbb2338ef44b86943d0fdd0c4e977b63 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Wed, 17 Apr 2019 18:25:21 +0200 Subject: [PATCH 04/33] run keyboard listeners on background executor --- .../conversations/ui/widget/EditMessage.java | 302 +++++++++--------- 1 file changed, 155 insertions(+), 147 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/ui/widget/EditMessage.java b/src/main/java/eu/siacs/conversations/ui/widget/EditMessage.java index 359571dd3..1ce695ab3 100644 --- a/src/main/java/eu/siacs/conversations/ui/widget/EditMessage.java +++ b/src/main/java/eu/siacs/conversations/ui/widget/EditMessage.java @@ -19,176 +19,184 @@ import android.view.KeyEvent; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + import eu.siacs.conversations.Config; import eu.siacs.conversations.R; public class EditMessage extends EmojiWrapperEditText { - private static final InputFilter SPAN_FILTER = (source, start, end, dest, dstart, dend) -> source instanceof Spanned ? source.toString() : source; - protected Handler mTypingHandler = new Handler(); - protected KeyboardListener keyboardListener; - private OnCommitContentListener mCommitContentListener = null; - private String[] mimeTypes = null; - private boolean isUserTyping = false; - protected Runnable mTypingTimeout = new Runnable() { - @Override - public void run() { - if (isUserTyping && keyboardListener != null) { - keyboardListener.onTypingStopped(); - isUserTyping = false; - } - } - }; - private boolean lastInputWasTab = false; + private static final InputFilter SPAN_FILTER = (source, start, end, dest, dstart, dend) -> source instanceof Spanned ? source.toString() : source; + private final ExecutorService executor = Executors.newSingleThreadExecutor(); + protected Handler mTypingHandler = new Handler(); + protected KeyboardListener keyboardListener; + private OnCommitContentListener mCommitContentListener = null; + private String[] mimeTypes = null; + private boolean isUserTyping = false; + private final Runnable mTypingTimeout = new Runnable() { + @Override + public void run() { + if (isUserTyping && keyboardListener != null) { + keyboardListener.onTypingStopped(); + isUserTyping = false; + } + } + }; + private boolean lastInputWasTab = false; - public EditMessage(Context context, AttributeSet attrs) { - super(context, attrs); - } + public EditMessage(Context context, AttributeSet attrs) { + super(context, attrs); + } - public EditMessage(Context context) { - super(context); - } + public EditMessage(Context context) { + super(context); + } - @Override - public boolean onKeyDown(int keyCode, KeyEvent e) { - if (keyCode == KeyEvent.KEYCODE_ENTER && !e.isShiftPressed()) { - lastInputWasTab = false; - if (keyboardListener != null && keyboardListener.onEnterPressed()) { - return true; - } - } else if (keyCode == KeyEvent.KEYCODE_TAB && !e.isAltPressed() && !e.isCtrlPressed()) { - if (keyboardListener != null && keyboardListener.onTabPressed(this.lastInputWasTab)) { - lastInputWasTab = true; - return true; - } - } else { - lastInputWasTab = false; - } - return super.onKeyDown(keyCode, e); - } + @Override + public boolean onKeyDown(int keyCode, KeyEvent e) { + if (keyCode == KeyEvent.KEYCODE_ENTER && !e.isShiftPressed()) { + lastInputWasTab = false; + if (keyboardListener != null && keyboardListener.onEnterPressed()) { + return true; + } + } else if (keyCode == KeyEvent.KEYCODE_TAB && !e.isAltPressed() && !e.isCtrlPressed()) { + if (keyboardListener != null && keyboardListener.onTabPressed(this.lastInputWasTab)) { + lastInputWasTab = true; + return true; + } + } else { + lastInputWasTab = false; + } + return super.onKeyDown(keyCode, e); + } - @Override - public int getAutofillType() { - return AUTOFILL_TYPE_NONE; - } + @Override + public int getAutofillType() { + return AUTOFILL_TYPE_NONE; + } - @Override - public void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) { - super.onTextChanged(text, start, lengthBefore, lengthAfter); - lastInputWasTab = false; - if (this.mTypingHandler != null && this.keyboardListener != null) { - this.mTypingHandler.removeCallbacks(mTypingTimeout); - this.mTypingHandler.postDelayed(mTypingTimeout, Config.TYPING_TIMEOUT * 1000); - final int length = text.length(); - if (!isUserTyping && length > 0) { - this.isUserTyping = true; - this.keyboardListener.onTypingStarted(); - } else if (length == 0) { - this.isUserTyping = false; - this.keyboardListener.onTextDeleted(); - } - this.keyboardListener.onTextChanged(); - } - } - public void setKeyboardListener(KeyboardListener listener) { - this.keyboardListener = listener; - if (listener != null) { - this.isUserTyping = false; - } - } + @Override + public void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) { + super.onTextChanged(text, start, lengthBefore, lengthAfter); + lastInputWasTab = false; + if (this.mTypingHandler != null && this.keyboardListener != null) { + executor.execute(() -> triggerKeyboardEvents(text.length())); + } + } - @Override - public boolean onTextContextMenuItem(int id) { - if (id == android.R.id.paste) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - return super.onTextContextMenuItem(android.R.id.pasteAsPlainText); - } else { - Editable editable = getEditableText(); - InputFilter[] filters = editable.getFilters(); - InputFilter[] tempFilters = new InputFilter[filters != null ? filters.length + 1 : 1]; - if (filters != null) { - System.arraycopy(filters, 0, tempFilters, 1, filters.length); - } - tempFilters[0] = SPAN_FILTER; - editable.setFilters(tempFilters); - try { - return super.onTextContextMenuItem(id); - } finally { - editable.setFilters(filters); - } - } - } else { - return super.onTextContextMenuItem(id); - } - } + private void triggerKeyboardEvents(final int length) { + this.mTypingHandler.removeCallbacks(mTypingTimeout); + this.mTypingHandler.postDelayed(mTypingTimeout, Config.TYPING_TIMEOUT * 1000); + if (!isUserTyping && length > 0) { + this.isUserTyping = true; + this.keyboardListener.onTypingStarted(); + } else if (length == 0) { + this.isUserTyping = false; + this.keyboardListener.onTextDeleted(); + } + this.keyboardListener.onTextChanged(); + } - public void setRichContentListener(String[] mimeTypes, OnCommitContentListener listener) { - this.mimeTypes = mimeTypes; - this.mCommitContentListener = listener; - } + public void setKeyboardListener(KeyboardListener listener) { + this.keyboardListener = listener; + if (listener != null) { + this.isUserTyping = false; + } + } - public void insertAsQuote(String text) { - text = text.replaceAll("(\n *){2,}", "\n").replaceAll("(^|\n)", "$1> ").replaceAll("\n$", ""); - Editable editable = getEditableText(); - int position = getSelectionEnd(); - if (position == -1) position = editable.length(); - if (position > 0 && editable.charAt(position - 1) != '\n') { - editable.insert(position++, "\n"); - } - editable.insert(position, text); - position += text.length(); - editable.insert(position++, "\n"); - if (position < editable.length() && editable.charAt(position) != '\n') { - editable.insert(position, "\n"); - } - setSelection(position); - } + @Override + public boolean onTextContextMenuItem(int id) { + if (id == android.R.id.paste) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + return super.onTextContextMenuItem(android.R.id.pasteAsPlainText); + } else { + Editable editable = getEditableText(); + InputFilter[] filters = editable.getFilters(); + InputFilter[] tempFilters = new InputFilter[filters != null ? filters.length + 1 : 1]; + if (filters != null) { + System.arraycopy(filters, 0, tempFilters, 1, filters.length); + } + tempFilters[0] = SPAN_FILTER; + editable.setFilters(tempFilters); + try { + return super.onTextContextMenuItem(id); + } finally { + editable.setFilters(filters); + } + } + } else { + return super.onTextContextMenuItem(id); + } + } - @Override - public InputConnection onCreateInputConnection(EditorInfo editorInfo) { - final InputConnection ic = super.onCreateInputConnection(editorInfo); + public void setRichContentListener(String[] mimeTypes, OnCommitContentListener listener) { + this.mimeTypes = mimeTypes; + this.mCommitContentListener = listener; + } - if (mimeTypes != null && mCommitContentListener != null && ic != null) { - EditorInfoCompat.setContentMimeTypes(editorInfo, mimeTypes); - return InputConnectionCompat.createWrapper(ic, editorInfo, (inputContentInfo, flags, opts) -> EditMessage.this.mCommitContentListener.onCommitContent(inputContentInfo, flags, opts, mimeTypes)); - } else { - return ic; - } - } + public void insertAsQuote(String text) { + text = text.replaceAll("(\n *){2,}", "\n").replaceAll("(^|\n)", "$1> ").replaceAll("\n$", ""); + Editable editable = getEditableText(); + int position = getSelectionEnd(); + if (position == -1) position = editable.length(); + if (position > 0 && editable.charAt(position - 1) != '\n') { + editable.insert(position++, "\n"); + } + editable.insert(position, text); + position += text.length(); + editable.insert(position++, "\n"); + if (position < editable.length() && editable.charAt(position) != '\n') { + editable.insert(position, "\n"); + } + setSelection(position); + } - public void refreshIme() { - SharedPreferences p = PreferenceManager.getDefaultSharedPreferences(getContext()); - final boolean usingEnterKey = p.getBoolean("display_enter_key", getResources().getBoolean(R.bool.display_enter_key)); - final boolean enterIsSend = p.getBoolean("enter_is_send", getResources().getBoolean(R.bool.enter_is_send)); + @Override + public InputConnection onCreateInputConnection(EditorInfo editorInfo) { + final InputConnection ic = super.onCreateInputConnection(editorInfo); - if (usingEnterKey && enterIsSend) { - setInputType(getInputType() & (~InputType.TYPE_TEXT_FLAG_MULTI_LINE)); - setInputType(getInputType() & (~InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE)); - } else if (usingEnterKey) { - setInputType(getInputType() | InputType.TYPE_TEXT_FLAG_MULTI_LINE); - setInputType(getInputType() & (~InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE)); - } else { - setInputType(getInputType() | InputType.TYPE_TEXT_FLAG_MULTI_LINE); - setInputType(getInputType() | InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE); - } - } + if (mimeTypes != null && mCommitContentListener != null && ic != null) { + EditorInfoCompat.setContentMimeTypes(editorInfo, mimeTypes); + return InputConnectionCompat.createWrapper(ic, editorInfo, (inputContentInfo, flags, opts) -> EditMessage.this.mCommitContentListener.onCommitContent(inputContentInfo, flags, opts, mimeTypes)); + } else { + return ic; + } + } - public interface OnCommitContentListener { - boolean onCommitContent(InputContentInfoCompat inputContentInfo, int flags, Bundle opts, String[] mimeTypes); - } + public void refreshIme() { + SharedPreferences p = PreferenceManager.getDefaultSharedPreferences(getContext()); + final boolean usingEnterKey = p.getBoolean("display_enter_key", getResources().getBoolean(R.bool.display_enter_key)); + final boolean enterIsSend = p.getBoolean("enter_is_send", getResources().getBoolean(R.bool.enter_is_send)); - public interface KeyboardListener { - boolean onEnterPressed(); + if (usingEnterKey && enterIsSend) { + setInputType(getInputType() & (~InputType.TYPE_TEXT_FLAG_MULTI_LINE)); + setInputType(getInputType() & (~InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE)); + } else if (usingEnterKey) { + setInputType(getInputType() | InputType.TYPE_TEXT_FLAG_MULTI_LINE); + setInputType(getInputType() & (~InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE)); + } else { + setInputType(getInputType() | InputType.TYPE_TEXT_FLAG_MULTI_LINE); + setInputType(getInputType() | InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE); + } + } - void onTypingStarted(); + public interface OnCommitContentListener { + boolean onCommitContent(InputContentInfoCompat inputContentInfo, int flags, Bundle opts, String[] mimeTypes); + } - void onTypingStopped(); + public interface KeyboardListener { + boolean onEnterPressed(); - void onTextDeleted(); + void onTypingStarted(); - void onTextChanged(); + void onTypingStopped(); - boolean onTabPressed(boolean repeated); - } + void onTextDeleted(); + + void onTextChanged(); + + boolean onTabPressed(boolean repeated); + } } From a0f88aa9b4292e22b18bf8b19b2e722b815012b8 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sun, 31 Mar 2019 17:12:01 +0200 Subject: [PATCH 05/33] implement channel discovery over jabber.search.network --- .../res/layout/activity_channel_discovery.xml | 33 ++++ .../res/layout/search_result_item.xml | 48 +++++ src/main/AndroidManifest.xml | 1 + .../java/eu/siacs/conversations/Config.java | 3 + .../entities/ChannelSearchResult.java | 35 ++++ .../conversations/services/AvatarService.java | 7 + .../services/XmppConnectionService.java | 55 ++++++ .../ui/ChannelDiscoveryActivity.java | 165 ++++++++++++++++++ .../ui/StartConversationActivity.java | 13 +- .../adapter/ChannelSearchResultAdapter.java | 78 +++++++++ .../conversations/utils/AccountUtils.java | 16 ++ .../res/menu/channel_discovery_activity.xml | 11 ++ .../menu/start_conversation_fab_submenu.xml | 4 + src/main/res/values/strings.xml | 4 + 14 files changed, 464 insertions(+), 9 deletions(-) create mode 100644 src/conversations/res/layout/activity_channel_discovery.xml create mode 100644 src/conversations/res/layout/search_result_item.xml create mode 100644 src/main/java/eu/siacs/conversations/entities/ChannelSearchResult.java create mode 100644 src/main/java/eu/siacs/conversations/ui/ChannelDiscoveryActivity.java create mode 100644 src/main/java/eu/siacs/conversations/ui/adapter/ChannelSearchResultAdapter.java create mode 100644 src/main/res/menu/channel_discovery_activity.xml diff --git a/src/conversations/res/layout/activity_channel_discovery.xml b/src/conversations/res/layout/activity_channel_discovery.xml new file mode 100644 index 000000000..679c5156e --- /dev/null +++ b/src/conversations/res/layout/activity_channel_discovery.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/conversations/res/layout/search_result_item.xml b/src/conversations/res/layout/search_result_item.xml new file mode 100644 index 000000000..2d77be1fb --- /dev/null +++ b/src/conversations/res/layout/search_result_item.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index b6a8faac1..6b43106f9 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -283,6 +283,7 @@ + diff --git a/src/main/java/eu/siacs/conversations/Config.java b/src/main/java/eu/siacs/conversations/Config.java index 427502487..4626bffc8 100644 --- a/src/main/java/eu/siacs/conversations/Config.java +++ b/src/main/java/eu/siacs/conversations/Config.java @@ -40,6 +40,9 @@ public final class Config { public static final String DOMAIN_LOCK = null; //only allow account creation for this domain public static final String MAGIC_CREATE_DOMAIN = "conversations.im"; public static final String QUICKSY_DOMAIN = "quicksy.im"; + + public static final Jid CHANNEL_DISCOVERY = Jid.of("rodrigo.de.mucobedo@dreckshal.de"); + public static final boolean DISALLOW_REGISTRATION_IN_UI = false; //hide the register checkbox public static final boolean USE_RANDOM_RESOURCE_ON_EVERY_BIND = false; diff --git a/src/main/java/eu/siacs/conversations/entities/ChannelSearchResult.java b/src/main/java/eu/siacs/conversations/entities/ChannelSearchResult.java new file mode 100644 index 000000000..0222f2f85 --- /dev/null +++ b/src/main/java/eu/siacs/conversations/entities/ChannelSearchResult.java @@ -0,0 +1,35 @@ +package eu.siacs.conversations.entities; + +import eu.siacs.conversations.services.AvatarService; +import eu.siacs.conversations.utils.UIHelper; +import rocks.xmpp.addr.Jid; + +public class ChannelSearchResult implements AvatarService.Avatarable { + + private final String name; + private final String description; + private final Jid room; + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public Jid getRoom() { + return room; + } + + public ChannelSearchResult(String name, String description, Jid room) { + this.name = name; + this.description = description; + this.room = room; + } + + @Override + public int getAvatarBackgroundColor() { + return UIHelper.getColorForName(room != null ? room.asBareJid().toEscapedString() : getName()); + } +} diff --git a/src/main/java/eu/siacs/conversations/services/AvatarService.java b/src/main/java/eu/siacs/conversations/services/AvatarService.java index f4807a11b..055c50005 100644 --- a/src/main/java/eu/siacs/conversations/services/AvatarService.java +++ b/src/main/java/eu/siacs/conversations/services/AvatarService.java @@ -33,6 +33,7 @@ import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Bookmark; +import eu.siacs.conversations.entities.ChannelSearchResult; import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Conversational; @@ -82,11 +83,17 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded { return get((ListItem) avatarable, size, cachedOnly); } else if (avatarable instanceof MucOptions.User) { return get((MucOptions.User) avatarable, size, cachedOnly); + } else if (avatarable instanceof ChannelSearchResult) { + return get((ChannelSearchResult) avatarable, size, cachedOnly); } throw new AssertionError("AvatarService does not know how to generate avatar from "+avatarable.getClass().getName()); } + private Bitmap get(final ChannelSearchResult result, final int size, boolean cacheOnly) { + return get(result.getName(), result.getRoom().asBareJid().toEscapedString(), size, cacheOnly); + } + private Bitmap get(final Contact contact, final int size, boolean cachedOnly) { if (contact.isSelf()) { return get(contact.getAccount(), size, cachedOnly); diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index 41e3ff8d9..3c2164902 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -49,6 +49,7 @@ import org.openintents.openpgp.util.OpenPgpServiceConnection; import java.io.File; import java.net.URL; +import java.nio.channels.Channel; import java.security.SecureRandom; import java.security.Security; import java.security.cert.CertificateException; @@ -84,6 +85,7 @@ import eu.siacs.conversations.crypto.axolotl.XmppAxolotlMessage; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Blockable; import eu.siacs.conversations.entities.Bookmark; +import eu.siacs.conversations.entities.ChannelSearchResult; import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Conversational; @@ -112,6 +114,7 @@ import eu.siacs.conversations.ui.UiCallback; import eu.siacs.conversations.ui.interfaces.OnAvatarPublication; import eu.siacs.conversations.ui.interfaces.OnMediaLoaded; import eu.siacs.conversations.ui.interfaces.OnSearchResultsAvailable; +import eu.siacs.conversations.utils.AccountUtils; import eu.siacs.conversations.utils.Compatibility; import eu.siacs.conversations.utils.ConversationsFileObserver; import eu.siacs.conversations.utils.CryptoHelper; @@ -795,6 +798,58 @@ public class XmppConnectionService extends Service { return pingNow; } + public void discoverChannels(String query, OnChannelSearchResultsFound listener) { + IqPacket packet = new IqPacket(IqPacket.TYPE.GET); + packet.setTo(Config.CHANNEL_DISCOVERY); + Element search = packet.addChild("search","https://xmlns.zombofant.net/muclumbus/search/1.0"); + search.addChild("set","http://jabber.org/protocol/rsm").addChild("max").setContent("100"); + Bundle bundle = new Bundle(); + if (!TextUtils.isEmpty(query)) { + bundle.putString("q",query); + } + Data data = Data.create("https://xmlns.zombofant.net/muclumbus/search/1.0#params", bundle); + search.addChild(data); + final Account account = AccountUtils.getFirstEnabled(this); + if (account == null) { + return; + } + sendIqPacket(account, packet, new OnIqPacketReceived() { + @Override + public void onIqPacketReceived(Account account, IqPacket response) { + ArrayList searchResults = new ArrayList<>(); + if (response.getType() == IqPacket.TYPE.RESULT) { + Element result = response.findChild("result","https://xmlns.zombofant.net/muclumbus/search/1.0"); + if (result != null) { + for(Element child : result.getChildren()) { + if ("item".equals(child.getName())) { + String name = child.findChildContent("name"); + String description = child.findChildContent("description"); + Jid room = child.getAttributeAsJid("address"); + if (room != null) { + searchResults.add(new ChannelSearchResult(name,description,room)); + } else { + Log.d(Config.LOGTAG,"skipping because room was null"); + } + } + } + } else { + Log.d(Config.LOGTAG,"result was null"); + } + } else { + Log.d(Config.LOGTAG,response.toString()); + } + if (listener != null) { + listener.onChannelSearchResultsFound(searchResults); + } + + } + }); + } + + public interface OnChannelSearchResultsFound { + void onChannelSearchResultsFound(List results); + } + public boolean isDataSaverDisabled() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE); diff --git a/src/main/java/eu/siacs/conversations/ui/ChannelDiscoveryActivity.java b/src/main/java/eu/siacs/conversations/ui/ChannelDiscoveryActivity.java new file mode 100644 index 000000000..5b0c30ed3 --- /dev/null +++ b/src/main/java/eu/siacs/conversations/ui/ChannelDiscoveryActivity.java @@ -0,0 +1,165 @@ +package eu.siacs.conversations.ui; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.SharedPreferences; +import android.databinding.DataBindingUtil; +import android.os.Bundle; +import android.support.v7.widget.Toolbar; +import android.text.Html; +import android.util.Log; +import android.view.KeyEvent; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.inputmethod.InputMethodManager; +import android.widget.EditText; +import android.widget.TextView; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; + +import eu.siacs.conversations.Config; +import eu.siacs.conversations.R; +import eu.siacs.conversations.databinding.ActivityChannelDiscoveryBinding; +import eu.siacs.conversations.entities.Account; +import eu.siacs.conversations.entities.ChannelSearchResult; +import eu.siacs.conversations.entities.Conversation; +import eu.siacs.conversations.services.XmppConnectionService; +import eu.siacs.conversations.ui.adapter.ChannelSearchResultAdapter; +import eu.siacs.conversations.ui.util.SoftKeyboardUtils; +import eu.siacs.conversations.utils.AccountUtils; +import rocks.xmpp.addr.Jid; + +public class ChannelDiscoveryActivity extends XmppActivity implements MenuItem.OnActionExpandListener, TextView.OnEditorActionListener, XmppConnectionService.OnChannelSearchResultsFound, ChannelSearchResultAdapter.OnChannelSearchResultSelected { + + private static final String CHANNEL_DISCOVERY_OPT_IN = "channel_discovery_opt_in"; + + private final ChannelSearchResultAdapter adapter = new ChannelSearchResultAdapter(); + + private EditText mSearchEditText; + + private boolean optedIn = false; + + @Override + protected void refreshUiReal() { + + } + + @Override + void onBackendConnected() { + if (optedIn) { + xmppConnectionService.discoverChannels(null, this); + } + } + + @Override + protected void onCreate(final Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + ActivityChannelDiscoveryBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_channel_discovery); + setSupportActionBar((Toolbar) binding.toolbar); + configureActionBar(getSupportActionBar(), true); + binding.list.setAdapter(this.adapter); + this.adapter.setOnChannelSearchResultSelectedListener(this); + optedIn = getPreferences().getBoolean(CHANNEL_DISCOVERY_OPT_IN, false); + } + + @Override + public boolean onCreateOptionsMenu(final Menu menu) { + getMenuInflater().inflate(R.menu.muc_users_activity, menu); + final MenuItem menuSearchView = menu.findItem(R.id.action_search); + final View mSearchView = menuSearchView.getActionView(); + mSearchEditText = mSearchView.findViewById(R.id.search_field); + mSearchEditText.setHint(R.string.search_channels); + mSearchEditText.setOnEditorActionListener(this); + menuSearchView.setOnActionExpandListener(this); + return true; + } + + @Override + public boolean onMenuItemActionExpand(MenuItem item) { + mSearchEditText.post(() -> { + mSearchEditText.requestFocus(); + final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + imm.showSoftInput(mSearchEditText, InputMethodManager.SHOW_IMPLICIT); + }); + return true; + } + + @Override + public boolean onMenuItemActionCollapse(MenuItem item) { + final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(mSearchEditText.getWindowToken(), InputMethodManager.HIDE_IMPLICIT_ONLY); + mSearchEditText.setText(""); + adapter.submitList(Collections.emptyList()); + if (optedIn) { + xmppConnectionService.discoverChannels(null, this); + } + return true; + } + + @Override + public void onStart() { + super.onStart(); + if (!optedIn) { + final AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(R.string.channel_discovery_opt_in_title); + builder.setMessage(Html.fromHtml(getString(R.string.channel_discover_opt_in_message))); + builder.setNegativeButton(R.string.cancel, (dialog, which) -> finish()); + builder.setPositiveButton(R.string.confirm, (dialog, which) -> optIn()); + builder.setOnCancelListener(dialog -> finish()); + final AlertDialog dialog = builder.create(); + dialog.setCanceledOnTouchOutside(false); + dialog.show(); + } + } + + private void optIn() { + SharedPreferences preferences = getPreferences(); + preferences.edit().putBoolean(CHANNEL_DISCOVERY_OPT_IN,true).apply(); + optedIn = true; + xmppConnectionService.discoverChannels(null, this); + } + + @Override + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + if (optedIn) { + xmppConnectionService.discoverChannels(v.getText().toString(), this); + } + adapter.submitList(Collections.emptyList()); + SoftKeyboardUtils.hideSoftKeyboard(this); + return true; + } + + @Override + public void onChannelSearchResultsFound(List results) { + runOnUiThread(() -> adapter.submitList(results)); + + } + + @Override + public void onChannelSearchResult(final ChannelSearchResult result) { + List accounts = AccountUtils.getEnabledAccounts(xmppConnectionService); + if (accounts.size() == 1) { + joinChannelSearchResult(accounts.get(0),result); + } else if (accounts.size() > 0){ + final AtomicReference account = new AtomicReference<>(accounts.get(0)); + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(R.string.choose_account); + builder.setSingleChoiceItems(accounts.toArray(new CharSequence[0]), 0, (dialog, which) -> account.set(accounts.get(which))); + builder.setPositiveButton(R.string.join, (dialog, which) -> joinChannelSearchResult(account.get(), result)); + builder.setNegativeButton(R.string.cancel, null); + builder.create().show(); + } + + } + + public void joinChannelSearchResult(String accountJid, ChannelSearchResult result) { + Account account = xmppConnectionService.findAccountByJid(Jid.of(accountJid)); + final Conversation conversation = xmppConnectionService.findOrCreateConversation(account, result.getRoom(), true, true, true); + switchToConversation(conversation); + } +} diff --git a/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java b/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java index 2a0066e1b..d776d0ff0 100644 --- a/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java @@ -313,6 +313,9 @@ public class StartConversationActivity extends XmppActivity implements XmppConne prefilled = null; } switch (actionItem.getId()) { + case R.id.discover_public_channels: + startActivity(new Intent(this, ChannelDiscoveryActivity.class)); + break; case R.id.join_public_channel: showJoinConferenceDialog(prefilled); break; @@ -781,15 +784,7 @@ public class StartConversationActivity extends XmppActivity implements XmppConne this.mPostponedActivityResult = null; } this.mActivatedAccounts.clear(); - for (Account account : xmppConnectionService.getAccounts()) { - if (account.getStatus() != Account.State.DISABLED) { - if (Config.DOMAIN_LOCK != null) { - this.mActivatedAccounts.add(account.getJid().getLocal()); - } else { - this.mActivatedAccounts.add(account.getJid().asBareJid().toString()); - } - } - } + this.mActivatedAccounts.addAll(AccountUtils.getEnabledAccounts(xmppConnectionService)); configureHomeButton(); Intent intent = pendingViewIntent.pop(); if (intent != null && processViewIntent(intent)) { diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/ChannelSearchResultAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/ChannelSearchResultAdapter.java new file mode 100644 index 000000000..ae1c70c1e --- /dev/null +++ b/src/main/java/eu/siacs/conversations/ui/adapter/ChannelSearchResultAdapter.java @@ -0,0 +1,78 @@ +package eu.siacs.conversations.ui.adapter; + +import android.databinding.DataBindingUtil; +import android.support.annotation.NonNull; +import android.support.v7.recyclerview.extensions.ListAdapter; +import android.support.v7.util.DiffUtil; +import android.support.v7.widget.RecyclerView; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import eu.siacs.conversations.R; +import eu.siacs.conversations.databinding.SearchResultItemBinding; +import eu.siacs.conversations.entities.ChannelSearchResult; +import eu.siacs.conversations.ui.util.AvatarWorkerTask; + +public class ChannelSearchResultAdapter extends ListAdapter { + + private OnChannelSearchResultSelected listener; + + private static final DiffUtil.ItemCallback DIFF = new DiffUtil.ItemCallback() { + @Override + public boolean areItemsTheSame(@NonNull ChannelSearchResult a, @NonNull ChannelSearchResult b) { + return false; + } + + @Override + public boolean areContentsTheSame(@NonNull ChannelSearchResult a, @NonNull ChannelSearchResult b) { + return a.equals(b); + } + }; + + public ChannelSearchResultAdapter() { + super(DIFF); + } + + @NonNull + @Override + public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) { + return new ViewHolder(DataBindingUtil.inflate(LayoutInflater.from(viewGroup.getContext()), R.layout.search_result_item,viewGroup,false)); + } + + @Override + public void onBindViewHolder(@NonNull ViewHolder viewHolder, int position) { + final ChannelSearchResult searchResult = getItem(position); + viewHolder.binding.name.setText(searchResult.getName()); + final String description = searchResult.getDescription(); + if (TextUtils.isEmpty(description)) { + viewHolder.binding.description.setVisibility(View.GONE); + } else { + viewHolder.binding.description.setText(description); + viewHolder.binding.description.setVisibility(View.VISIBLE); + } + viewHolder.binding.room.setText(searchResult.getRoom().asBareJid().toString()); + AvatarWorkerTask.loadAvatar(searchResult, viewHolder.binding.avatar, R.dimen.avatar); + viewHolder.binding.getRoot().setOnClickListener(v -> listener.onChannelSearchResult(searchResult)); + } + + public void setOnChannelSearchResultSelectedListener(OnChannelSearchResultSelected listener) { + this.listener = listener; + } + + + public static class ViewHolder extends RecyclerView.ViewHolder { + + private final SearchResultItemBinding binding; + + private ViewHolder(SearchResultItemBinding binding) { + super(binding.getRoot()); + this.binding = binding; + } + } + + public interface OnChannelSearchResultSelected { + void onChannelSearchResult(ChannelSearchResult result); + } +} diff --git a/src/main/java/eu/siacs/conversations/utils/AccountUtils.java b/src/main/java/eu/siacs/conversations/utils/AccountUtils.java index f546a69e4..df8d128d3 100644 --- a/src/main/java/eu/siacs/conversations/utils/AccountUtils.java +++ b/src/main/java/eu/siacs/conversations/utils/AccountUtils.java @@ -6,8 +6,10 @@ import android.view.Menu; import android.view.MenuItem; import android.widget.Toast; +import java.util.ArrayList; import java.util.List; +import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.services.XmppConnectionService; @@ -22,6 +24,20 @@ public class AccountUtils { } + public static List getEnabledAccounts(final XmppConnectionService service) { + ArrayList accounts = new ArrayList<>(); + for (Account account : service.getAccounts()) { + if (account.getStatus() != Account.State.DISABLED) { + if (Config.DOMAIN_LOCK != null) { + accounts.add(account.getJid().getLocal()); + } else { + accounts.add(account.getJid().asBareJid().toString()); + } + } + } + return accounts; + } + public static Account getFirstEnabled(XmppConnectionService service) { final List accounts = service.getAccounts(); for(Account account : accounts) { diff --git a/src/main/res/menu/channel_discovery_activity.xml b/src/main/res/menu/channel_discovery_activity.xml new file mode 100644 index 000000000..209bb27e0 --- /dev/null +++ b/src/main/res/menu/channel_discovery_activity.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/src/main/res/menu/start_conversation_fab_submenu.xml b/src/main/res/menu/start_conversation_fab_submenu.xml index 76a576d6a..bfaca0727 100644 --- a/src/main/res/menu/start_conversation_fab_submenu.xml +++ b/src/main/res/menu/start_conversation_fab_submenu.xml @@ -1,5 +1,9 @@ + Search participants File too large Attach + Discover channels + Search channels + Possible privacy violation! + search.jabber.network.

Using this feature will transmit your Jabber ID and search terms to that service. See their Privacy Policy for more information.]]>
From 2fa687b8ae16b93feb9c8f7a7d7e7b7d87182720 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Mon, 22 Apr 2019 13:00:45 +0200 Subject: [PATCH 06/33] refactor muc search to use http --- build.gradle | 2 + .../java/eu/siacs/conversations/Config.java | 2 +- .../entities/ChannelSearchResult.java | 35 ------ .../http/services/MuclumbusService.java | 85 ++++++++++++++ .../conversations/services/AvatarService.java | 18 +-- .../services/XmppConnectionService.java | 107 +++++++++++------- .../ui/ChannelDiscoveryActivity.java | 54 +++++++-- .../adapter/ChannelSearchResultAdapter.java | 14 +-- src/main/res/values/strings.xml | 2 +- 9 files changed, 211 insertions(+), 108 deletions(-) delete mode 100644 src/main/java/eu/siacs/conversations/entities/ChannelSearchResult.java create mode 100644 src/main/java/eu/siacs/conversations/http/services/MuclumbusService.java diff --git a/build.gradle b/build.gradle index dc8c7303f..9dc7576b3 100644 --- a/build.gradle +++ b/build.gradle @@ -64,6 +64,8 @@ dependencies { implementation 'org.conscrypt:conscrypt-android:1.3.0' implementation 'me.drakeet.support:toastcompat:1.1.0' implementation "com.leinardi.android:speed-dial:2.0.1" + implementation 'com.squareup.retrofit2:retrofit:2.5.0' + implementation 'com.squareup.retrofit2:converter-gson:2.5.0' quicksyImplementation 'io.michaelrocks:libphonenumber-android:8.10.1' } diff --git a/src/main/java/eu/siacs/conversations/Config.java b/src/main/java/eu/siacs/conversations/Config.java index 4626bffc8..d95e4d906 100644 --- a/src/main/java/eu/siacs/conversations/Config.java +++ b/src/main/java/eu/siacs/conversations/Config.java @@ -41,7 +41,7 @@ public final class Config { public static final String MAGIC_CREATE_DOMAIN = "conversations.im"; public static final String QUICKSY_DOMAIN = "quicksy.im"; - public static final Jid CHANNEL_DISCOVERY = Jid.of("rodrigo.de.mucobedo@dreckshal.de"); + public static final String CHANNEL_DISCOVERY = "https://search.jabbercat.org"; public static final boolean DISALLOW_REGISTRATION_IN_UI = false; //hide the register checkbox diff --git a/src/main/java/eu/siacs/conversations/entities/ChannelSearchResult.java b/src/main/java/eu/siacs/conversations/entities/ChannelSearchResult.java deleted file mode 100644 index 0222f2f85..000000000 --- a/src/main/java/eu/siacs/conversations/entities/ChannelSearchResult.java +++ /dev/null @@ -1,35 +0,0 @@ -package eu.siacs.conversations.entities; - -import eu.siacs.conversations.services.AvatarService; -import eu.siacs.conversations.utils.UIHelper; -import rocks.xmpp.addr.Jid; - -public class ChannelSearchResult implements AvatarService.Avatarable { - - private final String name; - private final String description; - private final Jid room; - - public String getName() { - return name; - } - - public String getDescription() { - return description; - } - - public Jid getRoom() { - return room; - } - - public ChannelSearchResult(String name, String description, Jid room) { - this.name = name; - this.description = description; - this.room = room; - } - - @Override - public int getAvatarBackgroundColor() { - return UIHelper.getColorForName(room != null ? room.asBareJid().toEscapedString() : getName()); - } -} diff --git a/src/main/java/eu/siacs/conversations/http/services/MuclumbusService.java b/src/main/java/eu/siacs/conversations/http/services/MuclumbusService.java new file mode 100644 index 000000000..2b132d8fc --- /dev/null +++ b/src/main/java/eu/siacs/conversations/http/services/MuclumbusService.java @@ -0,0 +1,85 @@ +package eu.siacs.conversations.http.services; + +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import eu.siacs.conversations.services.AvatarService; +import eu.siacs.conversations.utils.UIHelper; +import retrofit2.Call; +import retrofit2.http.Body; +import retrofit2.http.GET; +import retrofit2.http.POST; +import retrofit2.http.Query; +import rocks.xmpp.addr.Jid; + +public interface MuclumbusService { + + @GET("/api/1.0/rooms/unsafe") + Call getRooms(@Query("p") int page); + + @POST("/api/1.0/search") + Call search(@Body SearchRequest searchRequest); + + class Rooms { + int page; + int total; + int pages; + public List items; + } + + class Room implements AvatarService.Avatarable { + + public String address; + public int nusers; + public boolean is_open; + public String anonymity_mode; + public String name; + public String description; + public String language; + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public Jid getRoom() { + try { + return Jid.of(address); + } catch (IllegalArgumentException e) { + return null; + } + } + + @Override + public int getAvatarBackgroundColor() { + Jid room = getRoom(); + return UIHelper.getColorForName(room != null ? room.asBareJid().toEscapedString() : name); + } + } + + class SearchRequest { + + public Set keywords; + + public SearchRequest(String keyword) { + this.keywords = Collections.singleton(keyword); + } + } + + class SearchResult { + + public Result result; + + } + + class Result { + + public List items; + + } + +} diff --git a/src/main/java/eu/siacs/conversations/services/AvatarService.java b/src/main/java/eu/siacs/conversations/services/AvatarService.java index 055c50005..17e05929c 100644 --- a/src/main/java/eu/siacs/conversations/services/AvatarService.java +++ b/src/main/java/eu/siacs/conversations/services/AvatarService.java @@ -3,7 +3,6 @@ package eu.siacs.conversations.services; import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; -import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.PorterDuff; @@ -14,7 +13,6 @@ import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; import android.support.annotation.ColorInt; -import android.support.annotation.DrawableRes; import android.support.annotation.Nullable; import android.support.v4.content.res.ResourcesCompat; import android.text.TextUtils; @@ -33,17 +31,16 @@ import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Bookmark; -import eu.siacs.conversations.entities.ChannelSearchResult; import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Conversational; import eu.siacs.conversations.entities.ListItem; import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.MucOptions; +import eu.siacs.conversations.http.services.MuclumbusService; import eu.siacs.conversations.utils.UIHelper; import eu.siacs.conversations.xmpp.OnAdvancedStreamFeaturesLoaded; import eu.siacs.conversations.xmpp.XmppConnection; -import eu.siacs.conversations.xmpp.pep.Avatar; import rocks.xmpp.addr.Jid; public class AvatarService implements OnAdvancedStreamFeaturesLoaded { @@ -83,15 +80,20 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded { return get((ListItem) avatarable, size, cachedOnly); } else if (avatarable instanceof MucOptions.User) { return get((MucOptions.User) avatarable, size, cachedOnly); - } else if (avatarable instanceof ChannelSearchResult) { - return get((ChannelSearchResult) avatarable, size, cachedOnly); + } else if (avatarable instanceof MuclumbusService.Room) { + return get((MuclumbusService.Room) avatarable, size, cachedOnly); } throw new AssertionError("AvatarService does not know how to generate avatar from "+avatarable.getClass().getName()); } - private Bitmap get(final ChannelSearchResult result, final int size, boolean cacheOnly) { - return get(result.getName(), result.getRoom().asBareJid().toEscapedString(), size, cacheOnly); + private Bitmap get(final MuclumbusService.Room result, final int size, boolean cacheOnly) { + final Jid room = result.getRoom(); + Conversation conversation = room != null ? mXmppConnectionService.findFirstMuc(room) : null; + if (conversation != null) { + return get(conversation,size,cacheOnly); + } + return get(result.getName(), room != null ? room.asBareJid().toEscapedString() : result.getName(), size, cacheOnly); } private Bitmap get(final Contact contact, final int size, boolean cachedOnly) { diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index 3c2164902..45b5e9f97 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -49,7 +49,6 @@ import org.openintents.openpgp.util.OpenPgpServiceConnection; import java.io.File; import java.net.URL; -import java.nio.channels.Channel; import java.security.SecureRandom; import java.security.Security; import java.security.cert.CertificateException; @@ -69,6 +68,9 @@ import java.util.Set; import java.util.WeakHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; @@ -85,7 +87,6 @@ import eu.siacs.conversations.crypto.axolotl.XmppAxolotlMessage; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Blockable; import eu.siacs.conversations.entities.Bookmark; -import eu.siacs.conversations.entities.ChannelSearchResult; import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Conversational; @@ -102,6 +103,7 @@ import eu.siacs.conversations.generator.MessageGenerator; import eu.siacs.conversations.generator.PresenceGenerator; import eu.siacs.conversations.http.HttpConnectionManager; import eu.siacs.conversations.http.CustomURLStreamHandlerFactory; +import eu.siacs.conversations.http.services.MuclumbusService; import eu.siacs.conversations.parser.AbstractParser; import eu.siacs.conversations.parser.IqParser; import eu.siacs.conversations.parser.MessageParser; @@ -114,7 +116,6 @@ import eu.siacs.conversations.ui.UiCallback; import eu.siacs.conversations.ui.interfaces.OnAvatarPublication; import eu.siacs.conversations.ui.interfaces.OnMediaLoaded; import eu.siacs.conversations.ui.interfaces.OnSearchResultsAvailable; -import eu.siacs.conversations.utils.AccountUtils; import eu.siacs.conversations.utils.Compatibility; import eu.siacs.conversations.utils.ConversationsFileObserver; import eu.siacs.conversations.utils.CryptoHelper; @@ -154,6 +155,11 @@ import eu.siacs.conversations.xmpp.stanzas.IqPacket; import eu.siacs.conversations.xmpp.stanzas.MessagePacket; import eu.siacs.conversations.xmpp.stanzas.PresencePacket; import me.leolin.shortcutbadger.ShortcutBadger; +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; +import retrofit2.Retrofit; +import retrofit2.converter.gson.GsonConverterFactory; import rocks.xmpp.addr.Jid; public class XmppConnectionService extends Service { @@ -236,6 +242,7 @@ public class XmppConnectionService extends Service { private AvatarService mAvatarService = new AvatarService(this); private MessageArchiveService mMessageArchiveService = new MessageArchiveService(this); private PushManagementService mPushManagementService = new PushManagementService(this); + private MuclumbusService muclumbusService; private QuickConversationsService mQuickConversationsService = new QuickConversationsService(this); private final ConversationsFileObserver fileObserver = new ConversationsFileObserver( Environment.getExternalStorageDirectory().getAbsolutePath() @@ -798,56 +805,61 @@ public class XmppConnectionService extends Service { return pingNow; } - public void discoverChannels(String query, OnChannelSearchResultsFound listener) { - IqPacket packet = new IqPacket(IqPacket.TYPE.GET); - packet.setTo(Config.CHANNEL_DISCOVERY); - Element search = packet.addChild("search","https://xmlns.zombofant.net/muclumbus/search/1.0"); - search.addChild("set","http://jabber.org/protocol/rsm").addChild("max").setContent("100"); - Bundle bundle = new Bundle(); - if (!TextUtils.isEmpty(query)) { - bundle.putString("q",query); + public void discoverChannels(String query, OnChannelSearchResultsFound onChannelSearchResultsFound) { + Log.d(Config.LOGTAG,"discover channels. query="+query); + if (query == null || query.trim().isEmpty()) { + discoverChannelsInternal(onChannelSearchResultsFound); + } else { + discoverChannelsInternal(query, onChannelSearchResultsFound); } - Data data = Data.create("https://xmlns.zombofant.net/muclumbus/search/1.0#params", bundle); - search.addChild(data); - final Account account = AccountUtils.getFirstEnabled(this); - if (account == null) { - return; - } - sendIqPacket(account, packet, new OnIqPacketReceived() { - @Override - public void onIqPacketReceived(Account account, IqPacket response) { - ArrayList searchResults = new ArrayList<>(); - if (response.getType() == IqPacket.TYPE.RESULT) { - Element result = response.findChild("result","https://xmlns.zombofant.net/muclumbus/search/1.0"); - if (result != null) { - for(Element child : result.getChildren()) { - if ("item".equals(child.getName())) { - String name = child.findChildContent("name"); - String description = child.findChildContent("description"); - Jid room = child.getAttributeAsJid("address"); - if (room != null) { - searchResults.add(new ChannelSearchResult(name,description,room)); - } else { - Log.d(Config.LOGTAG,"skipping because room was null"); - } - } - } - } else { - Log.d(Config.LOGTAG,"result was null"); + } + + private void discoverChannelsInternal(OnChannelSearchResultsFound listener) { + Call call = muclumbusService.getRooms(1); + try { + call.enqueue(new Callback() { + @Override + public void onResponse(Call call, Response response) { + final MuclumbusService.Rooms body = response.body(); + if (body == null) { + return; } - } else { - Log.d(Config.LOGTAG,response.toString()); - } - if (listener != null) { - listener.onChannelSearchResultsFound(searchResults); + listener.onChannelSearchResultsFound(body.items); } + @Override + public void onFailure(Call call, Throwable throwable) { + + } + }); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private void discoverChannelsInternal(String query, OnChannelSearchResultsFound listener) { + Call searchResultCall = muclumbusService.search(new MuclumbusService.SearchRequest(query)); + + searchResultCall.enqueue(new Callback() { + @Override + public void onResponse(Call call, Response response) { + System.out.println(response.message()); + MuclumbusService.SearchResult body = response.body(); + if (body == null) { + return; + } + listener.onChannelSearchResultsFound(body.result.items); + } + + @Override + public void onFailure(Call call, Throwable throwable) { + throwable.printStackTrace(); } }); } public interface OnChannelSearchResultsFound { - void onChannelSearchResultsFound(List results); + void onChannelSearchResultsFound(List results); } public boolean isDataSaverDisabled() { @@ -1118,6 +1130,13 @@ public class XmppConnectionService extends Service { } mForceDuringOnCreate.set(false); toggleForegroundService(); + + Retrofit retrofit = new Retrofit.Builder() + .baseUrl(Config.CHANNEL_DISCOVERY) + .addConverterFactory(GsonConverterFactory.create()) + .callbackExecutor(Executors.newSingleThreadExecutor()) + .build(); + muclumbusService = retrofit.create(MuclumbusService.class); } private void checkForDeletedFiles() { diff --git a/src/main/java/eu/siacs/conversations/ui/ChannelDiscoveryActivity.java b/src/main/java/eu/siacs/conversations/ui/ChannelDiscoveryActivity.java index 5b0c30ed3..03211c9ec 100644 --- a/src/main/java/eu/siacs/conversations/ui/ChannelDiscoveryActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ChannelDiscoveryActivity.java @@ -2,13 +2,12 @@ package eu.siacs.conversations.ui; import android.app.AlertDialog; import android.content.Context; -import android.content.DialogInterface; +import android.content.Intent; import android.content.SharedPreferences; import android.databinding.DataBindingUtil; import android.os.Bundle; import android.support.v7.widget.Toolbar; import android.text.Html; -import android.util.Log; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; @@ -17,19 +16,18 @@ import android.view.inputmethod.InputMethodManager; import android.widget.EditText; import android.widget.TextView; -import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.concurrent.atomic.AtomicReference; -import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.databinding.ActivityChannelDiscoveryBinding; import eu.siacs.conversations.entities.Account; -import eu.siacs.conversations.entities.ChannelSearchResult; import eu.siacs.conversations.entities.Conversation; +import eu.siacs.conversations.http.services.MuclumbusService; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.ui.adapter.ChannelSearchResultAdapter; +import eu.siacs.conversations.ui.util.PendingItem; import eu.siacs.conversations.ui.util.SoftKeyboardUtils; import eu.siacs.conversations.utils.AccountUtils; import rocks.xmpp.addr.Jid; @@ -40,6 +38,9 @@ public class ChannelDiscoveryActivity extends XmppActivity implements MenuItem.O private final ChannelSearchResultAdapter adapter = new ChannelSearchResultAdapter(); + private final PendingItem mInitialSearchValue = new PendingItem<>(); + + private MenuItem mMenuSearchView; private EditText mSearchEditText; private boolean optedIn = false; @@ -52,7 +53,13 @@ public class ChannelDiscoveryActivity extends XmppActivity implements MenuItem.O @Override void onBackendConnected() { if (optedIn) { - xmppConnectionService.discoverChannels(null, this); + String query; + if (mMenuSearchView != null && mMenuSearchView.isActionViewExpanded()) { + query = mSearchEditText.getText().toString(); + } else { + query = mInitialSearchValue.peek(); + } + xmppConnectionService.discoverChannels(query, this); } } @@ -65,17 +72,32 @@ public class ChannelDiscoveryActivity extends XmppActivity implements MenuItem.O binding.list.setAdapter(this.adapter); this.adapter.setOnChannelSearchResultSelectedListener(this); optedIn = getPreferences().getBoolean(CHANNEL_DISCOVERY_OPT_IN, false); + + final String search = savedInstanceState == null ? null : savedInstanceState.getString("search"); + if (search != null) { + mInitialSearchValue.push(search); + } + } @Override public boolean onCreateOptionsMenu(final Menu menu) { getMenuInflater().inflate(R.menu.muc_users_activity, menu); - final MenuItem menuSearchView = menu.findItem(R.id.action_search); - final View mSearchView = menuSearchView.getActionView(); + mMenuSearchView = menu.findItem(R.id.action_search); + final View mSearchView = mMenuSearchView.getActionView(); mSearchEditText = mSearchView.findViewById(R.id.search_field); mSearchEditText.setHint(R.string.search_channels); + String initialSearchValue = mInitialSearchValue.pop(); + if (initialSearchValue != null) { + mMenuSearchView.expandActionView(); + mSearchEditText.append(initialSearchValue); + mSearchEditText.requestFocus(); + if (optedIn) { + xmppConnectionService.discoverChannels(initialSearchValue, this); + } + } mSearchEditText.setOnEditorActionListener(this); - menuSearchView.setOnActionExpandListener(this); + mMenuSearchView.setOnActionExpandListener(this); return true; } @@ -117,6 +139,14 @@ public class ChannelDiscoveryActivity extends XmppActivity implements MenuItem.O } } + @Override + public void onSaveInstanceState(Bundle savedInstanceState) { + if (mMenuSearchView != null && mMenuSearchView.isActionViewExpanded()) { + savedInstanceState.putString("search", mSearchEditText != null ? mSearchEditText.getText().toString() : null); + } + super.onSaveInstanceState(savedInstanceState); + } + private void optIn() { SharedPreferences preferences = getPreferences(); preferences.edit().putBoolean(CHANNEL_DISCOVERY_OPT_IN,true).apply(); @@ -135,13 +165,13 @@ public class ChannelDiscoveryActivity extends XmppActivity implements MenuItem.O } @Override - public void onChannelSearchResultsFound(List results) { + public void onChannelSearchResultsFound(List results) { runOnUiThread(() -> adapter.submitList(results)); } @Override - public void onChannelSearchResult(final ChannelSearchResult result) { + public void onChannelSearchResult(final MuclumbusService.Room result) { List accounts = AccountUtils.getEnabledAccounts(xmppConnectionService); if (accounts.size() == 1) { joinChannelSearchResult(accounts.get(0),result); @@ -157,7 +187,7 @@ public class ChannelDiscoveryActivity extends XmppActivity implements MenuItem.O } - public void joinChannelSearchResult(String accountJid, ChannelSearchResult result) { + public void joinChannelSearchResult(String accountJid, MuclumbusService.Room result) { Account account = xmppConnectionService.findAccountByJid(Jid.of(accountJid)); final Conversation conversation = xmppConnectionService.findOrCreateConversation(account, result.getRoom(), true, true, true); switchToConversation(conversation); diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/ChannelSearchResultAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/ChannelSearchResultAdapter.java index ae1c70c1e..42c6647ed 100644 --- a/src/main/java/eu/siacs/conversations/ui/adapter/ChannelSearchResultAdapter.java +++ b/src/main/java/eu/siacs/conversations/ui/adapter/ChannelSearchResultAdapter.java @@ -12,21 +12,21 @@ import android.view.ViewGroup; import eu.siacs.conversations.R; import eu.siacs.conversations.databinding.SearchResultItemBinding; -import eu.siacs.conversations.entities.ChannelSearchResult; +import eu.siacs.conversations.http.services.MuclumbusService; import eu.siacs.conversations.ui.util.AvatarWorkerTask; -public class ChannelSearchResultAdapter extends ListAdapter { +public class ChannelSearchResultAdapter extends ListAdapter { private OnChannelSearchResultSelected listener; - private static final DiffUtil.ItemCallback DIFF = new DiffUtil.ItemCallback() { + private static final DiffUtil.ItemCallback DIFF = new DiffUtil.ItemCallback() { @Override - public boolean areItemsTheSame(@NonNull ChannelSearchResult a, @NonNull ChannelSearchResult b) { + public boolean areItemsTheSame(@NonNull MuclumbusService.Room a, @NonNull MuclumbusService.Room b) { return false; } @Override - public boolean areContentsTheSame(@NonNull ChannelSearchResult a, @NonNull ChannelSearchResult b) { + public boolean areContentsTheSame(@NonNull MuclumbusService.Room a, @NonNull MuclumbusService.Room b) { return a.equals(b); } }; @@ -43,7 +43,7 @@ public class ChannelSearchResultAdapter extends ListAdapterDiscover channels Search channels Possible privacy violation! - search.jabber.network.

Using this feature will transmit your Jabber ID and search terms to that service. See their Privacy Policy for more information.]]>
+ search.jabbercat.org.

Using this feature will transmit your IP address and search terms to that service. See their Privacy Policy for more information.]]>
From 1857fe9e0d0e2637bceb75eeece2ddf5b2f017c8 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Mon, 22 Apr 2019 13:16:49 +0200 Subject: [PATCH 07/33] moved layout files from conversations flavor to main --- .../res/layout/activity_channel_discovery.xml | 0 src/{conversations => main}/res/layout/search_result_item.xml | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename src/{conversations => main}/res/layout/activity_channel_discovery.xml (100%) rename src/{conversations => main}/res/layout/search_result_item.xml (100%) diff --git a/src/conversations/res/layout/activity_channel_discovery.xml b/src/main/res/layout/activity_channel_discovery.xml similarity index 100% rename from src/conversations/res/layout/activity_channel_discovery.xml rename to src/main/res/layout/activity_channel_discovery.xml diff --git a/src/conversations/res/layout/search_result_item.xml b/src/main/res/layout/search_result_item.xml similarity index 100% rename from src/conversations/res/layout/search_result_item.xml rename to src/main/res/layout/search_result_item.xml From ba21c9b55f9c18bbea7351815547f076cc34c6f6 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Wed, 24 Apr 2019 13:25:30 +0200 Subject: [PATCH 08/33] update gradle --- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 9dc7576b3..240736afc 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.3.0' + classpath 'com.android.tools.build:gradle:3.4.0' } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 0682df5e2..f0ff44317 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ +#Wed Apr 24 10:50:09 CEST 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.2-all.zip From ff84ee6964f16f98dc2691dd77bb4906451dbb11 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Wed, 24 Apr 2019 13:25:54 +0200 Subject: [PATCH 09/33] cache channel search results --- build.gradle | 1 + proguard-rules.pro | 2 + .../http/services/MuclumbusService.java | 21 +++- .../services/ChannelDiscoveryService.java | 106 ++++++++++++++++++ .../services/XmppConnectionService.java | 66 +---------- .../ui/ChannelDiscoveryActivity.java | 24 ++-- .../adapter/ChannelSearchResultAdapter.java | 2 +- .../ui/adapter/MediaPreviewAdapter.java | 4 - .../res/layout/activity_channel_discovery.xml | 10 +- 9 files changed, 156 insertions(+), 80 deletions(-) create mode 100644 src/main/java/eu/siacs/conversations/services/ChannelDiscoveryService.java diff --git a/build.gradle b/build.gradle index 240736afc..c3242f851 100644 --- a/build.gradle +++ b/build.gradle @@ -66,6 +66,7 @@ dependencies { implementation "com.leinardi.android:speed-dial:2.0.1" implementation 'com.squareup.retrofit2:retrofit:2.5.0' implementation 'com.squareup.retrofit2:converter-gson:2.5.0' + implementation 'com.google.guava:guava:27.1-android' quicksyImplementation 'io.michaelrocks:libphonenumber-android:8.10.1' } diff --git a/proguard-rules.pro b/proguard-rules.pro index afd589c54..3c3593577 100644 --- a/proguard-rules.pro +++ b/proguard-rules.pro @@ -18,3 +18,5 @@ -dontwarn org.bouncycastle.cert.dane.** -dontwarn rocks.xmpp.addr.** -dontwarn com.google.firebase.analytics.connector.AnalyticsConnector +-dontwarn java.lang.** +-dontwarn javax.lang.** diff --git a/src/main/java/eu/siacs/conversations/http/services/MuclumbusService.java b/src/main/java/eu/siacs/conversations/http/services/MuclumbusService.java index 2b132d8fc..5ef557c0f 100644 --- a/src/main/java/eu/siacs/conversations/http/services/MuclumbusService.java +++ b/src/main/java/eu/siacs/conversations/http/services/MuclumbusService.java @@ -1,5 +1,7 @@ package eu.siacs.conversations.http.services; +import com.google.common.base.Objects; + import java.util.Collections; import java.util.List; import java.util.Set; @@ -31,12 +33,8 @@ public interface MuclumbusService { class Room implements AvatarService.Avatarable { public String address; - public int nusers; - public boolean is_open; - public String anonymity_mode; public String name; public String description; - public String language; public String getName() { return name; @@ -59,6 +57,21 @@ public interface MuclumbusService { Jid room = getRoom(); return UIHelper.getColorForName(room != null ? room.asBareJid().toEscapedString() : name); } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Room room = (Room) o; + return Objects.equal(address, room.address) && + Objects.equal(name, room.name) && + Objects.equal(description, room.description); + } + + @Override + public int hashCode() { + return Objects.hashCode(address, name, description); + } } class SearchRequest { diff --git a/src/main/java/eu/siacs/conversations/services/ChannelDiscoveryService.java b/src/main/java/eu/siacs/conversations/services/ChannelDiscoveryService.java new file mode 100644 index 000000000..1c7c6883e --- /dev/null +++ b/src/main/java/eu/siacs/conversations/services/ChannelDiscoveryService.java @@ -0,0 +1,106 @@ +package eu.siacs.conversations.services; + +import android.util.Log; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; + +import java.util.List; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import eu.siacs.conversations.Config; +import eu.siacs.conversations.http.services.MuclumbusService; +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; +import retrofit2.Retrofit; +import retrofit2.converter.gson.GsonConverterFactory; + +public class ChannelDiscoveryService { + + private final XmppConnectionService service; + + + private final MuclumbusService muclumbusService; + + private final Cache> cache; + + public ChannelDiscoveryService(XmppConnectionService service) { + this.service = service; + Retrofit retrofit = new Retrofit.Builder() + .baseUrl(Config.CHANNEL_DISCOVERY) + .addConverterFactory(GsonConverterFactory.create()) + .callbackExecutor(Executors.newSingleThreadExecutor()) + .build(); + this.muclumbusService = retrofit.create(MuclumbusService.class); + this.cache = CacheBuilder.newBuilder().expireAfterWrite(5, TimeUnit.MINUTES).build(); + } + + public void discover(String query, OnChannelSearchResultsFound onChannelSearchResultsFound) { + final boolean all = query == null || query.trim().isEmpty(); + Log.d(Config.LOGTAG, "discover channels. query=" + query); + List result = cache.getIfPresent(all ? "" : query); + if (result != null) { + onChannelSearchResultsFound.onChannelSearchResultsFound(result); + return; + } + if (all) { + discoverChannels(onChannelSearchResultsFound); + } else { + discoverChannels(query, onChannelSearchResultsFound); + } + } + + private void discoverChannels(OnChannelSearchResultsFound listener) { + Call call = muclumbusService.getRooms(1); + try { + call.enqueue(new Callback() { + @Override + public void onResponse(Call call, Response response) { + final MuclumbusService.Rooms body = response.body(); + if (body == null) { + return; + } + cache.put("", body.items); + listener.onChannelSearchResultsFound(body.items); + } + + @Override + public void onFailure(Call call, Throwable throwable) { + + } + }); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private void discoverChannels(final String query, OnChannelSearchResultsFound listener) { + Call searchResultCall = muclumbusService.search(new MuclumbusService.SearchRequest(query)); + + searchResultCall.enqueue(new Callback() { + @Override + public void onResponse(Call call, Response response) { + System.out.println(response.message()); + MuclumbusService.SearchResult body = response.body(); + if (body == null) { + return; + } + cache.put(query, body.result.items); + listener.onChannelSearchResultsFound(body.result.items); + } + + @Override + public void onFailure(Call call, Throwable throwable) { + throwable.printStackTrace(); + } + }); + } + + public interface OnChannelSearchResultsFound { + void onChannelSearchResultsFound(List results); + } +} diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index 45b5e9f97..575cac60e 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -209,6 +209,7 @@ public class XmppConnectionService extends Service { private FileBackend fileBackend = new FileBackend(this); private MemorizingTrustManager mMemorizingTrustManager; private NotificationService mNotificationService = new NotificationService(this); + private ChannelDiscoveryService mChannelDiscoveryService = new ChannelDiscoveryService(this); private ShortcutService mShortcutService = new ShortcutService(this); private AtomicBoolean mInitialAddressbookSyncCompleted = new AtomicBoolean(false); private AtomicBoolean mForceForegroundService = new AtomicBoolean(false); @@ -242,7 +243,6 @@ public class XmppConnectionService extends Service { private AvatarService mAvatarService = new AvatarService(this); private MessageArchiveService mMessageArchiveService = new MessageArchiveService(this); private PushManagementService mPushManagementService = new PushManagementService(this); - private MuclumbusService muclumbusService; private QuickConversationsService mQuickConversationsService = new QuickConversationsService(this); private final ConversationsFileObserver fileObserver = new ConversationsFileObserver( Environment.getExternalStorageDirectory().getAbsolutePath() @@ -805,61 +805,8 @@ public class XmppConnectionService extends Service { return pingNow; } - public void discoverChannels(String query, OnChannelSearchResultsFound onChannelSearchResultsFound) { - Log.d(Config.LOGTAG,"discover channels. query="+query); - if (query == null || query.trim().isEmpty()) { - discoverChannelsInternal(onChannelSearchResultsFound); - } else { - discoverChannelsInternal(query, onChannelSearchResultsFound); - } - } - - private void discoverChannelsInternal(OnChannelSearchResultsFound listener) { - Call call = muclumbusService.getRooms(1); - try { - call.enqueue(new Callback() { - @Override - public void onResponse(Call call, Response response) { - final MuclumbusService.Rooms body = response.body(); - if (body == null) { - return; - } - listener.onChannelSearchResultsFound(body.items); - } - - @Override - public void onFailure(Call call, Throwable throwable) { - - } - }); - } catch (Exception e) { - e.printStackTrace(); - } - } - - private void discoverChannelsInternal(String query, OnChannelSearchResultsFound listener) { - Call searchResultCall = muclumbusService.search(new MuclumbusService.SearchRequest(query)); - - searchResultCall.enqueue(new Callback() { - @Override - public void onResponse(Call call, Response response) { - System.out.println(response.message()); - MuclumbusService.SearchResult body = response.body(); - if (body == null) { - return; - } - listener.onChannelSearchResultsFound(body.result.items); - } - - @Override - public void onFailure(Call call, Throwable throwable) { - throwable.printStackTrace(); - } - }); - } - - public interface OnChannelSearchResultsFound { - void onChannelSearchResultsFound(List results); + public void discoverChannels(String query, ChannelDiscoveryService.OnChannelSearchResultsFound onChannelSearchResultsFound) { + mChannelDiscoveryService.discover(query, onChannelSearchResultsFound); } public boolean isDataSaverDisabled() { @@ -1130,13 +1077,6 @@ public class XmppConnectionService extends Service { } mForceDuringOnCreate.set(false); toggleForegroundService(); - - Retrofit retrofit = new Retrofit.Builder() - .baseUrl(Config.CHANNEL_DISCOVERY) - .addConverterFactory(GsonConverterFactory.create()) - .callbackExecutor(Executors.newSingleThreadExecutor()) - .build(); - muclumbusService = retrofit.create(MuclumbusService.class); } private void checkForDeletedFiles() { diff --git a/src/main/java/eu/siacs/conversations/ui/ChannelDiscoveryActivity.java b/src/main/java/eu/siacs/conversations/ui/ChannelDiscoveryActivity.java index 03211c9ec..ab29c6f86 100644 --- a/src/main/java/eu/siacs/conversations/ui/ChannelDiscoveryActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ChannelDiscoveryActivity.java @@ -2,7 +2,6 @@ package eu.siacs.conversations.ui; import android.app.AlertDialog; import android.content.Context; -import android.content.Intent; import android.content.SharedPreferences; import android.databinding.DataBindingUtil; import android.os.Bundle; @@ -25,19 +24,21 @@ import eu.siacs.conversations.databinding.ActivityChannelDiscoveryBinding; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.http.services.MuclumbusService; -import eu.siacs.conversations.services.XmppConnectionService; +import eu.siacs.conversations.services.ChannelDiscoveryService; import eu.siacs.conversations.ui.adapter.ChannelSearchResultAdapter; import eu.siacs.conversations.ui.util.PendingItem; import eu.siacs.conversations.ui.util.SoftKeyboardUtils; import eu.siacs.conversations.utils.AccountUtils; import rocks.xmpp.addr.Jid; -public class ChannelDiscoveryActivity extends XmppActivity implements MenuItem.OnActionExpandListener, TextView.OnEditorActionListener, XmppConnectionService.OnChannelSearchResultsFound, ChannelSearchResultAdapter.OnChannelSearchResultSelected { +public class ChannelDiscoveryActivity extends XmppActivity implements MenuItem.OnActionExpandListener, TextView.OnEditorActionListener, ChannelDiscoveryService.OnChannelSearchResultsFound, ChannelSearchResultAdapter.OnChannelSearchResultSelected { private static final String CHANNEL_DISCOVERY_OPT_IN = "channel_discovery_opt_in"; private final ChannelSearchResultAdapter adapter = new ChannelSearchResultAdapter(); + private ActivityChannelDiscoveryBinding binding; + private final PendingItem mInitialSearchValue = new PendingItem<>(); private MenuItem mMenuSearchView; @@ -66,7 +67,7 @@ public class ChannelDiscoveryActivity extends XmppActivity implements MenuItem.O @Override protected void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); - ActivityChannelDiscoveryBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_channel_discovery); + binding = DataBindingUtil.setContentView(this, R.layout.activity_channel_discovery); setSupportActionBar((Toolbar) binding.toolbar); configureActionBar(getSupportActionBar(), true); binding.list.setAdapter(this.adapter); @@ -116,13 +117,18 @@ public class ChannelDiscoveryActivity extends XmppActivity implements MenuItem.O final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(mSearchEditText.getWindowToken(), InputMethodManager.HIDE_IMPLICIT_ONLY); mSearchEditText.setText(""); - adapter.submitList(Collections.emptyList()); + toggleLoadingScreen(); if (optedIn) { xmppConnectionService.discoverChannels(null, this); } return true; } + private void toggleLoadingScreen() { + adapter.submitList(Collections.emptyList()); + binding.progressBar.setVisibility(View.VISIBLE); + } + @Override public void onStart() { super.onStart(); @@ -159,14 +165,18 @@ public class ChannelDiscoveryActivity extends XmppActivity implements MenuItem.O if (optedIn) { xmppConnectionService.discoverChannels(v.getText().toString(), this); } - adapter.submitList(Collections.emptyList()); + toggleLoadingScreen(); SoftKeyboardUtils.hideSoftKeyboard(this); return true; } @Override public void onChannelSearchResultsFound(List results) { - runOnUiThread(() -> adapter.submitList(results)); + runOnUiThread(() -> { + adapter.submitList(results); + binding.list.setVisibility(View.VISIBLE); + binding.progressBar.setVisibility(View.GONE); + }); } diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/ChannelSearchResultAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/ChannelSearchResultAdapter.java index 42c6647ed..7d1a7c873 100644 --- a/src/main/java/eu/siacs/conversations/ui/adapter/ChannelSearchResultAdapter.java +++ b/src/main/java/eu/siacs/conversations/ui/adapter/ChannelSearchResultAdapter.java @@ -22,7 +22,7 @@ public class ChannelSearchResultAdapter extends ListAdapter DIFF = new DiffUtil.ItemCallback() { @Override public boolean areItemsTheSame(@NonNull MuclumbusService.Room a, @NonNull MuclumbusService.Room b) { - return false; + return a.address != null && a.address.equals(b.address); } @Override diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/MediaPreviewAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/MediaPreviewAdapter.java index d888d3198..405ddcb41 100644 --- a/src/main/java/eu/siacs/conversations/ui/adapter/MediaPreviewAdapter.java +++ b/src/main/java/eu/siacs/conversations/ui/adapter/MediaPreviewAdapter.java @@ -1,6 +1,5 @@ package eu.siacs.conversations.ui.adapter; -import android.app.Activity; import android.content.Context; import android.content.res.Resources; import android.databinding.DataBindingUtil; @@ -8,7 +7,6 @@ import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.AsyncTask; -import android.support.annotation.AttrRes; import android.support.annotation.NonNull; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; @@ -17,7 +15,6 @@ import android.widget.ImageView; import java.lang.ref.WeakReference; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.concurrent.RejectedExecutionException; @@ -26,7 +23,6 @@ import eu.siacs.conversations.databinding.MediaPreviewBinding; import eu.siacs.conversations.ui.ConversationFragment; import eu.siacs.conversations.ui.XmppActivity; import eu.siacs.conversations.ui.util.Attachment; -import eu.siacs.conversations.ui.util.StyledAttributes; public class MediaPreviewAdapter extends RecyclerView.Adapter { diff --git a/src/main/res/layout/activity_channel_discovery.xml b/src/main/res/layout/activity_channel_discovery.xml index 679c5156e..6ed46433e 100644 --- a/src/main/res/layout/activity_channel_discovery.xml +++ b/src/main/res/layout/activity_channel_discovery.xml @@ -14,18 +14,26 @@ layout="@layout/toolbar" /> + + - From a04b7a1c0f9ac7d36e1723a1de03dd0295709c54 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Thu, 25 Apr 2019 12:27:38 +0200 Subject: [PATCH 10/33] keyboard event callbacks shoud run on ui thread. fixes #3441 --- .../eu/siacs/conversations/ui/ConversationFragment.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java index 314b8fe73..2bff31edb 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java @@ -2592,7 +2592,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke if (status == Account.State.ONLINE && conversation.setOutgoingChatState(ChatState.COMPOSING)) { service.sendChatState(conversation); } - updateSendButton(); + runOnUiThread(this::updateSendButton); } @Override @@ -2618,15 +2618,15 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke service.sendChatState(conversation); } if (storeNextMessage()) { - activity.onConversationsListItemUpdated(); + runOnUiThread(() -> activity.onConversationsListItemUpdated()); } - updateSendButton(); + runOnUiThread(this::updateSendButton); } @Override public void onTextChanged() { if (conversation != null && conversation.getCorrectingMessage() != null) { - updateSendButton(); + runOnUiThread(this::updateSendButton); } } From 7b61bb984173ec314f057c8fbee7f69b6e4f8ee6 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Thu, 25 Apr 2019 19:00:59 +0200 Subject: [PATCH 11/33] rework welcome screens to split use existing account / create new closes #3440 --- src/conversations/AndroidManifest.xml | 6 +- .../conversations/ui/PickServerActivity.java | 104 ++++++++++++++++++ .../conversations/ui/WelcomeActivity.java | 25 ++--- .../conversations/utils/SignupUtils.java | 15 ++- .../res/layout/activity_pick_server.xml | 102 +++++++++++++++++ .../res/layout/activity_welcome.xml | 91 +++++++++++++++ src/conversations/res/layout/welcome.xml | 89 --------------- src/conversations/res/values/strings.xml | 9 ++ .../conversations/ui/EditAccountActivity.java | 29 ++++- src/main/res/values/strings.xml | 4 +- .../conversations/utils/SignupUtils.java | 7 +- 11 files changed, 368 insertions(+), 113 deletions(-) create mode 100644 src/conversations/java/eu/siacs/conversations/ui/PickServerActivity.java create mode 100644 src/conversations/res/layout/activity_pick_server.xml create mode 100644 src/conversations/res/layout/activity_welcome.xml delete mode 100644 src/conversations/res/layout/welcome.xml create mode 100644 src/conversations/res/values/strings.xml diff --git a/src/conversations/AndroidManifest.xml b/src/conversations/AndroidManifest.xml index 2c01f6d6c..0a01c6c0f 100644 --- a/src/conversations/AndroidManifest.xml +++ b/src/conversations/AndroidManifest.xml @@ -12,9 +12,13 @@ android:name=".ui.WelcomeActivity" android:label="@string/app_name" android:launchMode="singleTask"/> + { + final Intent intent = new Intent(this, MagicCreateActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); + addInviteUri(intent); + startActivity(intent); + }); + binding.useOwnProvider.setOnClickListener(v -> { + List accounts = xmppConnectionService.getAccounts(); + Intent intent = new Intent(this, EditAccountActivity.class); + intent.putExtra(EditAccountActivity.EXTRA_FORCE_REGISTER, true); + if (accounts.size() == 1) { + intent.putExtra("jid", accounts.get(0).getJid().asBareJid().toString()); + intent.putExtra("init", true); + } else if (accounts.size() >= 1) { + intent = new Intent(this, ManageAccountActivity.class); + } + addInviteUri(intent); + startActivity(intent); + }); + + } + + public void addInviteUri(Intent intent) { + StartConversationActivity.addInviteUri(intent, getIntent()); + } + + public static void launch(AppCompatActivity activity) { + Intent intent = new Intent(activity, PickServerActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + activity.startActivity(intent); + activity.overridePendingTransition(0, 0); + } + +} diff --git a/src/conversations/java/eu/siacs/conversations/ui/WelcomeActivity.java b/src/conversations/java/eu/siacs/conversations/ui/WelcomeActivity.java index eb466e37a..02613f368 100644 --- a/src/conversations/java/eu/siacs/conversations/ui/WelcomeActivity.java +++ b/src/conversations/java/eu/siacs/conversations/ui/WelcomeActivity.java @@ -2,18 +2,19 @@ package eu.siacs.conversations.ui; import android.content.Intent; import android.content.pm.ActivityInfo; +import android.databinding.DataBindingUtil; import android.os.Bundle; import android.support.annotation.NonNull; -import android.support.v7.app.ActionBar; import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.Toolbar; import android.view.Menu; import android.view.MenuItem; -import android.widget.Button; import android.widget.Toast; import java.util.List; import eu.siacs.conversations.R; +import eu.siacs.conversations.databinding.ActivityWelcomeBinding; import eu.siacs.conversations.entities.Account; import static eu.siacs.conversations.utils.PermissionUtils.allGranted; @@ -55,24 +56,18 @@ public class WelcomeActivity extends XmppActivity { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } super.onCreate(savedInstanceState); - setContentView(R.layout.welcome); - setSupportActionBar(findViewById(R.id.toolbar)); - final ActionBar ab = getSupportActionBar(); - if (ab != null) { - ab.setDisplayShowHomeEnabled(false); - ab.setDisplayHomeAsUpEnabled(false); - } - final Button createAccount = findViewById(R.id.create_account); - createAccount.setOnClickListener(v -> { - final Intent intent = new Intent(WelcomeActivity.this, MagicCreateActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); + ActivityWelcomeBinding binding = DataBindingUtil.setContentView(this,R.layout.activity_welcome); + setSupportActionBar((Toolbar) binding.toolbar); + configureActionBar(getSupportActionBar(), false); + binding.registerNewAccount.setOnClickListener(v -> { + final Intent intent = new Intent(this, PickServerActivity.class); addInviteUri(intent); startActivity(intent); }); - final Button useOwnProvider = findViewById(R.id.use_own_provider); - useOwnProvider.setOnClickListener(v -> { + binding.useExisting.setOnClickListener(v -> { List accounts = xmppConnectionService.getAccounts(); Intent intent = new Intent(WelcomeActivity.this, EditAccountActivity.class); + intent.putExtra(EditAccountActivity.EXTRA_FORCE_REGISTER,false); if (accounts.size() == 1) { intent.putExtra("jid", accounts.get(0).getJid().asBareJid().toString()); intent.putExtra("init", true); diff --git a/src/conversations/java/eu/siacs/conversations/utils/SignupUtils.java b/src/conversations/java/eu/siacs/conversations/utils/SignupUtils.java index ee5433cf4..6e8ed2eff 100644 --- a/src/conversations/java/eu/siacs/conversations/utils/SignupUtils.java +++ b/src/conversations/java/eu/siacs/conversations/utils/SignupUtils.java @@ -9,13 +9,23 @@ import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.ui.ConversationsActivity; import eu.siacs.conversations.ui.EditAccountActivity; import eu.siacs.conversations.ui.ManageAccountActivity; +import eu.siacs.conversations.ui.PickServerActivity; import eu.siacs.conversations.ui.StartConversationActivity; import eu.siacs.conversations.ui.WelcomeActivity; public class SignupUtils { public static Intent getSignUpIntent(final Activity activity) { - Intent intent = new Intent(activity, WelcomeActivity.class); + return getSignUpIntent(activity, false); + } + + public static Intent getSignUpIntent(final Activity activity, final boolean toServerChooser) { + Intent intent; + if (toServerChooser) { + intent = new Intent(activity, PickServerActivity.class); + } else { + intent = new Intent(activity, WelcomeActivity.class); + } StartConversationActivity.addInviteUri(intent, activity.getIntent()); return intent; } @@ -27,6 +37,9 @@ public class SignupUtils { if (pendingAccount != null) { intent = new Intent(activity, EditAccountActivity.class); intent.putExtra("jid", pendingAccount.getJid().asBareJid().toString()); + if (!pendingAccount.isOptionSet(Account.OPTION_MAGIC_CREATE)) { + intent.putExtra(EditAccountActivity.EXTRA_FORCE_REGISTER, pendingAccount.isOptionSet(Account.OPTION_REGISTER)); + } } else { if (service.getAccounts().size() == 0) { if (Config.X509_VERIFICATION) { diff --git a/src/conversations/res/layout/activity_pick_server.xml b/src/conversations/res/layout/activity_pick_server.xml new file mode 100644 index 000000000..16be52ec4 --- /dev/null +++ b/src/conversations/res/layout/activity_pick_server.xml @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + +