~singpolyma/cheogram-android

4c3d0f7a0e42c91d604c096af1f37a2a02843343 — Stephen Paul Weber 3 months ago 15692fb + f54fe5a
Merge branch 'start-join'

* start-join:
  Unify add contact and join channel
  If a MUC address is entered as a contact, join it
M src/cheogram/res/values/strings.xml => src/cheogram/res/values/strings.xml +1 -0
@@ 35,4 35,5 @@
    <string name="moderate_reason">Moderation Reason</string>
    <string name="unable_to_moderate">Unable to Moderate</string>
    <string name="block_media">Block Media</string>
    <string name="add_contact">New Contact or Channel</string>
</resources>

M src/main/java/eu/siacs/conversations/services/XmppConnectionService.java => src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +12 -0
@@ 136,6 136,7 @@ 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.Consumer;
import eu.siacs.conversations.utils.ConversationsFileObserver;
import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.utils.EasyOnboardingInvite;


@@ 3432,6 3433,17 @@ public class XmppConnectionService extends Service {
        }
    }

    public void checkIfMuc(final Account account, final Jid jid, Consumer<Boolean> cb) {
        IqPacket request = mIqGenerator.queryDiscoInfo(jid.asBareJid());
        sendIqPacket(account, request, (acct, reply) -> {
            ServiceDiscoveryResult result = new ServiceDiscoveryResult(reply);
            cb.accept(
                result.getFeatures().contains("http://jabber.org/protocol/muc") &&
                result.hasIdentity("conference", null)
            );
        });
    }

    public void fetchConferenceConfiguration(final Conversation conversation) {
        fetchConferenceConfiguration(conversation, null);
    }

M src/main/java/eu/siacs/conversations/ui/BlocklistActivity.java => src/main/java/eu/siacs/conversations/ui/BlocklistActivity.java +4 -2
@@ 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
				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();

M src/main/java/eu/siacs/conversations/ui/ChooseContactActivity.java => src/main/java/eu/siacs/conversations/ui/ChooseContactActivity.java +4 -2
@@ 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
                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());

M src/main/java/eu/siacs/conversations/ui/ConversationFragment.java => src/main/java/eu/siacs/conversations/ui/ConversationFragment.java +3 -0
@@ 2910,6 2910,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);

M src/main/java/eu/siacs/conversations/ui/EnterJidDialog.java => src/main/java/eu/siacs/conversations/ui/EnterJidDialog.java +37 -18
@@ 57,39 57,51 @@ 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();

    private EnterJidDialogBinding binding;
    private AlertDialog dialog;
    private boolean sanityCheckJid = false;
    private SanityCheck sanityCheckJid = SanityCheck.NO;

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

    public static enum SanityCheck {
        NO,
        YES,
        ALLOW_MUC
    }

    public static EnterJidDialog newInstance(
            final List<String> activatedAccounts,
            final String title,
            final String positiveButton,
            final String secondaryButton,
            final String prefilledJid,
            final String account,
            boolean allowEditJid,
            final boolean sanity_check_jid) {
            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.putBoolean(SANITY_CHECK_JID, sanity_check_jid);
        bundle.putInt(SANITY_CHECK_JID, sanity_check_jid.ordinal());
        bundle.putBoolean(SHOW_BOOKMARK_CHECKBOX, showBookmarkCheckbox);
        dialog.setArguments(bundle);
        return dialog;
    }


@@ 131,7 143,11 @@ public class EnterJidDialog extends DialogFragment implements OnBackendConnected
                binding.jid.setCursorVisible(false);
            }
        }
        sanityCheckJid = getArguments().getBoolean(SANITY_CHECK_JID, false);
        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);



@@ 185,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;
    }



@@ 217,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;
        }


