~singpolyma/cheogram-android

bea715c50f00356bc6e75a638ae9074d23a2a2ed — Stephen Paul Weber 10 months ago 512dd7f + 4c04210
Merge branch 'inversion'

* inversion:
  Don't jump straight in, since the conversation list is safe now
  Never show setup dialogs during onboarding
  Try to get list of commands for any domain jid
  Hide cancel button when onboarding
  When entering cheogram.com conversation while onboarding, go to register
  Hide menu and fab when onboarding
  Use the JID from the link
  Never show tabs in onboarding mode
  This doesn't even work, because we never treat cheogram.com as a stranger
  If onboarding and just cheogram.com conversation open, switch into command flow
  When starting command, wait for account to connect
  Switch to existing session when there is one, for incoming intent
  Start onboarding where we left off
  Fix NPE
  Fix NPE
  Allow cancel onboarding
  Inverted flow happy path
  If a command response indicates an XMPP URI, jump to that
  Make sure UI update happens even if the View was not attached when the iq came in
M src/cheogram/java/eu/siacs/conversations/ui/WelcomeActivity.java => src/cheogram/java/eu/siacs/conversations/ui/WelcomeActivity.java +37 -5
@@ 17,15 17,18 @@ import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;

import java.security.SecureRandom;
import java.util.Arrays;
import java.util.List;
import java.util.HashSet;
import java.util.UUID;

import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.databinding.ActivityWelcomeBinding;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.utils.Compatibility;
import eu.siacs.conversations.utils.InstallReferrerUtils;
import eu.siacs.conversations.utils.SignupUtils;


@@ 35,11 38,12 @@ import eu.siacs.conversations.xmpp.Jid;
import static eu.siacs.conversations.utils.PermissionUtils.allGranted;
import static eu.siacs.conversations.utils.PermissionUtils.writeGranted;

public class WelcomeActivity extends XmppActivity implements XmppConnectionService.OnAccountCreated, KeyChainAliasCallback {
public class WelcomeActivity extends XmppActivity implements XmppConnectionService.OnAccountCreated, XmppConnectionService.OnAccountUpdate, KeyChainAliasCallback {

    private static final int REQUEST_IMPORT_BACKUP = 0x63fb;

    private XmppUri inviteUri;
    private Account onboardingAccount = null;

    public static void launch(AppCompatActivity activity) {
        Intent intent = new Intent(activity, WelcomeActivity.class);


@@ 82,8 86,21 @@ public class WelcomeActivity extends XmppActivity implements XmppConnectionServi
    }

