diff --git a/src/cheogram/java/eu/siacs/conversations/ui/WelcomeActivity.java b/src/cheogram/java/eu/siacs/conversations/ui/WelcomeActivity.java index 9f91e0b687c8299ba15b8ab8e9573e0d74337e2c..5c23520c596c8381b7e8c934b33bbc3bc2725901 100644 --- a/src/cheogram/java/eu/siacs/conversations/ui/WelcomeActivity.java +++ b/src/cheogram/java/eu/siacs/conversations/ui/WelcomeActivity.java @@ -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 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)) { diff --git a/src/main/java/eu/siacs/conversations/Config.java b/src/main/java/eu/siacs/conversations/Config.java index 681fdee3c7a3c9822aa3565d6c92b1a9f7215c66..73453583c237a0ad8b8c3e7b03fcb7a80f74c12d 100644 --- a/src/main/java/eu/siacs/conversations/Config.java +++ b/src/main/java/eu/siacs/conversations/Config.java @@ -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"; diff --git a/src/main/java/eu/siacs/conversations/entities/Conversation.java b/src/main/java/eu/siacs/conversations/entities/Conversation.java index c7ace79ffcc4c6b1e924022f743c2df500b93341..b09ff01fe7775abd573379780e61877efea918a3 100644 --- a/src/main/java/eu/siacs/conversations/entities/Conversation.java +++ b/src/main/java/eu/siacs/conversations/entities/Conversation.java @@ -797,7 +797,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl } } } else if ((QuickConversationsService.isConversations() || !Config.QUICKSY_DOMAIN.equals(contactJid.getDomain())) && isWithStranger()) { - return contactJid; + return contactJid.equals(Jid.of("cheogram.com")) ? "Cheogram" : contactJid; } else { return this.getContact().getDisplayName(); } @@ -2400,7 +2400,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl } public void updateWithResponse(final IqPacket iq) { - if (getView().isAttachedToWindow()) { + if (getView() != null && getView().isAttachedToWindow()) { getView().post(() -> updateWithResponseUiThread(iq)); } else { pendingResponsePacket = iq; @@ -2706,6 +2706,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl } public View getView() { + if (mBinding == null) return null; return mBinding.getRoot(); } @@ -2874,8 +2875,9 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl actionsAdapter.notifyDataSetChanged(); if (pendingResponsePacket != null) { - updateWithResponseUiThread(pendingResponsePacket); + final IqPacket pending = pendingResponsePacket; pendingResponsePacket = null; + updateWithResponseUiThread(pending); } } diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index c4e7e4f6fb8daab7af161d951843f7311f5f8bd1..5f802cd165771568a8832f89e90d4c4282a9061f 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -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); diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java index 264cca2bc2249b4d3c2491a9040e5020afa87f94..9ca8b274a04d0cfa486725d50765948ef1f71ea1 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java @@ -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); @@ -2997,6 +2999,7 @@ public class ConversationFragment extends XmppFragment } if (node != null && commandJid != null) { conversation.startCommand(commandFor(commandJid, node), activity.xmppConnectionService); + if (activity.xmppConnectionService.isOnboarding()) binding.tabLayout.setVisibility(View.GONE); } }); return; diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationsActivity.java b/src/main/java/eu/siacs/conversations/ui/ConversationsActivity.java index 5af53409fb5fbfba05b4e142f59fb3c9a92ef2f4..9c9a1e9a0c5f420f24920e015e7ce2b775faa84a 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationsActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationsActivity.java @@ -740,10 +740,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; } diff --git a/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java b/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java index 6c6a9b0ea46e88147ef1853025ccb33903e2d214..139b80b1c85cb63f68e2ab5e6345623859120618 100644 --- a/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java @@ -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 { @@ -918,8 +922,11 @@ public class StartConversationActivity extends XmppActivity implements XmppConne 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 +948,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; + } } }