Unify add contact and join channel

Stephen Paul Weber created

Instead of making the user know what kind of JID they have, just let them enter
it and we can figure that out.

Instead of forcing them to add a contact or bookmark, make it optional with a
checkbox (default checked).

Instead of assuming they want to message the contact, allow them to choose to
call instead (coupled with PSTN gateway features this is effectively a dialler).

Change summary

src/cheogram/res/values/strings.xml                                    |  1 
src/main/java/eu/siacs/conversations/ui/BlocklistActivity.java         |  4 
src/main/java/eu/siacs/conversations/ui/ChooseContactActivity.java     |  4 
src/main/java/eu/siacs/conversations/ui/ConversationFragment.java      |  3 
src/main/java/eu/siacs/conversations/ui/EnterJidDialog.java            | 35 
src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java | 58 
src/main/res/layout/enter_jid_dialog.xml                               |  9 
src/main/res/menu/start_conversation_fab_submenu.xml                   |  6 
8 files changed, 86 insertions(+), 34 deletions(-)

Detailed changes

src/cheogram/res/values/strings.xml 🔗

@@ -31,4 +31,5 @@
     <string name="pref_dialler_integration_incoming_summary">Incoming calls from phone numbers may ring with your system dialler instead of this app\'s notification settings</string>
     <string name="save_as_sticker">Save as Sticker</string>
     <string name="sticker_name">Sticker Name</string>
+    <string name="add_contact">New Contact or Channel</string>
 </resources>

src/main/java/eu/siacs/conversations/ui/BlocklistActivity.java 🔗

@@ -80,12 +80,14 @@ public class BlocklistActivity extends AbstractSearchableListItemActivity implem
 				getString(R.string.block_jabber_id),
 				getString(R.string.block),
 				null,
+				null,
 				account.getJid().asBareJid().toEscapedString(),
 				true,
+				false,
 				EnterJidDialog.SanityCheck.NO
 		);
 