    @Override
    protected void refreshUiReal() {
    protected synchronized void refreshUiReal() {
        if (onboardingAccount == null) return;
        if (onboardingAccount.getStatus() != Account.State.ONLINE) return;

        Intent intent = new Intent(this, StartConversationActivity.class);
        intent.putExtra("init", true);
        intent.putExtra(EXTRA_ACCOUNT, onboardingAccount.getJid().asBareJid().toEscapedString());
        onboardingAccount = null;
        startActivity(intent);
        finish();
    }

    @Override
    public void onAccountUpdate() {
        refreshUi();
    }

    @Override


@@ 124,9 141,18 @@ public class WelcomeActivity extends XmppActivity implements XmppConnectionServi
        setSupportActionBar(binding.toolbar);
        configureActionBar(getSupportActionBar(), false);
        binding.registerNewAccount.setOnClickListener(v -> {
            final Intent intent = new Intent(this, MagicCreateActivity.class);
            addInviteUri(intent);
            startActivity(intent);
            if (hasInviteUri()) {
                final Intent intent = new Intent(this, MagicCreateActivity.class);
                addInviteUri(intent);
                startActivity(intent);
            } else {
                binding.registerNewAccount.setText("Working...");
                binding.registerNewAccount.setEnabled(false);
                onboardingAccount = new Account(Jid.ofLocalAndDomain(UUID.randomUUID().toString(), Config.ONBOARDING_DOMAIN.toEscapedString()), CryptoHelper.createPassword(new SecureRandom()));
                onboardingAccount.setOption(Account.OPTION_REGISTER, true);
                onboardingAccount.setOption(Account.OPTION_FIXED_USERNAME, true);
                xmppConnectionService.createAccount(onboardingAccount);
            }
        });
        binding.useExisting.setOnClickListener(v -> {
            final List<Account> accounts = xmppConnectionService.getAccounts();


@@ 235,6 261,12 @@ public class WelcomeActivity extends XmppActivity implements XmppConnectionServi
        }
    }

    protected boolean hasInviteUri() {
        final Intent from = getIntent();
        if (from != null && from.hasExtra(StartConversationActivity.EXTRA_INVITE_URI)) return true;
        return this.inviteUri != null;
    }

    public void addInviteUri(Intent to) {
        final Intent from = getIntent();
        if (from != null && from.hasExtra(StartConversationActivity.EXTRA_INVITE_URI)) {

M src/cheogram/java/eu/siacs/conversations/utils/SignupUtils.java => src/cheogram/java/eu/siacs/conversations/utils/SignupUtils.java +2 -3
@@ 62,7 62,7 @@ public class SignupUtils {
                if (Config.X509_VERIFICATION) {
                    intent = new Intent(activity, ManageAccountActivity.class);
                } else if (Config.MAGIC_CREATE_DOMAIN != null) {
                    intent = getSignUpIntent(activity);
                    intent = activity.xmppConnectionService.getPreferences().contains("onboarding_action") ? new Intent(activity, MagicCreateActivity.class) : getSignUpIntent(activity);
                } else {
                    intent = new Intent(activity, EditAccountActivity.class);
                }


@@ 70,8 70,7 @@ public class SignupUtils {
                intent = new Intent(activity, StartConversationActivity.class);
            }
        }
        intent.putExtra("init", true);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
        return intent;
    }
}
\ No newline at end of file
}

M src/main/java/eu/siacs/conversations/Config.java => src/main/java/eu/siacs/conversations/Config.java +1 -0
@@ 48,6 48,7 @@ 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 = "chatterboxtown.us";
    public static final Jid QUICKSY_DOMAIN = Jid.of("cheogram.com");
    public static final Jid ONBOARDING_DOMAIN = Jid.of("onboarding.cheogram.com");

    public static final String CHANNEL_DISCOVERY = "https://search.jabber.network";


M src/main/java/eu/siacs/conversations/entities/Conversation.java => src/main/java/eu/siacs/conversations/entities/Conversation.java +103 -21
@@ 3,6 3,7 @@ package eu.siacs.conversations.entities;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.Cursor;
import android.database.DataSetObserver;
import android.graphics.drawable.BitmapDrawable;


@@ 102,6 103,7 @@ import eu.siacs.conversations.persistance.DatabaseBackend;
import eu.siacs.conversations.services.AvatarService;
import eu.siacs.conversations.services.QuickConversationsService;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.ui.UriHandlerActivity;
import eu.siacs.conversations.ui.text.FixedURLSpan;
import eu.siacs.conversations.ui.util.ShareUtil;
import eu.siacs.conversations.ui.util.SoftKeyboardUtils;


@@ 1301,8 1303,12 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
        pagerAdapter.startCommand(command, xmppConnectionService);
    }

