Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/iNPUTmice/Conversations.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/src/main
diff options
context:
space:
mode:
authorDaniel Gultsch <daniel@gultsch.de>2020-12-01 22:31:30 +0300
committerDaniel Gultsch <daniel@gultsch.de>2020-12-01 22:31:30 +0300
commit1f392a688d1439fdf163cc767e3630d566726d83 (patch)
treee78f1903e43e3590ef6cd20db989cac21a3068cd /src/main
parent92083fec83d6dda988ae98f012308db62fe7bcdc (diff)
initial (untested) support for easy onboarding invites
Diffstat (limited to 'src/main')
-rw-r--r--src/main/java/eu/siacs/conversations/services/XmppConnectionService.java38
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ConversationFragment.java2
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ConversationsActivity.java1
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ConversationsOverviewFragment.java31
-rw-r--r--src/main/java/eu/siacs/conversations/utils/EasyOnboardingInvite.java94
-rw-r--r--src/main/java/eu/siacs/conversations/xml/Namespace.java1
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java52
-rw-r--r--src/main/res/menu/fragment_conversations_overview.xml4
-rw-r--r--src/main/res/values/strings.xml3
9 files changed, 225 insertions, 1 deletions
diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
index 3225be67d..9adef0309 100644
--- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
+++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
@@ -120,6 +120,7 @@ import eu.siacs.conversations.ui.interfaces.OnSearchResultsAvailable;
import eu.siacs.conversations.utils.Compatibility;
import eu.siacs.conversations.utils.ConversationsFileObserver;
import eu.siacs.conversations.utils.CryptoHelper;
+import eu.siacs.conversations.utils.EasyOnboardingInvite;
import eu.siacs.conversations.utils.ExceptionHelper;
import eu.siacs.conversations.utils.MimeUtils;
import eu.siacs.conversations.utils.PhoneHelper;
@@ -1619,6 +1620,43 @@ public class XmppConnectionService extends Service {
sendMessage(message, true, delay);
}
+ 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);
+ if (jid == null) {
+ callback.inviteRequestFailed(getString(R.string.server_does_not_support_easy_onboarding_invites));
+ return;
+ }
+ final IqPacket request = new IqPacket(IqPacket.TYPE.SET);
+ request.setTo(jid);
+ final Element command = request.addChild("command", Namespace.COMMANDS);
+ command.setAttribute("node", Namespace.COMMANDS);
+ command.setAttribute("action", "execute");
+ sendIqPacket(account, request, (a, response) -> {
+ if (response.getType() == IqPacket.TYPE.RESULT) {
+ final Element resultCommand = response.findChild("command", Namespace.COMMANDS);
+ final Element x = resultCommand == null ? null : resultCommand.findChild("x", Namespace.DATA);
+ if (x != null) {
+ final Data data = Data.parse(x);
+ final String uri = data.getValue("uri");
+ final String landingUrl = data.getValue("landing-url");
+ if (uri != null) {
+ final EasyOnboardingInvite invite = new EasyOnboardingInvite(jid.getDomain().toEscapedString(), uri, landingUrl);
+ callback.inviteRequested(invite);
+ return;
+ }
+ }
+ callback.inviteRequestFailed(getString(R.string.unable_to_parse_invite));
+ Log.d(Config.LOGTAG, response.toString());
+ } else if (response.getType() == IqPacket.TYPE.ERROR) {
+ callback.inviteRequestFailed(IqParser.extractErrorMessage(response));
+ } else {
+ callback.inviteRequestFailed(getString(R.string.remote_server_timeout));
+ }
+ });
+
+ }
+
public void fetchRosterFromServer(final Account account) {
final IqPacket iqPacket = new IqPacket(IqPacket.TYPE.GET);
if (!"".equals(account.getRosterVersion())) {
diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
index f0c3bc181..07e35a50e 100644
--- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
+++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
@@ -2117,6 +2117,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
this.binding.textinput.setKeyboardListener(this);
messageListAdapter.updatePreferences();
refresh(false);
+ activity.invalidateOptionsMenu();
this.conversation.messagesLoaded.set(true);
Log.d(Config.LOGTAG, "scrolledToBottomAndNoPending=" + Boolean.toString(scrolledToBottomAndNoPending));
@@ -2397,7 +2398,6 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
}
updateSendButton();
updateEditablity();
- activity.invalidateOptionsMenu();
}
}
}
diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationsActivity.java b/src/main/java/eu/siacs/conversations/ui/ConversationsActivity.java
index 049c0f27e..026a0cafa 100644
--- a/src/main/java/eu/siacs/conversations/ui/ConversationsActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/ConversationsActivity.java
@@ -131,6 +131,7 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
@Override
protected void refreshUiReal() {
+ invalidateOptionsMenu();
for (@IdRes int id : FRAGMENT_ID_NOTIFICATION_ORDER) {
refreshFragment(id);
}
diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationsOverviewFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationsOverviewFragment.java
index b76caaec7..ae81874e1 100644
--- a/src/main/java/eu/siacs/conversations/ui/ConversationsOverviewFragment.java
+++ b/src/main/java/eu/siacs/conversations/ui/ConversationsOverviewFragment.java
@@ -30,6 +30,7 @@
package eu.siacs.conversations.ui;
import android.app.Activity;
+import android.app.AlertDialog;
import android.app.Fragment;
import android.content.Intent;
import android.databinding.DataBindingUtil;
@@ -48,12 +49,16 @@ import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
+import com.google.common.collect.Collections2;
+
import java.util.ArrayList;
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.FragmentConversationsOverviewBinding;
+import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Conversational;
import eu.siacs.conversations.ui.adapter.ConversationAdapter;
@@ -65,6 +70,7 @@ import eu.siacs.conversations.ui.util.PendingItem;
import eu.siacs.conversations.ui.util.ScrollState;
import eu.siacs.conversations.ui.util.StyledAttributes;
import eu.siacs.conversations.utils.AccountUtils;
+import eu.siacs.conversations.utils.EasyOnboardingInvite;
import eu.siacs.conversations.utils.ThemeHelper;
import static android.support.v7.widget.helper.ItemTouchHelper.LEFT;
@@ -300,6 +306,8 @@ public class ConversationsOverviewFragment extends XmppFragment {
public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
menuInflater.inflate(R.menu.fragment_conversations_overview, menu);
AccountUtils.showHideMenuItems(menu);
+ final MenuItem easyOnboardInvite = menu.findItem(R.id.action_easy_invite);
+ easyOnboardInvite.setVisible(EasyOnboardingInvite.anyHasSupport(activity == null ? null : activity.xmppConnectionService));
}
@Override
@@ -354,10 +362,33 @@ public class ConversationsOverviewFragment extends XmppFragment {
case R.id.action_search:
startActivity(new Intent(getActivity(), SearchActivity.class));
return true;
+ case R.id.action_easy_invite:
+ selectAccountToStartEasyInvite();
+ return true;
}
return super.onOptionsItemSelected(item);
}
+ private void selectAccountToStartEasyInvite() {
+ final List<Account> accounts = EasyOnboardingInvite.getSupportingAccounts(activity.xmppConnectionService);
+ if (accounts.size() == 1) {
+ openEasyInviteScreen(accounts.get(0));
+ } else {
+ final AtomicReference<Account> selectedAccount = new AtomicReference<>(accounts.get(0));
+ final AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(activity);
+ alertDialogBuilder.setTitle(R.string.choose_account);
+ final String[] asStrings = Collections2.transform(accounts, a -> a.getJid().asBareJid().toEscapedString()).toArray(new String[0]);
+ alertDialogBuilder.setSingleChoiceItems(asStrings, 0, (dialog, which) -> selectedAccount.set(accounts.get(which)));
+ alertDialogBuilder.setNegativeButton(R.string.cancel, null);
+ alertDialogBuilder.setPositiveButton(R.string.ok, (dialog, which) -> openEasyInviteScreen(selectedAccount.get()));
+ alertDialogBuilder.create().show();
+ }
+ }
+
+ private void openEasyInviteScreen(final Account account) {
+ EasyOnboardingInviteActivity.launch(account, activity);
+ }
+
@Override
void refresh() {
if (this.binding == null || this.activity == null) {
diff --git a/src/main/java/eu/siacs/conversations/utils/EasyOnboardingInvite.java b/src/main/java/eu/siacs/conversations/utils/EasyOnboardingInvite.java
new file mode 100644
index 000000000..954e2c65a
--- /dev/null
+++ b/src/main/java/eu/siacs/conversations/utils/EasyOnboardingInvite.java
@@ -0,0 +1,94 @@
+package eu.siacs.conversations.utils;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.Collections;
+import java.util.List;
+
+import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.services.QuickConversationsService;
+import eu.siacs.conversations.services.XmppConnectionService;
+import eu.siacs.conversations.xmpp.XmppConnection;
+
+public class EasyOnboardingInvite implements Parcelable {
+
+ private String domain;
+ private String uri;
+ private String landingUrl;
+
+ protected EasyOnboardingInvite(Parcel in) {
+ domain = in.readString();
+ uri = in.readString();
+ landingUrl = in.readString();
+ }
+
+ public EasyOnboardingInvite(String domain, String uri, String landingUrl) {
+ this.domain = domain;
+ this.uri = uri;
+ this.landingUrl = landingUrl;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(domain);
+ dest.writeString(uri);
+ dest.writeString(landingUrl);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Creator<EasyOnboardingInvite> CREATOR = new Creator<EasyOnboardingInvite>() {
+ @Override
+ public EasyOnboardingInvite createFromParcel(Parcel in) {
+ return new EasyOnboardingInvite(in);
+ }
+
+ @Override
+ public EasyOnboardingInvite[] newArray(int size) {
+ return new EasyOnboardingInvite[size];
+ }
+ };
+
+ public static boolean anyHasSupport(final XmppConnectionService service) {
+ if (QuickConversationsService.isQuicksy()) {
+ return false;
+ }
+ return getSupportingAccounts(service).size() > 0;
+
+ }
+
+ public static List<Account> getSupportingAccounts(final XmppConnectionService service) {
+ final ImmutableList.Builder<Account> supportingAccountsBuilder = new ImmutableList.Builder<>();
+ final List<Account> accounts = service == null ? Collections.emptyList() : service.getAccounts();
+ for(Account account : accounts) {
+ final XmppConnection xmppConnection = account.getXmppConnection();
+ if (xmppConnection != null && xmppConnection.getFeatures().easyOnboardingInvites()) {
+ supportingAccountsBuilder.add(account);
+ }
+ }
+ return supportingAccountsBuilder.build();
+ }
+
+ public String getUri() {
+ return uri;
+ }
+
+ public String getLandingUrl() {
+ return landingUrl;
+ }
+
+ public String getDomain() {
+ return domain;
+ }
+
+ public interface OnInviteRequested {
+ void inviteRequested(EasyOnboardingInvite invite);
+ void inviteRequestFailed(String message);
+ }
+}
diff --git a/src/main/java/eu/siacs/conversations/xml/Namespace.java b/src/main/java/eu/siacs/conversations/xml/Namespace.java
index 31b3420dd..b65076016 100644
--- a/src/main/java/eu/siacs/conversations/xml/Namespace.java
+++ b/src/main/java/eu/siacs/conversations/xml/Namespace.java
@@ -52,4 +52,5 @@ public final class Namespace {
public static final String BOOKMARKS2_COMPAT = BOOKMARKS2 + "#compat";
public static final String INVITE = "urn:xmpp:invite";
public static final String PARS = "urn:xmpp:pars:0";
+ public static final String EASY_ONBOARDING_INVITE = "urn:xmpp:invite#invite";
}
diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
index 9929a9c81..3f699e64e 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
@@ -137,6 +137,7 @@ public class XmppConnection implements Runnable {
protected final Account account;
private final Features features = new Features(this);
private final HashMap<Jid, ServiceDiscoveryResult> disco = new HashMap<>();
+ private final HashMap<String, Jid> commands = new HashMap<>();
private final SparseArray<AbstractAcknowledgeableStanza> mStanzaQueue = new SparseArray<>();
private final Hashtable<String, Pair<IqPacket, OnIqPacketReceived>> packetCallbacks = new Hashtable<>();
private final Set<OnAdvancedStreamFeaturesLoaded> advancedStreamFeaturesLoadedListeners = new HashSet<>();
@@ -228,6 +229,12 @@ public class XmppConnection implements Runnable {
}
}
+ public Jid getJidForCommand(final String node) {
+ synchronized (this.commands) {
+ return this.commands.get(node);
+ }
+ }
+
public void prepareNewConnection() {
this.lastConnect = SystemClock.elapsedRealtime();
this.lastPingSent = SystemClock.elapsedRealtime();
@@ -1028,6 +1035,9 @@ public class XmppConnection implements Runnable {
synchronized (this.disco) {
disco.clear();
}
+ synchronized (this.commands) {
+ this.commands.clear();
+ }
}
private void sendBindRequest() {
@@ -1250,6 +1260,35 @@ public class XmppConnection implements Runnable {
});
}
+ private void discoverCommands() {
+ final IqPacket request = new IqPacket(IqPacket.TYPE.GET);
+ request.setTo(account.getDomain());
+ request.addChild("query", Namespace.DISCO_ITEMS).setAttribute("node", Namespace.COMMANDS);
+ sendIqPacket(request, (account, response) -> {
+ if (response.getType() == IqPacket.TYPE.RESULT) {
+ final Element query = response.findChild("query",Namespace.DISCO_ITEMS);
+ if (query == null) {
+ return;
+ }
+ final HashMap<String, Jid> commands = new HashMap<>();
+ for(final Element child : query.getChildren()) {
+ if ("item".equals(child.getName())) {
+ final String node = child.getAttribute("node");
+ final Jid jid = child.getAttributeAsJid("jid");
+ if (node != null && jid != null) {
+ commands.put(node, jid);
+ }
+ }
+ }
+ Log.d(Config.LOGTAG,commands.toString());
+ synchronized (this.commands) {
+ this.commands.clear();
+ this.commands.putAll(commands);
+ }
+ }
+ });
+ }
+
public boolean isMamPreferenceAlways() {
return isMamPreferenceAlways;
}
@@ -1273,6 +1312,9 @@ public class XmppConnection implements Runnable {
if (getFeatures().carbons() && !features.carbonsEnabled) {
sendEnableCarbons();
}
+ if (getFeatures().commands()) {
+ discoverCommands();
+ }
}
private void sendServiceDiscoveryItems(final Jid server) {
@@ -1788,6 +1830,16 @@ public class XmppConnection implements Runnable {
return hasDiscoFeature(account.getDomain(), "urn:xmpp:carbons:2");
}
+ public boolean commands() {
+ return hasDiscoFeature(account.getDomain(), Namespace.COMMANDS);
+ }
+
+ public boolean easyOnboardingInvites() {
+ synchronized (commands) {
+ return commands.containsKey(Namespace.EASY_ONBOARDING_INVITE);
+ }
+ }
+
public boolean bookmarksConversion() {
return hasDiscoFeature(account.getJid().asBareJid(), Namespace.BOOKMARKS_CONVERSION) && pepPublishOptions();
}
diff --git a/src/main/res/menu/fragment_conversations_overview.xml b/src/main/res/menu/fragment_conversations_overview.xml
index 52937ebd1..38c83df8f 100644
--- a/src/main/res/menu/fragment_conversations_overview.xml
+++ b/src/main/res/menu/fragment_conversations_overview.xml
@@ -36,6 +36,10 @@
android:visible="@bool/show_individual_search_options"
app:showAsAction="never" />
<item
+ android:id="@+id/action_easy_invite"
+ android:orderInCategory="89"
+ android:title="@string/invite_to_app" />
+ <item
android:id="@+id/action_accounts"
android:orderInCategory="90"
android:title="@string/action_accounts"
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
index f40317ea8..a9670533f 100644
--- a/src/main/res/values/strings.xml
+++ b/src/main/res/values/strings.xml
@@ -950,4 +950,7 @@
<string name="failed_deliveries">Failed deliveries</string>
<string name="more_options">More options</string>
<string name="no_application_found">No application found</string>
+ <string name="invite_to_app">Invite to Conversations</string>
+ <string name="unable_to_parse_invite">Unable to parse invite</string>
+ <string name="server_does_not_support_easy_onboarding_invites">Server does not support generating invites</string>
</resources>