-		dialog.setOnEnterJidDialogPositiveListener((accountJid, contactJid) -> {
+		dialog.setOnEnterJidDialogPositiveListener((accountJid, contactJid, x, y) -> {
 			Blockable blockable = new RawBlockable(account, contactJid);
 			if (xmppConnectionService.sendBlockRequest(blockable, false)) {
 				Toast.makeText(BlocklistActivity.this, R.string.corresponding_conversations_closed, Toast.LENGTH_SHORT).show();

src/main/java/eu/siacs/conversations/ui/ChooseContactActivity.java 🔗

@@ -326,13 +326,15 @@ public class ChooseContactActivity extends AbstractSearchableListItemActivity im
                 mActivatedAccounts,
                 getString(R.string.enter_contact),
                 getString(R.string.select),
+                null,
                 jid == null ? null : jid.asBareJid().toString(),
                 getIntent().getStringExtra(EXTRA_ACCOUNT),
                 true,
+                false,
                 EnterJidDialog.SanityCheck.NO
         );
 
-        dialog.setOnEnterJidDialogPositiveListener((accountJid, contactJid) -> {
+        dialog.setOnEnterJidDialogPositiveListener((accountJid, contactJid, x, y) -> {
             final Intent request = getIntent();
             final Intent data = new Intent();
             data.putExtra("contact", contactJid.toString());

src/main/java/eu/siacs/conversations/ui/ConversationFragment.java 🔗

@@ -2859,6 +2859,9 @@ public class ConversationFragment extends XmppFragment
             attachFile(ATTACHMENT_CHOICE_RECORD_VOICE, false);
             return;
         }
+        if ("call".equals(postInitAction)) {
+            checkPermissionAndTriggerAudioCall();
+        }
         if ("message".equals(postInitAction)) {
             binding.conversationViewPager.post(() -> {
                 binding.conversationViewPager.setCurrentItem(0);

src/main/java/eu/siacs/conversations/ui/EnterJidDialog.java 🔗

@@ -57,11 +57,13 @@ public class EnterJidDialog extends DialogFragment implements OnBackendConnected
 
     private static final String TITLE_KEY = "title";
     private static final String POSITIVE_BUTTON_KEY = "positive_button";
+    private static final String SECONDARY_BUTTON_KEY = "secondary_button";
     private static final String PREFILLED_JID_KEY = "prefilled_jid";
     private static final String ACCOUNT_KEY = "account";
     private static final String ALLOW_EDIT_JID_KEY = "allow_edit_jid";
     private static final String ACCOUNTS_LIST_KEY = "activated_accounts_list";
     private static final String SANITY_CHECK_JID = "sanity_check_jid";
+    private static final String SHOW_BOOKMARK_CHECKBOX = "show_bookmark_checkbox";
 
     private KnownHostsAdapter knownHostsAdapter;
     private Collection<String> whitelistedDomains = Collections.emptyList();
@@ -83,19 +85,23 @@ public class EnterJidDialog extends DialogFragment implements OnBackendConnected
             final List<String> activatedAccounts,
             final String title,
             final String positiveButton,
+            final String secondaryButton,
             final String prefilledJid,
             final String account,
             boolean allowEditJid,
+            boolean showBookmarkCheckbox,
             final SanityCheck sanity_check_jid) {
         EnterJidDialog dialog = new EnterJidDialog();
         Bundle bundle = new Bundle();
         bundle.putString(TITLE_KEY, title);
         bundle.putString(POSITIVE_BUTTON_KEY, positiveButton);
+        bundle.putString(SECONDARY_BUTTON_KEY, secondaryButton);
         bundle.putString(PREFILLED_JID_KEY, prefilledJid);
         bundle.putString(ACCOUNT_KEY, account);
         bundle.putBoolean(ALLOW_EDIT_JID_KEY, allowEditJid);
         bundle.putStringArrayList(ACCOUNTS_LIST_KEY, (ArrayList<String>) activatedAccounts);
         bundle.putInt(SANITY_CHECK_JID, sanity_check_jid.ordinal());
+        bundle.putBoolean(SHOW_BOOKMARK_CHECKBOX, showBookmarkCheckbox);
         dialog.setArguments(bundle);
         return dialog;
     }
@@ -139,6 +145,10 @@ public class EnterJidDialog extends DialogFragment implements OnBackendConnected
         }
         sanityCheckJid = SanityCheck.values()[getArguments().getInt(SANITY_CHECK_JID, SanityCheck.NO.ordinal())];
 
+        if (!getArguments().getBoolean(SHOW_BOOKMARK_CHECKBOX, false)) {
+            binding.bookmark.setVisibility(View.GONE);
+        }
+
         DelayedHintHelper.setHint(R.string.account_settings_example_jabber_id, binding.jid);
 
         String account = getArguments().getString(ACCOUNT_KEY);
@@ -191,23 +201,26 @@ public class EnterJidDialog extends DialogFragment implements OnBackendConnected
         });
 
         builder.setView(binding.getRoot());
-        builder.setNegativeButton(R.string.cancel, null);
         builder.setPositiveButton(getArguments().getString(POSITIVE_BUTTON_KEY), null);
+        if (getArguments().getString(SECONDARY_BUTTON_KEY) == null) {
+            builder.setNegativeButton(R.string.cancel, null);
+        } else {
+            builder.setNegativeButton(getArguments().getString(SECONDARY_BUTTON_KEY), null);
+            builder.setNeutralButton(R.string.cancel, null);
+        }
         this.dialog = builder.create();
 
-        View.OnClickListener dialogOnClick =
-                v -> {
-                    handleEnter(binding, account);
-                };
-
         binding.jid.setOnEditorActionListener(
                 (v, actionId, event) -> {
-                    handleEnter(binding, account);
+                    handleEnter(binding, account, false);
                     return true;
                 });
 
         dialog.show();
-        dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(dialogOnClick);
+        dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener((v) -> handleEnter(binding, account, false));
+        if (getArguments().getString(SECONDARY_BUTTON_KEY) != null) {
+            dialog.getButton(AlertDialog.BUTTON_NEGATIVE).setOnClickListener((v) -> handleEnter(binding, account, true));
+        }
         return dialog;
     }
 
@@ -223,7 +236,7 @@ public class EnterJidDialog extends DialogFragment implements OnBackendConnected
         }
     }
 
-    private void handleEnter(EnterJidDialogBinding binding, String account) {
+    private void handleEnter(EnterJidDialogBinding binding, String account, boolean secondary) {
         if (!binding.account.isEnabled() && account == null) {
             return;
         }
@@ -267,7 +280,7 @@ public class EnterJidDialog extends DialogFragment implements OnBackendConnected
 
                 if (mListener != null) {
                     try {
-                        if (mListener.onEnterJidDialogPositive(accountJid, contactJid)) {
+                        if (mListener.onEnterJidDialogPositive(accountJid, contactJid, secondary, binding.bookmark.isChecked())) {
                             dialog.dismiss();
                         }
                     } catch (JidError error) {
@@ -341,7 +354,7 @@ public class EnterJidDialog extends DialogFragment implements OnBackendConnected
     }
 
     public interface OnEnterJidDialogPositiveListener {
-        boolean onEnterJidDialogPositive(Jid account, Jid contact) throws EnterJidDialog.JidError;
+        boolean onEnterJidDialogPositive(Jid account, Jid contact, boolean secondary, boolean save) throws EnterJidDialog.JidError;
     }
 
     public static class JidError extends Exception {

src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java 🔗

@@ -339,9 +339,6 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
                 case R.id.discover_public_channels:
                     startActivity(new Intent(this, ChannelDiscoveryActivity.class));
                     break;
-                case R.id.join_public_channel:
-                    showJoinConferenceDialog(prefilled);
-                    break;
                 case R.id.create_private_group_chat:
                     showCreatePrivateGroupChatDialog();
                     break;
@@ -531,15 +528,17 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
         ft.addToBackStack(null);
         EnterJidDialog dialog = EnterJidDialog.newInstance(
                 mActivatedAccounts,
-                getString(R.string.add_contact),
-                getString(R.string.add),
+                getString(R.string.start_conversation),
+                getString(R.string.message),
+                "Call",
                 prefilledJid,
                 invite == null ? null : invite.account,
                 invite == null || !invite.hasFingerprints(),
+                true,
                 EnterJidDialog.SanityCheck.ALLOW_MUC
         );
 
-        dialog.setOnEnterJidDialogPositiveListener((accountJid, contactJid) -> {
+        dialog.setOnEnterJidDialogPositiveListener((accountJid, contactJid, call, save) -> {
             if (!xmppConnectionServiceBound) {
                 return false;
             }
@@ -555,25 +554,48 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
             }
 
             if (contact.isSelf() || contact.showInRoster()) {
-                switchToConversationDoNotAppend(contact, invite == null ? null : invite.getBody());
+                switchToConversationDoNotAppend(contact, invite == null ? null : invite.getBody(), call ? "call" : null);
                 return true;
             }
 
             xmppConnectionService.checkIfMuc(account, contactJid, (isMuc) -> {
                 if (isMuc) {
-                    final Conversation conversation = xmppConnectionService.findOrCreateConversation(account, contactJid, true, true, true);
-                    switchToConversationDoNotAppend(conversation, invite == null ? null : invite.getBody());
+                    if (save) {
+                        Bookmark bookmark = account.getBookmark(contactJid);
+                        if (bookmark != null) {
+                            openConversationsForBookmark(bookmark);
+                        } else {
+                            bookmark = new Bookmark(account, contactJid.asBareJid());
+                            bookmark.setAutojoin(getBooleanPreference("autojoin", R.bool.autojoin));
+                            final String nick = contactJid.getResource();
+                            if (nick != null && !nick.isEmpty() && !nick.equals(MucOptions.defaultNick(account))) {
+                                bookmark.setNick(nick);
+                            }
+                            xmppConnectionService.createBookmark(account, bookmark);
+                            final Conversation conversation = xmppConnectionService
+                                    .findOrCreateConversation(account, contactJid, true, true, true);
+                            bookmark.setConversation(conversation);
+                            switchToConversationDoNotAppend(conversation, invite == null ? null : invite.getBody());
+                        }
+                    } else {
+                        final Conversation conversation = xmppConnectionService.findOrCreateConversation(account, contactJid, true, true, true);
+                        switchToConversationDoNotAppend(conversation, invite == null ? null : invite.getBody());
+                    }
                 } else {
-                    final String preAuth = invite == null ? null : invite.getParameter(XmppUri.PARAMETER_PRE_AUTH);
-                    xmppConnectionService.createContact(contact, true, preAuth);
-                    if (invite != null && invite.hasFingerprints()) {
-                        xmppConnectionService.verifyFingerprints(contact, invite.getFingerprints());
+                    if (save) {
+                        final String preAuth = invite == null ? null : invite.getParameter(XmppUri.PARAMETER_PRE_AUTH);
+                        xmppConnectionService.createContact(contact, true, preAuth);
+                        if (invite != null && invite.hasFingerprints()) {
+                            xmppConnectionService.verifyFingerprints(contact, invite.getFingerprints());
+                        }
                     }
-                    switchToConversationDoNotAppend(contact, invite == null ? null : invite.getBody());
+                    switchToConversationDoNotAppend(contact, invite == null ? null : invite.getBody(), call ? "call" : null);
                 }
+
+                dialog.dismiss();
             });
 
-            return true;
+            return false;
         });
         dialog.show(ft, FRAGMENT_TAG_DIALOG);
     }
@@ -643,8 +665,12 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
     }
 
     protected void switchToConversationDoNotAppend(Contact contact, String body) {
+        switchToConversationDoNotAppend(contact, body, null);
+    }
+
+    protected void switchToConversationDoNotAppend(Contact contact, String body, String postInit) {
         Conversation conversation = xmppConnectionService.findOrCreateConversation(contact.getAccount(), contact.getJid(), false, true);
-        switchToConversationDoNotAppend(conversation, body);
+        switchToConversation(conversation, body, false, null, false, true, postInit);
     }
 
     @Override

src/main/res/layout/enter_jid_dialog.xml 🔗

@@ -43,5 +43,14 @@
                 android:imeOptions="actionDone|flagNoExtractUi"
                 android:inputType="textEmailAddress" />
         </com.google.android.material.textfield.TextInputLayout>
+
+        <CheckBox
+            android:id="@+id/bookmark"
+            style="@style/Widget.Conversations.CheckBox"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="8dp"
+            android:checked="true"
+            android:text="Save as Contact / Bookmark"/>
     </LinearLayout>
 </layout>

src/main/res/menu/start_conversation_fab_submenu.xml 🔗

@@ -4,10 +4,6 @@
         android:id="@+id/discover_public_channels"
         android:icon="@drawable/ic_search_white_24dp"
         android:title="@string/discover_channels" />
-    <item
-        android:id="@+id/join_public_channel"
-        android:icon="@drawable/ic_input_white_24dp"
-        android:title="@string/join_public_channel" />
     <item
         android:id="@+id/create_public_channel"
         android:icon="@drawable/ic_public_white_24dp"
@@ -20,4 +16,4 @@
         android:id="@+id/create_contact"
         android:icon="@drawable/ic_person_white_48dp"
         android:title="@string/add_contact" />
-</menu>
+</menu>