    public void setupViewPager(ViewPager pager, TabLayout tabs) {
        pagerAdapter.setupViewPager(pager, tabs);
    public boolean switchToSession(final String node) {
        return pagerAdapter.switchToSession(node);
    }

    public void setupViewPager(ViewPager pager, TabLayout tabs, boolean onboarding) {
        pagerAdapter.setupViewPager(pager, tabs, onboarding);
    }

    public void showViewPager() {


@@ 1341,10 1347,12 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
        ArrayList<CommandSession> sessions = null;
        protected View page1 = null;
        protected View page2 = null;
        protected boolean mOnboarding = false;

        public void setupViewPager(ViewPager pager, TabLayout tabs) {
        public void setupViewPager(ViewPager pager, TabLayout tabs, boolean onboarding) {
            mPager = pager;
            mTabs = tabs;
            mOnboarding = onboarding;

            if (mPager == null) return;
            if (sessions != null) show();


@@ 1370,7 1378,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
                sessions = new ArrayList<>();
                notifyDataSetChanged();
            }
            if (mTabs != null) mTabs.setVisibility(View.VISIBLE);
            if (!mOnboarding && mTabs != null) mTabs.setVisibility(View.VISIBLE);
        }

        public void hide() {


@@ 1390,27 1398,32 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
            final Element c = packet.addChild("command", Namespace.COMMANDS);
            c.setAttribute("node", command.getAttribute("node"));
            c.setAttribute("action", "execute");
            View v = mPager;

            final TimerTask task = new TimerTask() {
                @Override
                public void run() {
                    if (getAccount().getStatus() != Account.State.ONLINE) {
                        new Timer().schedule(this, 1000);
                    } else {
                        xmppConnectionService.sendIqPacket(getAccount(), packet, (a, iq) -> {
                            session.updateWithResponse(iq);
                        });
                    }
                }
            };
            task.run();

            if (command.getAttribute("node").equals("jabber:iq:register") && packet.getTo().asBareJid().equals(Jid.of("cheogram.com"))) {
                new com.cheogram.android.CheogramLicenseChecker(v.getContext(), (signedData, signature) -> {
                new com.cheogram.android.CheogramLicenseChecker(mPager.getContext(), (signedData, signature) -> {
                    if (signedData != null && signature != null) {
                        c.addChild("license", "https://ns.cheogram.com/google-play").setContent(signedData);
                        c.addChild("licenseSignature", "https://ns.cheogram.com/google-play").setContent(signature);
                    }

                    xmppConnectionService.sendIqPacket(getAccount(), packet, (a, iq) -> {
                        v.post(() -> {
                            session.updateWithResponse(iq);
                        });
                    });
                    task.run();
                }).checkLicense();
            } else {
                xmppConnectionService.sendIqPacket(getAccount(), packet, (a, iq) -> {
                    v.post(() -> {
                        session.updateWithResponse(iq);
                    });
                });
                task.run();
            }

            sessions.add(session);


@@ 1423,6 1436,21 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
            notifyDataSetChanged();
        }

        public boolean switchToSession(final String node) {
            if (sessions == null) return false;

            int i = 0;
            for (CommandSession session : sessions) {
                if (session.mNode.equals(node)) {
                    if (mPager != null) mPager.setCurrentItem(i + 2);
                    return true;
                }
                i++;
            }

            return false;
        }

        @NonNull
        @Override
        public Object instantiateItem(@NonNull ViewGroup container, int position) {


@@ 2376,6 2404,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
            protected GridLayoutManager layoutManager;
            protected WebView actionToWebview = null;
            protected int fillableFieldCount = 0;
            protected IqPacket pendingResponsePacket = null;

            CommandSession(String title, String node, XmppConnectionService xmppConnectionService) {
                loading();


@@ 2401,7 2430,15 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
                return mTitle;
            }

            public void updateWithResponse(IqPacket iq) {
            public void updateWithResponse(final IqPacket iq) {
                if (getView() != null && getView().isAttachedToWindow()) {
                    getView().post(() -> updateWithResponseUiThread(iq));
                } else {
                    pendingResponsePacket = iq;
                }
            }

            protected void updateWithResponseUiThread(final IqPacket iq) {
                this.loadingTimer.cancel();
                this.loadingTimer = new Timer();
                this.loading = false;


@@ 2476,6 2513,13 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
                                    this.responseElement = el;
                                    break;
                                }
                                if (scheme.equals("xmpp")) {
                                    final Intent intent = new Intent(getView().getContext(), UriHandlerActivity.class);
                                    intent.setAction(Intent.ACTION_VIEW);
                                    intent.setData(Uri.parse(url));
                                    getView().getContext().startActivity(intent);
                                    break;
                                }
                            }
                        }
                        if (el.getName().equals("note") && el.getNamespace().equals("http://jabber.org/protocol/commands")) {


@@ 2485,6 2529,16 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
                    }

                    if (responseElement == null && command.getAttribute("status") != null && (command.getAttribute("status").equals("completed") || command.getAttribute("status").equals("canceled"))) {
                        if (mNode.equals("jabber:iq:register") && command.getAttribute("status").equals("canceled")) {
                            if (xmppConnectionService.isOnboarding()) {
                                if (!xmppConnectionService.getPreferences().contains("onboarding_action")) {
                                    xmppConnectionService.getPreferences().edit().putString("onboarding_action", "cancel").commit();
                                }
                                xmppConnectionService.deleteAccount(getAccount());
                            }
                            xmppConnectionService.archiveConversation(Conversation.this);
                        }

                        removeSession(this);
                        return;
                    }


@@ 2498,7 2552,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
                    if (!actionsAdapter.isEmpty() || fillableFieldCount > 0) {
                        if (command.getAttribute("status").equals("completed") || command.getAttribute("status").equals("canceled")) {
                            actionsAdapter.add(Pair.create("close", "close"));
                        } else if (actionsAdapter.getPosition("cancel") < 0) {
                        } else if (actionsAdapter.getPosition("cancel") < 0 && !xmppConnectionService.isOnboarding()) {
                            actionsAdapter.insert(Pair.create("cancel", "cancel"), 0);
                        }
                    }


@@ 2508,6 2562,25 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
                    actionsAdapter.add(Pair.create("close", "close"));
                }

                actionsAdapter.sort((x, y) -> {
                    if (x.first.equals("cancel")) return -1;
                    if (y.first.equals("cancel")) return 1;
                    if (x.first.equals("prev") && xmppConnectionService.isOnboarding()) return -1;
                    if (y.first.equals("prev") && xmppConnectionService.isOnboarding()) return 1;
                    return 0;
                });

                Data dataForm = null;
                if (responseElement != null && responseElement.getName().equals("x") && responseElement.getNamespace().equals("jabber:x:data")) dataForm = Data.parse(responseElement);
                if (mNode.equals("jabber:iq:register") &&
                    xmppConnectionService.getPreferences().contains("onboarding_action") &&
                    dataForm != null && dataForm.getFieldByName("gateway-jid") != null) {


                    dataForm.put("gateway-jid", xmppConnectionService.getPreferences().getString("onboarding_action", ""));
                    execute();
                }
                xmppConnectionService.getPreferences().edit().remove("onboarding_action").commit();
                notifyDataSetChanged();
            }



@@ 2693,6 2766,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
            }

            public View getView() {
                if (mBinding == null) return null;
                return mBinding.getRoot();
            }



@@ 2750,6 2824,10 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
                        c.setAttribute("action", "execute");
                    }

