initial (untested) support for easy onboarding invites
This commit is contained in:
parent
92083fec83
commit
1f392a688d
|
@ -20,6 +20,10 @@
|
||||||
android:name=".ui.MagicCreateActivity"
|
android:name=".ui.MagicCreateActivity"
|
||||||
android:label="@string/create_new_account"
|
android:label="@string/create_new_account"
|
||||||
android:launchMode="singleTask" />
|
android:launchMode="singleTask" />
|
||||||
|
<activity
|
||||||
|
android:name=".ui.EasyOnboardingInviteActivity"
|
||||||
|
android:label="@string/invite_to_app"
|
||||||
|
android:launchMode="singleTask" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.ImportBackupActivity"
|
android:name=".ui.ImportBackupActivity"
|
||||||
android:label="@string/restore_backup"
|
android:label="@string/restore_backup"
|
||||||
|
|
|
@ -0,0 +1,157 @@
|
||||||
|
package eu.siacs.conversations.ui;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.databinding.DataBindingUtil;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.Point;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.v7.widget.Toolbar;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.google.common.base.Strings;
|
||||||
|
|
||||||
|
import eu.siacs.conversations.Config;
|
||||||
|
import eu.siacs.conversations.R;
|
||||||
|
import eu.siacs.conversations.databinding.ActivityEasyInviteBinding;
|
||||||
|
import eu.siacs.conversations.entities.Account;
|
||||||
|
import eu.siacs.conversations.services.BarcodeProvider;
|
||||||
|
import eu.siacs.conversations.utils.EasyOnboardingInvite;
|
||||||
|
import eu.siacs.conversations.xmpp.Jid;
|
||||||
|
|
||||||
|
public class EasyOnboardingInviteActivity extends XmppActivity implements EasyOnboardingInvite.OnInviteRequested {
|
||||||
|
|
||||||
|
private ActivityEasyInviteBinding binding;
|
||||||
|
|
||||||
|
private EasyOnboardingInvite easyOnboardingInvite;
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(final Bundle bundle) {
|
||||||
|
super.onCreate(bundle);
|
||||||
|
this.binding = DataBindingUtil.setContentView(this, R.layout.activity_easy_invite);
|
||||||
|
setSupportActionBar((Toolbar) binding.toolbar);
|
||||||
|
configureActionBar(getSupportActionBar(), true);
|
||||||
|
this.binding.shareButton.setOnClickListener(v -> share());
|
||||||
|
if (bundle != null && bundle.containsKey("invite")) {
|
||||||
|
this.easyOnboardingInvite = bundle.getParcelable("invite");
|
||||||
|
if (this.easyOnboardingInvite != null) {
|
||||||
|
showInvite(this.easyOnboardingInvite);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.showLoading();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
|
getMenuInflater().inflate(R.menu.easy_onboarding_invite, menu);
|
||||||
|
final MenuItem share = menu.findItem(R.id.action_share);
|
||||||
|
share.setVisible(easyOnboardingInvite != null);
|
||||||
|
return super.onCreateOptionsMenu(menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onOptionsItemSelected(MenuItem menuItem) {
|
||||||
|
if (menuItem.getItemId() == R.id.action_share) {
|
||||||
|
share();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return super.onOptionsItemSelected(menuItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void share() {
|
||||||
|
final String shareText = getString(
|
||||||
|
R.string.easy_invite_share_text,
|
||||||
|
easyOnboardingInvite.getDomain(),
|
||||||
|
easyOnboardingInvite.getLandingUrl()
|
||||||
|
);
|
||||||
|
final Intent sendIntent = new Intent();
|
||||||
|
sendIntent.setAction(Intent.ACTION_SEND);
|
||||||
|
sendIntent.putExtra(Intent.EXTRA_TEXT, shareText);
|
||||||
|
sendIntent.setType("text/plain");
|
||||||
|
startActivity(Intent.createChooser(sendIntent, getString(R.string.share_invite_with)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void refreshUiReal() {
|
||||||
|
invalidateOptionsMenu();
|
||||||
|
if (easyOnboardingInvite != null) {
|
||||||
|
showInvite(easyOnboardingInvite);
|
||||||
|
} else {
|
||||||
|
showLoading();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showLoading() {
|
||||||
|
this.binding.inProgress.setVisibility(View.VISIBLE);
|
||||||
|
this.binding.invite.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showInvite(final EasyOnboardingInvite invite) {
|
||||||
|
this.binding.inProgress.setVisibility(View.GONE);
|
||||||
|
this.binding.invite.setVisibility(View.VISIBLE);
|
||||||
|
this.binding.tapToShare.setText(getString(R.string.tap_share_button_send_invite, invite.getDomain()));
|
||||||
|
final Point size = new Point();
|
||||||
|
getWindowManager().getDefaultDisplay().getSize(size);
|
||||||
|
final int width = Math.min(size.x, size.y);
|
||||||
|
final String content;
|
||||||
|
if (Strings.isNullOrEmpty(invite.getLandingUrl())) {
|
||||||
|
content = invite.getUri();
|
||||||
|
} else {
|
||||||
|
content = invite.getLandingUrl();
|
||||||
|
}
|
||||||
|
final Bitmap bitmap = BarcodeProvider.create2dBarcodeBitmap(content, width);
|
||||||
|
binding.qrCode.setImageBitmap(bitmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSaveInstanceState(Bundle bundle) {
|
||||||
|
super.onSaveInstanceState(bundle);
|
||||||
|
if (easyOnboardingInvite != null) {
|
||||||
|
bundle.putParcelable("invite", easyOnboardingInvite);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void onBackendConnected() {
|
||||||
|
if (easyOnboardingInvite != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final Intent launchIntent = getIntent();
|
||||||
|
final String accountExtra = launchIntent.getStringExtra(EXTRA_ACCOUNT);
|
||||||
|
final Jid jid = accountExtra == null ? null : Jid.ofEscaped(accountExtra);
|
||||||
|
if (jid == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final Account account = xmppConnectionService.findAccountByJid(jid);
|
||||||
|
xmppConnectionService.requestEasyOnboardingInvite(account, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void launch(final Account account, final Activity context) {
|
||||||
|
final Intent intent = new Intent(context, EasyOnboardingInviteActivity.class);
|
||||||
|
intent.putExtra(EXTRA_ACCOUNT, account.getJid().asBareJid().toEscapedString());
|
||||||
|
context.startActivity(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void inviteRequested(EasyOnboardingInvite invite) {
|
||||||
|
this.easyOnboardingInvite = invite;
|
||||||
|
Log.d(Config.LOGTAG, "invite requested");
|
||||||
|
refreshUi();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void inviteRequestFailed(final String message) {
|
||||||
|
runOnUiThread(() -> {
|
||||||
|
if (!Strings.isNullOrEmpty(message)) {
|
||||||
|
Toast.makeText(this, message, Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
finish();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
83
src/conversations/res/layout/activity_easy_invite.xml
Normal file
83
src/conversations/res/layout/activity_easy_invite.xml
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="fill_parent"
|
||||||
|
android:background="?attr/color_background_primary"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
layout="@layout/toolbar" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/in_progress"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center"
|
||||||
|
android:visibility="gone">
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:id="@+id/invite"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginLeft="@dimen/activity_horizontal_margin"
|
||||||
|
android:layout_marginTop="@dimen/activity_vertical_margin"
|
||||||
|
android:layout_marginRight="@dimen/activity_horizontal_margin"
|
||||||
|
android:layout_marginBottom="@dimen/activity_vertical_margin"
|
||||||
|
android:visibility="visible">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tap_to_share"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/tap_share_button_send_invite"
|
||||||
|
android:textAppearance="@style/TextAppearance.Conversations.Body1" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/scan_the_code"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@+id/tap_to_share"
|
||||||
|
android:layout_marginTop="24sp"
|
||||||
|
android:text="@string/if_contact_is_nearby_use_qr"
|
||||||
|
android:textAppearance="@style/TextAppearance.Conversations.Body1" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/qr_code"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_above="@+id/share_button"
|
||||||
|
android:layout_below="@id/scan_the_code"
|
||||||
|
android:layout_alignParentStart="true"
|
||||||
|
android:layout_alignParentRight="true"
|
||||||
|
android:layout_centerHorizontal="true"
|
||||||
|
android:layout_margin="24sp"
|
||||||
|
android:scaleType="fitCenter" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/share_button"
|
||||||
|
style="@style/Widget.Conversations.Button.Borderless"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentBottom="true"
|
||||||
|
android:minWidth="0dp"
|
||||||
|
android:paddingLeft="16dp"
|
||||||
|
android:paddingRight="16dp"
|
||||||
|
android:text="@string/share"
|
||||||
|
android:layout_centerHorizontal="true"
|
||||||
|
android:textColor="?attr/colorAccent" />
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</layout>
|
10
src/conversations/res/menu/easy_onboarding_invite.xml
Normal file
10
src/conversations/res/menu/easy_onboarding_invite.xml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_share"
|
||||||
|
android:icon="?attr/icon_share"
|
||||||
|
android:title="@string/invite"
|
||||||
|
app:showAsAction="always" />
|
||||||
|
</menu>
|
|
@ -9,4 +9,8 @@
|
||||||
<string name="magic_create_text_fixed">You have been invited to %1$s. A username has already been picked for you. We will guide you through the process of creating an account.\nYou will be able to communicate with users of other providers by giving them your full XMPP address.</string>
|
<string name="magic_create_text_fixed">You have been invited to %1$s. A username has already been picked for you. We will guide you through the process of creating an account.\nYou will be able to communicate with users of other providers by giving them your full XMPP address.</string>
|
||||||
<string name="your_server_invitation">Your server invitation</string>
|
<string name="your_server_invitation">Your server invitation</string>
|
||||||
<string name="improperly_formatted_provisioning">Improperly formatted provisioning code</string>
|
<string name="improperly_formatted_provisioning">Improperly formatted provisioning code</string>
|
||||||
|
<string name="tap_share_button_send_invite">Tap the share button to send your contact an invitation to %1$s.</string>
|
||||||
|
<string name="if_contact_is_nearby_use_qr">If your contact is nearby, they can also scan the code below to accept your invitation.</string>
|
||||||
|
<string name="easy_invite_share_text">Join %1$s and chat with me: %2$s</string>
|
||||||
|
<string name="share_invite_with">Share invite with…</string>
|
||||||
</resources>
|
</resources>
|
|
@ -120,6 +120,7 @@ import eu.siacs.conversations.ui.interfaces.OnSearchResultsAvailable;
|
||||||
import eu.siacs.conversations.utils.Compatibility;
|
import eu.siacs.conversations.utils.Compatibility;
|
||||||
import eu.siacs.conversations.utils.ConversationsFileObserver;
|
import eu.siacs.conversations.utils.ConversationsFileObserver;
|
||||||
import eu.siacs.conversations.utils.CryptoHelper;
|
import eu.siacs.conversations.utils.CryptoHelper;
|
||||||
|
import eu.siacs.conversations.utils.EasyOnboardingInvite;
|
||||||
import eu.siacs.conversations.utils.ExceptionHelper;
|
import eu.siacs.conversations.utils.ExceptionHelper;
|
||||||
import eu.siacs.conversations.utils.MimeUtils;
|
import eu.siacs.conversations.utils.MimeUtils;
|
||||||
import eu.siacs.conversations.utils.PhoneHelper;
|
import eu.siacs.conversations.utils.PhoneHelper;
|
||||||
|
@ -1619,6 +1620,43 @@ public class XmppConnectionService extends Service {
|
||||||
sendMessage(message, true, delay);
|
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) {
|
public void fetchRosterFromServer(final Account account) {
|
||||||
final IqPacket iqPacket = new IqPacket(IqPacket.TYPE.GET);
|
final IqPacket iqPacket = new IqPacket(IqPacket.TYPE.GET);
|
||||||
if (!"".equals(account.getRosterVersion())) {
|
if (!"".equals(account.getRosterVersion())) {
|
||||||
|
|
|
@ -2117,6 +2117,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
|
||||||
this.binding.textinput.setKeyboardListener(this);
|
this.binding.textinput.setKeyboardListener(this);
|
||||||
messageListAdapter.updatePreferences();
|
messageListAdapter.updatePreferences();
|
||||||
refresh(false);
|
refresh(false);
|
||||||
|
activity.invalidateOptionsMenu();
|
||||||
this.conversation.messagesLoaded.set(true);
|
this.conversation.messagesLoaded.set(true);
|
||||||
Log.d(Config.LOGTAG, "scrolledToBottomAndNoPending=" + Boolean.toString(scrolledToBottomAndNoPending));
|
Log.d(Config.LOGTAG, "scrolledToBottomAndNoPending=" + Boolean.toString(scrolledToBottomAndNoPending));
|
||||||
|
|
||||||
|
@ -2397,7 +2398,6 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
|
||||||
}
|
}
|
||||||
updateSendButton();
|
updateSendButton();
|
||||||
updateEditablity();
|
updateEditablity();
|
||||||
activity.invalidateOptionsMenu();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -131,6 +131,7 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void refreshUiReal() {
|
protected void refreshUiReal() {
|
||||||
|
invalidateOptionsMenu();
|
||||||
for (@IdRes int id : FRAGMENT_ID_NOTIFICATION_ORDER) {
|
for (@IdRes int id : FRAGMENT_ID_NOTIFICATION_ORDER) {
|
||||||
refreshFragment(id);
|
refreshFragment(id);
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
package eu.siacs.conversations.ui;
|
package eu.siacs.conversations.ui;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
import android.app.AlertDialog;
|
||||||
import android.app.Fragment;
|
import android.app.Fragment;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.databinding.DataBindingUtil;
|
import android.databinding.DataBindingUtil;
|
||||||
|
@ -48,12 +49,16 @@ import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import com.google.common.collect.Collections2;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
import eu.siacs.conversations.Config;
|
import eu.siacs.conversations.Config;
|
||||||
import eu.siacs.conversations.R;
|
import eu.siacs.conversations.R;
|
||||||
import eu.siacs.conversations.databinding.FragmentConversationsOverviewBinding;
|
import eu.siacs.conversations.databinding.FragmentConversationsOverviewBinding;
|
||||||
|
import eu.siacs.conversations.entities.Account;
|
||||||
import eu.siacs.conversations.entities.Conversation;
|
import eu.siacs.conversations.entities.Conversation;
|
||||||
import eu.siacs.conversations.entities.Conversational;
|
import eu.siacs.conversations.entities.Conversational;
|
||||||
import eu.siacs.conversations.ui.adapter.ConversationAdapter;
|
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.ScrollState;
|
||||||
import eu.siacs.conversations.ui.util.StyledAttributes;
|
import eu.siacs.conversations.ui.util.StyledAttributes;
|
||||||
import eu.siacs.conversations.utils.AccountUtils;
|
import eu.siacs.conversations.utils.AccountUtils;
|
||||||
|
import eu.siacs.conversations.utils.EasyOnboardingInvite;
|
||||||
import eu.siacs.conversations.utils.ThemeHelper;
|
import eu.siacs.conversations.utils.ThemeHelper;
|
||||||
|
|
||||||
import static android.support.v7.widget.helper.ItemTouchHelper.LEFT;
|
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) {
|
public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
|
||||||
menuInflater.inflate(R.menu.fragment_conversations_overview, menu);
|
menuInflater.inflate(R.menu.fragment_conversations_overview, menu);
|
||||||
AccountUtils.showHideMenuItems(menu);
|
AccountUtils.showHideMenuItems(menu);
|
||||||
|
final MenuItem easyOnboardInvite = menu.findItem(R.id.action_easy_invite);
|
||||||
|
easyOnboardInvite.setVisible(EasyOnboardingInvite.anyHasSupport(activity == null ? null : activity.xmppConnectionService));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -354,10 +362,33 @@ public class ConversationsOverviewFragment extends XmppFragment {
|
||||||
case R.id.action_search:
|
case R.id.action_search:
|
||||||
startActivity(new Intent(getActivity(), SearchActivity.class));
|
startActivity(new Intent(getActivity(), SearchActivity.class));
|
||||||
return true;
|
return true;
|
||||||
|
case R.id.action_easy_invite:
|
||||||
|
selectAccountToStartEasyInvite();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return super.onOptionsItemSelected(item);
|
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
|
@Override
|
||||||
void refresh() {
|
void refresh() {
|
||||||
if (this.binding == null || this.activity == null) {
|
if (this.binding == null || this.activity == null) {
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -52,4 +52,5 @@ public final class Namespace {
|
||||||
public static final String BOOKMARKS2_COMPAT = BOOKMARKS2 + "#compat";
|
public static final String BOOKMARKS2_COMPAT = BOOKMARKS2 + "#compat";
|
||||||
public static final String INVITE = "urn:xmpp:invite";
|
public static final String INVITE = "urn:xmpp:invite";
|
||||||
public static final String PARS = "urn:xmpp:pars:0";
|
public static final String PARS = "urn:xmpp:pars:0";
|
||||||
|
public static final String EASY_ONBOARDING_INVITE = "urn:xmpp:invite#invite";
|
||||||
}
|
}
|
||||||
|
|
|
@ -137,6 +137,7 @@ public class XmppConnection implements Runnable {
|
||||||
protected final Account account;
|
protected final Account account;
|
||||||
private final Features features = new Features(this);
|
private final Features features = new Features(this);
|
||||||
private final HashMap<Jid, ServiceDiscoveryResult> disco = new HashMap<>();
|
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 SparseArray<AbstractAcknowledgeableStanza> mStanzaQueue = new SparseArray<>();
|
||||||
private final Hashtable<String, Pair<IqPacket, OnIqPacketReceived>> packetCallbacks = new Hashtable<>();
|
private final Hashtable<String, Pair<IqPacket, OnIqPacketReceived>> packetCallbacks = new Hashtable<>();
|
||||||
private final Set<OnAdvancedStreamFeaturesLoaded> advancedStreamFeaturesLoadedListeners = new HashSet<>();
|
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() {
|
public void prepareNewConnection() {
|
||||||
this.lastConnect = SystemClock.elapsedRealtime();
|
this.lastConnect = SystemClock.elapsedRealtime();
|
||||||
this.lastPingSent = SystemClock.elapsedRealtime();
|
this.lastPingSent = SystemClock.elapsedRealtime();
|
||||||
|
@ -1028,6 +1035,9 @@ public class XmppConnection implements Runnable {
|
||||||
synchronized (this.disco) {
|
synchronized (this.disco) {
|
||||||
disco.clear();
|
disco.clear();
|
||||||
}
|
}
|
||||||
|
synchronized (this.commands) {
|
||||||
|
this.commands.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendBindRequest() {
|
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() {
|
public boolean isMamPreferenceAlways() {
|
||||||
return isMamPreferenceAlways;
|
return isMamPreferenceAlways;
|
||||||
}
|
}
|
||||||
|
@ -1273,6 +1312,9 @@ public class XmppConnection implements Runnable {
|
||||||
if (getFeatures().carbons() && !features.carbonsEnabled) {
|
if (getFeatures().carbons() && !features.carbonsEnabled) {
|
||||||
sendEnableCarbons();
|
sendEnableCarbons();
|
||||||
}
|
}
|
||||||
|
if (getFeatures().commands()) {
|
||||||
|
discoverCommands();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendServiceDiscoveryItems(final Jid server) {
|
private void sendServiceDiscoveryItems(final Jid server) {
|
||||||
|
@ -1788,6 +1830,16 @@ public class XmppConnection implements Runnable {
|
||||||
return hasDiscoFeature(account.getDomain(), "urn:xmpp:carbons:2");
|
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() {
|
public boolean bookmarksConversion() {
|
||||||
return hasDiscoFeature(account.getJid().asBareJid(), Namespace.BOOKMARKS_CONVERSION) && pepPublishOptions();
|
return hasDiscoFeature(account.getJid().asBareJid(), Namespace.BOOKMARKS_CONVERSION) && pepPublishOptions();
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,10 @@
|
||||||
android:title="@string/search_messages"
|
android:title="@string/search_messages"
|
||||||
android:visible="@bool/show_individual_search_options"
|
android:visible="@bool/show_individual_search_options"
|
||||||
app:showAsAction="never" />
|
app:showAsAction="never" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_easy_invite"
|
||||||
|
android:orderInCategory="89"
|
||||||
|
android:title="@string/invite_to_app" />
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_accounts"
|
android:id="@+id/action_accounts"
|
||||||
android:orderInCategory="90"
|
android:orderInCategory="90"
|
||||||
|
|
|
@ -950,4 +950,7 @@
|
||||||
<string name="failed_deliveries">Failed deliveries</string>
|
<string name="failed_deliveries">Failed deliveries</string>
|
||||||
<string name="more_options">More options</string>
|
<string name="more_options">More options</string>
|
||||||
<string name="no_application_found">No application found</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>
|
</resources>
|
||||||
|
|
Loading…
Reference in a new issue