@@ 236,7 255,7 @@ public class EnterJidDialog extends DialogFragment implements OnBackendConnected
                    return;
                }

                final Jid contactJid;
                Jid contactJid = null;
                try {
                    contactJid = Jid.ofEscaped(jidString);
                } catch (final IllegalArgumentException e) {


@@ 244,14 263,14 @@ public class EnterJidDialog extends DialogFragment implements OnBackendConnected
                    return;
                }

                if (!issuedWarning && sanityCheckJid) {
                if (!issuedWarning && sanityCheckJid != SanityCheck.NO) {
                    if (contactJid.isDomainJid()) {
                        binding.jidLayout.setError(getActivity().getString(R.string.this_looks_like_a_domain));
                        dialog.getButton(AlertDialog.BUTTON_POSITIVE).setText(R.string.add_anway);
                        issuedWarning = true;
                        return;
                    }
                    if (suspiciousSubDomain(contactJid.getDomain().toEscapedString())) {
                    if (sanityCheckJid != SanityCheck.ALLOW_MUC && suspiciousSubDomain(contactJid.getDomain().toEscapedString())) {
                        binding.jidLayout.setError(getActivity().getString(R.string.this_looks_like_channel));
                        dialog.getButton(AlertDialog.BUTTON_POSITIVE).setText(R.string.add_anway);
                        issuedWarning = true;


@@ 261,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) {


@@ 335,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 {

M src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java => src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java +54 -21
@@ 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
                true,
                EnterJidDialog.SanityCheck.ALLOW_MUC
        );

        dialog.setOnEnterJidDialogPositiveListener((accountJid, contactJid) -> {
        dialog.setOnEnterJidDialogPositiveListener((accountJid, contactJid, call, save) -> {
            if (!xmppConnectionServiceBound) {
                return false;
            }


@@ 548,25 547,55 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
            if (account == null) {
                return true;
            }

            final Contact contact = account.getRoster().getContact(contactJid);

            if (invite != null && invite.getName() != null) {
                contact.setServerName(invite.getName());
            }
            if (contact.isSelf()) {
                switchToConversation(contact);
                return true;
            } else if (contact.showInRoster()) {
                throw new EnterJidDialog.JidError(getString(R.string.contact_already_exists));
            } 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());
                }
                switchToConversationDoNotAppend(contact, invite == null ? null : invite.getBody());

            if (contact.isSelf() || contact.showInRoster()) {
                switchToConversationDoNotAppend(contact, invite == null ? null : invite.getBody(), call ? "call" : null);
                return true;
            }

            xmppConnectionService.checkIfMuc(account, contactJid, (isMuc) -> {
                if (isMuc) {
                    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 {
                    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(), call ? "call" : null);
                }

                dialog.dismiss();
            });

            return false;
        });
        dialog.show(ft, FRAGMENT_TAG_DIALOG);
    }


@@ 636,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

M src/main/java/eu/siacs/conversations/ui/widget/DialpadView.java => src/main/java/eu/siacs/conversations/ui/widget/DialpadView.java +1 -5
@@ 26,6 26,7 @@ import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.databinding.DataBindingUtil;
import eu.siacs.conversations.databinding.DialpadBinding;
import eu.siacs.conversations.R;
import eu.siacs.conversations.utils.Consumer;

public class DialpadView extends ConstraintLayout implements View.OnClickListener {



@@ 64,9 65,4 @@ public class DialpadView extends ConstraintLayout implements View.OnClickListene
    public void onClick(View v) {
        clickConsumer.accept(v.getTag().toString());
    }

	// Based on java.util.function.Consumer to avoid Android 24 dependency
	public interface Consumer<T> {
		void accept(T t);
	}
}

A src/main/java/eu/siacs/conversations/utils/Consumer.java => src/main/java/eu/siacs/conversations/utils/Consumer.java +6 -0
@@ 0,0 1,6 @@
package eu.siacs.conversations.utils;

// Based on java.util.function.Consumer to avoid Android 24 dependency
public interface Consumer<T> {
    void accept(T t);
}

M src/main/res/layout/enter_jid_dialog.xml => src/main/res/layout/enter_jid_dialog.xml +9 -0
@@ 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>

M src/main/res/menu/start_conversation_fab_submenu.xml => src/main/res/menu/start_conversation_fab_submenu.xml +1 -5
@@ 5,10 5,6 @@
        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"
        android:title="@string/create_public_channel" />


@@ 20,4 16,4 @@
        android:id="@+id/create_contact"
        android:icon="@drawable/ic_person_white_48dp"
        android:title="@string/add_contact" />
</menu>
\ No newline at end of file
</menu>