                    if (mNode.equals("jabber:iq:register") && xmppConnectionService.isOnboarding() && form.getValue("gateway-jid") != null) {
                        xmppConnectionService.getPreferences().edit().putString("onboarding_action", form.getValue("gateway-jid")).commit();
                    }

                    responseElement.setAttribute("type", "submit");
                    Element rsm = responseElement.findChild("set", "http://jabber.org/protocol/rsm");
                    if (rsm != null) {


@@ 2764,9 2842,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
                if (c.getAttribute("action") == null) c.setAttribute("action", action);

                xmppConnectionService.sendIqPacket(getAccount(), packet, (a, iq) -> {
                    getView().post(() -> {
                        updateWithResponse(iq);
                    });
                    updateWithResponse(iq);
                });

                loading();


@@ 2861,6 2937,12 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
                });

                actionsAdapter.notifyDataSetChanged();

                if (pendingResponsePacket != null) {
                    final IqPacket pending = pendingResponsePacket;
                    pendingResponsePacket = null;
                    updateWithResponseUiThread(pending);
                }
            }

            // https://stackoverflow.com/a/36037991/8611

M src/main/java/eu/siacs/conversations/services/XmppConnectionService.java => src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +4 -0
@@ 1805,6 1805,10 @@ public class XmppConnectionService extends Service {
        sendMessage(message, true, delay);
    }

    public boolean isOnboarding() {
        return getAccounts().size() == 1 && getAccounts().get(0).getJid().getDomain().equals(Config.ONBOARDING_DOMAIN);
    }

    public void requestEasyOnboardingInvite(final Account account, final EasyOnboardingInvite.OnInviteRequested callback) {
        final XmppConnection connection = account.getXmppConnection();
        final Jid jid = connection == null ? null : connection.getJidForCommand(Namespace.EASY_ONBOARDING_INVITE);

M src/main/java/eu/siacs/conversations/ui/ConversationFragment.java => src/main/java/eu/siacs/conversations/ui/ConversationFragment.java +14 -4
@@ 1202,6 1202,8 @@ public class ConversationFragment extends XmppFragment

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
        if (activity.xmppConnectionService.isOnboarding()) return;

        menuInflater.inflate(R.menu.fragment_conversation, menu);
        final MenuItem menuMucDetails = menu.findItem(R.id.action_muc_details);
        final MenuItem menuContactDetails = menu.findItem(R.id.action_contact_details);


@@ 1357,7 1359,7 @@ public class ConversationFragment extends XmppFragment
        messageListAdapter.setOnContactPictureClicked(null);
        messageListAdapter.setOnContactPictureLongClicked(null);
        messageListAdapter.setOnInlineImageLongClicked(null);
        if (conversation != null) conversation.setupViewPager(null, null);
        if (conversation != null) conversation.setupViewPager(null, null, false);
    }

    private void quoteText(String text) {


@@ 2836,12 2838,12 @@ public class ConversationFragment extends XmppFragment
                .setOpenConversation(this.conversation);

        if (commandAdapter != null && conversation != originalConversation) {
            originalConversation.setupViewPager(null, null);
            conversation.setupViewPager(binding.conversationViewPager, binding.tabLayout);
            originalConversation.setupViewPager(null, null, false);
            conversation.setupViewPager(binding.conversationViewPager, binding.tabLayout, activity.xmppConnectionService.isOnboarding());
            refreshCommands(false);
        }
        if (commandAdapter == null && conversation != null) {
            conversation.setupViewPager(binding.conversationViewPager, binding.tabLayout);
            conversation.setupViewPager(binding.conversationViewPager, binding.tabLayout, activity.xmppConnectionService.isOnboarding());
            commandAdapter = new CommandAdapter((XmppActivity) getActivity());
            binding.commandsView.setAdapter(commandAdapter);
            binding.commandsView.setOnItemClickListener((parent, view, position, id) -> {


@@ 2862,6 2864,9 @@ public class ConversationFragment extends XmppFragment
        if (commandAdapter == null) return;

        Jid commandJid = conversation.getContact().resourceWhichSupport(Namespace.COMMANDS);
        if (commandJid == null && conversation.getJid().isDomainJid()) {
            commandJid = conversation.getJid();
        }
        if (commandJid == null) {
            conversation.hideViewPager();
        } else {


@@ 3006,6 3011,11 @@ public class ConversationFragment extends XmppFragment
        if (message != null) {
            startDownloadable(message);
        }
        if (activity.xmppConnectionService.isOnboarding() && conversation.getJid().equals(Jid.of("cheogram.com"))) {
            if (!conversation.switchToSession("jabber:iq:register")) {
                conversation.startCommand(commandFor(Jid.of("cheogram.com/CHEOGRAM%jabber:iq:register"), "jabber:iq:register"), activity.xmppConnectionService);
            }
        }
    }

    private Element commandFor(final Jid jid, final String node) {

M src/main/java/eu/siacs/conversations/ui/ConversationsActivity.java => src/main/java/eu/siacs/conversations/ui/ConversationsActivity.java +6 -5
@@ 199,6 199,7 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
        if (xmppConnectionService == null) {
            return false;
        }

        boolean isConversationsListEmpty = xmppConnectionService.isConversationsListEmpty(ignore);
        if (isConversationsListEmpty && mRedirectInProcess.compareAndSet(false, true)) {
            final Intent intent = SignupUtils.getRedirectionIntent(this);


@@ 216,7 217,7 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
    }

    private void showDialogsIfMainIsOverview() {
        if (xmppConnectionService == null) {
        if (xmppConnectionService == null || xmppConnectionService.isOnboarding()) {
            return;
        }
        final Fragment fragment = getFragmentManager().findFragmentById(R.id.main_fragment);


@@ 486,7 487,7 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
        getMenuInflater().inflate(R.menu.activity_conversations, menu);
        final MenuItem qrCodeScanMenuItem = menu.findItem(R.id.action_scan_qr_code);
        if (qrCodeScanMenuItem != null) {
            if (isCameraFeatureAvailable()) {
            if (isCameraFeatureAvailable() && (xmppConnectionService == null || !xmppConnectionService.isOnboarding())) {
                Fragment fragment = getFragmentManager().findFragmentById(R.id.main_fragment);
                boolean visible = getResources().getBoolean(R.bool.show_qr_code_scan)
                        && fragment instanceof ConversationsOverviewFragment;


@@ 576,7 577,7 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
            final Conversation conversation = xmppConnectionService.findUniqueConversationByJid(xmppUri);
            if (conversation != null) {
                if (xmppUri.isAction("command")) {
                    startCommand(conversation.getAccount(), conversation.getJid(), xmppUri.getParameter("node"));
                    startCommand(conversation.getAccount(), xmppUri.getJid(), xmppUri.getParameter("node"));
                } else {
                    Bundle extras = new Bundle();
                    extras.putString(Intent.EXTRA_TEXT, xmppUri.getBody());


@@ 740,10 741,10 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
            final Conversation conversation = ((ConversationFragment) mainFragment).getConversation();
            if (conversation != null) {
                actionBar.setTitle(conversation.getName());
                actionBar.setDisplayHomeAsUpEnabled(true);
                actionBar.setDisplayHomeAsUpEnabled(!xmppConnectionService.isOnboarding());
                ActionBarUtil.setActionBarOnClickListener(
                        binding.toolbar,
                        (v) -> openConversationDetails(conversation)
                        (v) -> { if(!xmppConnectionService.isOnboarding()) openConversationDetails(conversation); }
                );
                return;
            }

M src/main/java/eu/siacs/conversations/ui/ConversationsOverviewFragment.java => src/main/java/eu/siacs/conversations/ui/ConversationsOverviewFragment.java +12 -0
@@ 316,6 316,13 @@ public class ConversationsOverviewFragment extends XmppFragment {
		AccountUtils.showHideMenuItems(menu);
		final MenuItem easyOnboardInvite = menu.findItem(R.id.action_easy_invite);
		easyOnboardInvite.setVisible(EasyOnboardingInvite.anyHasSupport(activity == null ? null : activity.xmppConnectionService));
		if (activity.xmppConnectionService != null && activity.xmppConnectionService.isOnboarding()) {
			final MenuItem manageAccounts = menu.findItem(R.id.action_accounts);
			if (manageAccounts != null) manageAccounts.setVisible(false);

			final MenuItem settings = menu.findItem(R.id.action_settings);
			if (settings != null) settings.setVisible(false);
		}
	}

	 @Override


@@ 380,6 387,11 @@ public class ConversationsOverviewFragment extends XmppFragment {
	@Override
	public void onBackendConnected() {
		refresh();
		if (activity.xmppConnectionService.isOnboarding()) {
			binding.fab.setVisibility(View.GONE);
		} else {
			binding.fab.setVisibility(View.VISIBLE);
		}
	}

	@Override

M src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java => src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java +94 -5
@@ 95,9 95,13 @@ import eu.siacs.conversations.ui.widget.SwipeRefreshListFragment;
import eu.siacs.conversations.utils.AccountUtils;
import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.utils.XmppUri;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xml.Namespace;
import eu.siacs.conversations.xmpp.Jid;
import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
import eu.siacs.conversations.xmpp.XmppConnection;
import eu.siacs.conversations.xmpp.forms.Data;
import eu.siacs.conversations.xmpp.stanzas.IqPacket;

public class StartConversationActivity extends XmppActivity implements XmppConnectionService.OnConversationUpdate, OnRosterUpdate, OnUpdateBlocklist, CreatePrivateGroupChatDialog.CreateConferenceDialogListener, JoinConferenceDialog.JoinConferenceDialogListener, SwipeRefreshLayout.OnRefreshListener, CreatePublicChannelDialog.CreatePublicChannelDialogListener {



@@ 913,13 917,19 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
        configureHomeButton();
        Intent intent = pendingViewIntent.pop();

        if (intent != null && intent.getBooleanExtra("init", false)) {
        final boolean onboardingCancel = xmppConnectionService.getPreferences().getString("onboarding_action", "").equals("cancel");
        if (onboardingCancel) xmppConnectionService.getPreferences().edit().remove("onboarding_action").commit();

        if (intent != null && intent.getBooleanExtra("init", false) && !onboardingCancel) {
            Account selectedAccount = xmppConnectionService.getAccounts().get(0);
            final String accountJid = intent.getStringExtra(EXTRA_ACCOUNT);
            intent = null;
            boolean hasPstnOrSms = false;
            Account onboardingAccount = null;
            outer:
            for (Account account : xmppConnectionService.getAccounts()) {
                if (onboardingAccount == null && account.getJid().getDomain().equals(Config.ONBOARDING_DOMAIN)) onboardingAccount = account;

                if (accountJid != null) {
                    if(account.getJid().asBareJid().toEscapedString().equals(accountJid)) {
                        selectedAccount = account;


@@ 941,9 951,88 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
            }

            if (!hasPstnOrSms) {
                startCommand(selectedAccount, Jid.of("cheogram.com/CHEOGRAM%jabber:iq:register"), "jabber:iq:register");
                finish();
                return;
                if (onboardingAccount != null && !selectedAccount.getJid().equals(onboardingAccount.getJid())) {
                    final Account onboardAccount = onboardingAccount;
                    final Account newAccount = selectedAccount;
                    final IqPacket packet = new IqPacket(IqPacket.TYPE.SET);
                    packet.setTo(Jid.of("cheogram.com"));
                    final Element c = packet.addChild("command", Namespace.COMMANDS);
                    c.setAttribute("node", "change jabber id");
                    c.setAttribute("action", "execute");

                    xmppConnectionService.sendIqPacket(onboardingAccount, packet, (a, iq) -> {
                        Element command = iq.findChild("command", "http://jabber.org/protocol/commands");
                        if (command == null) {
                            Log.e(Config.LOGTAG, "Did not get expected data form from cheogram, got: " + iq);
                            return;
                        }

                        Element form = command.findChild("x", "jabber:x:data");
                        Data dataForm = form == null ? null : Data.parse(form);
                        if (dataForm == null || dataForm.getFieldByName("new-jid") == null) {
                            Log.e(Config.LOGTAG, "Did not get expected data form from cheogram, got: " + iq);
                            return;
                        }

                        dataForm.put("new-jid", newAccount.getJid().toEscapedString());
                        dataForm.submit();
                        command.setAttribute("action", "execute");
                        iq.setTo(iq.getFrom());
                        iq.setAttribute("type", "set");
                        iq.removeAttribute("from");
                        iq.removeAttribute("id");
                        xmppConnectionService.sendIqPacket(a, iq, (a2, iq2) -> {
                            Element command2 = iq2.findChild("command", "http://jabber.org/protocol/commands");
                            if (command2 != null && command2.getAttribute("status") != null && command2.getAttribute("status").equals("completed")) {
                                final IqPacket regPacket = new IqPacket(IqPacket.TYPE.SET);
                                regPacket.setTo(Jid.of("cheogram.com/CHEOGRAM%jabber:iq:register"));
                                final Element c2 = regPacket.addChild("command", Namespace.COMMANDS);
                                c2.setAttribute("node", "jabber:iq:register");
                                c2.setAttribute("action", "execute");
                                xmppConnectionService.sendIqPacket(newAccount, regPacket, (a3, iq3) -> {
                                    Element command3 = iq3.findChild("command", "http://jabber.org/protocol/commands");
                                    if (command3 == null) {
                                        Log.e(Config.LOGTAG, "Did not get expected data form from cheogram, got: " + iq3);
                                        return;
                                    }

                                    Element form3 = command3.findChild("x", "jabber:x:data");
                                    Data dataForm3 = form3 == null ? null : Data.parse(form3);
                                    if (dataForm3 == null || dataForm3.getFieldByName("confirm") == null) {
                                        Log.e(Config.LOGTAG, "Did not get expected data form from cheogram, got: " + iq3);
                                        return;
                                    }

                                    dataForm3.put("confirm", "true");
                                    dataForm3.submit();
                                    command3.setAttribute("action", "execute");
                                    iq3.setTo(iq3.getFrom());
                                    iq3.setAttribute("type", "set");
                                    iq3.removeAttribute("from");
                                    iq3.removeAttribute("id");
                                    xmppConnectionService.sendIqPacket(newAccount, iq3, (a4, iq4) -> {
                                        Element command4 = iq2.findChild("command", "http://jabber.org/protocol/commands");
                                        if (command4 != null && command4.getAttribute("status") != null && command4.getAttribute("status").equals("completed")) {
                                            xmppConnectionService.createContact(newAccount.getRoster().getContact(iq4.getFrom().asBareJid()), true);
                                            Conversation withCheogram = xmppConnectionService.findOrCreateConversation(newAccount, iq4.getFrom().asBareJid(), true, true, true);
                                            xmppConnectionService.markRead(withCheogram);
                                            xmppConnectionService.clearConversationHistory(withCheogram);
                                            xmppConnectionService.deleteAccount(onboardAccount);
                                        } else {
                                            Log.e(Config.LOGTAG, "Error confirming jid switch, got: " + iq4);
                                        }
                                    });
                                });
                            } else {
                                Log.e(Config.LOGTAG, "Error during jid switch, got: " + iq2);
                            }
                        });
                    });
                } else {
                    startCommand(selectedAccount, Jid.of("cheogram.com/CHEOGRAM%jabber:iq:register"), "jabber:iq:register");
                    finish();
                    return;
                }
            }
        }



@@ 1119,7 1208,7 @@ public class StartConversationActivity extends XmppActivity implements XmppConne

        final boolean sopranicaDeleted = getPreferences().getBoolean("cheogram_sopranica_bookmark_deleted", false);

        if (!sopranicaDeleted && !foundSopranica && (needle == null || needle.equals(""))) {
        if (!sopranicaDeleted && !foundSopranica && (needle == null || needle.equals("")) && xmppConnectionService.getAccounts().size() > 0) {
            Bookmark bookmark = new Bookmark(
                xmppConnectionService.getAccounts().get(0),
                Jid.of("discuss@conference.soprani.ca")

M src/main/java/eu/siacs/conversations/xmpp/forms/Option.java => src/main/java/eu/siacs/conversations/xmpp/forms/Option.java +1 -1
@@ 14,7 14,7 @@ public class Option {
    public static List<Option> forField(Element field) {
        List<Option> options = new ArrayList<>();
        for (Element el : field.getChildren()) {
            if (!el.getNamespace().equals("jabber:x:data")) continue;
            if (el.getNamespace() == null || !el.getNamespace().equals("jabber:x:data")) continue;
            if (!el.getName().equals("option")) continue;
            options.add(new Option(el));
        }