forked from mirror/monocles_chat_clean
initial (untested) support for easy onboarding invites
(cherry picked from commit 1f392a688d1439fdf163cc767e3630d566726d83)
This commit is contained in:
parent
26c4c1bbc2
commit
74051c85a9
12 changed files with 479 additions and 1 deletions
|
@ -129,6 +129,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.Namespace;
|
||||
|
@ -1865,6 +1866,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())) {
|
||||
|
|
|
@ -2355,6 +2355,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);
|
||||
hasWriteAccessInMUC();
|
||||
Log.d(Config.LOGTAG, "scrolledToBottomAndNoPending=" + Boolean.toString(scrolledToBottomAndNoPending));
|
||||
|
@ -2693,7 +2694,6 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
|
|||
}
|
||||
updateSendButton();
|
||||
updateEditablity();
|
||||
activity.invalidateOptionsMenu();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -151,6 +151,7 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
|
|||
@Override
|
||||
protected void refreshUiReal() {
|
||||
invalidateActionBarTitle();
|
||||
invalidateOptionsMenu();
|
||||
for (@IdRes int id : FRAGMENT_ID_NOTIFICATION_ORDER) {
|
||||
refreshFragment(id);
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ package eu.siacs.conversations.ui;
|
|||
import android.animation.Animator;
|
||||
import android.animation.AnimatorInflater;
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Fragment;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
@ -45,13 +46,17 @@ import android.view.ViewGroup;
|
|||
|
||||
import androidx.databinding.DataBindingUtil;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
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.ui.adapter.ConversationAdapter;
|
||||
import eu.siacs.conversations.ui.interfaces.OnConversationSelected;
|
||||
|
@ -59,6 +64,7 @@ import eu.siacs.conversations.ui.util.PendingActionHelper;
|
|||
import eu.siacs.conversations.ui.util.PendingItem;
|
||||
import eu.siacs.conversations.ui.util.ScrollState;
|
||||
import eu.siacs.conversations.utils.MenuDoubleTabUtil;
|
||||
import eu.siacs.conversations.utils.EasyOnboardingInvite;
|
||||
|
||||
public class ConversationsOverviewFragment extends XmppFragment {
|
||||
|
||||
|
@ -179,6 +185,8 @@ public class ConversationsOverviewFragment extends XmppFragment {
|
|||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
|
||||
menuInflater.inflate(R.menu.fragment_conversations_overview, menu);
|
||||
final MenuItem easyOnboardInvite = menu.findItem(R.id.action_easy_invite);
|
||||
easyOnboardInvite.setVisible(EasyOnboardingInvite.anyHasSupport(activity == null ? null : activity.xmppConnectionService));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -234,8 +242,31 @@ public class ConversationsOverviewFragment extends XmppFragment {
|
|||
startActivity(new Intent(getActivity(), SearchActivity.class));
|
||||
activity.overridePendingTransition(R.animator.fade_in, R.animator.fade_out);
|
||||
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
|
||||
|
|
|
@ -0,0 +1,158 @@
|
|||
package eu.siacs.conversations.ui;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.databinding.DataBindingUtil;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Point;
|
||||
import android.os.Bundle;
|
||||
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();
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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 INVITE = "urn:xmpp:invite";
|
||||
public static final String PARS = "urn:xmpp:pars:0";
|
||||
public static final String EASY_ONBOARDING_INVITE = "urn:xmpp:invite#invite";
|
||||
}
|
||||
|
|
|
@ -145,6 +145,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<>();
|
||||
|
@ -240,6 +241,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();
|
||||
|
@ -1070,6 +1077,9 @@ public class XmppConnection implements Runnable {
|
|||
synchronized (this.disco) {
|
||||
disco.clear();
|
||||
}
|
||||
synchronized (this.commands) {
|
||||
this.commands.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private void sendBindRequest() {
|
||||
|
@ -1294,6 +1304,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;
|
||||
}
|
||||
|
@ -1317,6 +1356,9 @@ public class XmppConnection implements Runnable {
|
|||
if (getFeatures().carbons() && !features.carbonsEnabled) {
|
||||
sendEnableCarbons();
|
||||
}
|
||||
if (getFeatures().commands()) {
|
||||
discoverCommands();
|
||||
}
|
||||
}
|
||||
|
||||
private void sendServiceDiscoveryItems(final Jid server) {
|
||||
|
@ -1901,6 +1943,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();
|
||||
}
|
||||
|
|
83
src/main/res/layout/activity_easy_invite.xml
Normal file
83
src/main/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>
|
8
src/main/res/menu/easy_onboarding_invite.xml
Normal file
8
src/main/res/menu/easy_onboarding_invite.xml
Normal file
|
@ -0,0 +1,8 @@
|
|||
<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>
|
|
@ -36,4 +36,8 @@
|
|||
android:title="@string/search_messages"
|
||||
android:icon="?attr/icon_search"
|
||||
app:showAsAction="always" />
|
||||
<item
|
||||
android:id="@+id/action_easy_invite"
|
||||
android:orderInCategory="89"
|
||||
android:title="@string/invite_to_app" />
|
||||
</menu>
|
|
@ -1077,5 +1077,13 @@
|
|||
<string name="move_data">Move data</string>
|
||||
<string name="error_moving_data">An error occurred while moving your old data. Please move them manually.</string>
|
||||
<string name="data_successfully_moved">Your data has been moved successfully.</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>
|
||||
<string name="your_server">Your provider</string>
|
||||
<string name="tap_share_button_send_invite">Tap the share button to send your contact an invitation to %1$s.</string>
|
||||
<string name="invite">Invite</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>
|
||||
|
|
Loading…
Add table
Reference in a new issue