add account provisioning via QR code to welcome screen
This commit is contained in:
parent
68960398b2
commit
15489547b7
|
@ -0,0 +1,51 @@
|
|||
package eu.siacs.conversations.entities;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import eu.siacs.conversations.xmpp.Jid;
|
||||
|
||||
public class AccountConfiguration {
|
||||
|
||||
private static final Gson GSON = new GsonBuilder().create();
|
||||
|
||||
public Protocol protocol;
|
||||
public String address;
|
||||
public String password;
|
||||
|
||||
public Jid getJid() {
|
||||
return Jid.ofEscaped(address);
|
||||
}
|
||||
|
||||
public static AccountConfiguration parse(final String input) {
|
||||
final AccountConfiguration c;
|
||||
try {
|
||||
c = GSON.fromJson(input, AccountConfiguration.class);
|
||||
} catch (JsonSyntaxException e) {
|
||||
throw new IllegalArgumentException("Not a valid JSON string", e);
|
||||
}
|
||||
Preconditions.checkArgument(
|
||||
c.protocol == Protocol.XMPP,
|
||||
"Protocol must be XMPP"
|
||||
);
|
||||
Preconditions.checkArgument(
|
||||
c.address != null && c.getJid().isBareJid() && !c.getJid().isDomainJid(),
|
||||
"Invalid XMPP address"
|
||||
);
|
||||
Preconditions.checkArgument(
|
||||
c.password != null && c.password.length() > 0,
|
||||
"No password specified"
|
||||
);
|
||||
return c;
|
||||
}
|
||||
|
||||
public enum Protocol {
|
||||
@SerializedName("xmpp") XMPP,
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -26,6 +26,7 @@ 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.Compatibility;
|
||||
import eu.siacs.conversations.utils.InstallReferrerUtils;
|
||||
import eu.siacs.conversations.utils.SignupUtils;
|
||||
import eu.siacs.conversations.utils.XmppUri;
|
||||
|
@ -61,12 +62,12 @@ public class WelcomeActivity extends XmppActivity implements XmppConnectionServi
|
|||
if (!xmppUri.isValidJid()) {
|
||||
return;
|
||||
}
|
||||
final String preAuth = xmppUri.getParameter("preauth");
|
||||
final String preAuth = xmppUri.getParameter(XmppUri.PARAMETER_PRE_AUTH);
|
||||
final Jid jid = xmppUri.getJid();
|
||||
final Intent intent;
|
||||
if (xmppUri.isAction(XmppUri.ACTION_REGISTER)) {
|
||||
intent = SignupUtils.getTokenRegistrationIntent(this, jid, preAuth);
|
||||
} else if (xmppUri.isAction(XmppUri.ACTION_ROSTER) && "y".equals(xmppUri.getParameter("ibr"))) {
|
||||
} else if (xmppUri.isAction(XmppUri.ACTION_ROSTER) && "y".equals(xmppUri.getParameter(XmppUri.PARAMETER_IBR))) {
|
||||
intent = SignupUtils.getTokenRegistrationIntent(this, jid.getDomain(), preAuth);
|
||||
intent.putExtra(StartConversationActivity.EXTRA_INVITE_URI, xmppUri.toString());
|
||||
} else {
|
||||
|
@ -146,10 +147,12 @@ public class WelcomeActivity extends XmppActivity implements XmppConnectionServi
|
|||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
getMenuInflater().inflate(R.menu.welcome_menu, menu);
|
||||
final MenuItem scan = menu.findItem(R.id.action_scan_qr_code);
|
||||
scan.setVisible(getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA));
|
||||
scan.setVisible(Compatibility.hasFeatureCamera(this));
|
||||
return super.onCreateOptionsMenu(menu);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
|
@ -159,7 +162,7 @@ public class WelcomeActivity extends XmppActivity implements XmppConnectionServi
|
|||
}
|
||||
break;
|
||||
case R.id.action_scan_qr_code:
|
||||
UriHandlerActivity.scan(this);
|
||||
UriHandlerActivity.scan(this, true);
|
||||
break;
|
||||
case R.id.action_add_account_with_cert:
|
||||
addAccountFromKey();
|
||||
|
@ -186,7 +189,7 @@ public class WelcomeActivity extends XmppActivity implements XmppConnectionServi
|
|||
@Override
|
||||
public void onAccountCreated(final Account account) {
|
||||
final Intent intent = new Intent(this, EditAccountActivity.class);
|
||||
intent.putExtra("jid", account.getJid().asBareJid().toString());
|
||||
intent.putExtra("jid", account.getJid().asBareJid().toEscapedString());
|
||||
intent.putExtra("init", true);
|
||||
addInviteUri(intent);
|
||||
startActivity(intent);
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
package eu.siacs.conversations.utils;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import eu.siacs.conversations.R;
|
||||
import eu.siacs.conversations.entities.AccountConfiguration;
|
||||
import eu.siacs.conversations.persistance.DatabaseBackend;
|
||||
import eu.siacs.conversations.services.XmppConnectionService;
|
||||
import eu.siacs.conversations.ui.EditAccountActivity;
|
||||
import eu.siacs.conversations.xmpp.Jid;
|
||||
|
||||
public class ProvisioningUtils {
|
||||
|
||||
public static void provision(final Activity activity, final String json) {
|
||||
final AccountConfiguration accountConfiguration;
|
||||
try {
|
||||
accountConfiguration = AccountConfiguration.parse(json);
|
||||
} catch (final IllegalArgumentException e) {
|
||||
Toast.makeText(activity, R.string.improperly_formatted_provisioning, Toast.LENGTH_LONG).show();
|
||||
return;
|
||||
}
|
||||
final Jid jid = accountConfiguration.getJid();
|
||||
final List<Jid> accounts = DatabaseBackend.getInstance(activity).getAccountJids(true);
|
||||
if (accounts.contains(jid)) {
|
||||
Toast.makeText(activity, R.string.account_already_exists, Toast.LENGTH_LONG).show();
|
||||
return;
|
||||
}
|
||||
final Intent serviceIntent = new Intent(activity, XmppConnectionService.class);
|
||||
serviceIntent.setAction(XmppConnectionService.ACTION_PROVISION_ACCOUNT);
|
||||
serviceIntent.putExtra("address", jid.asBareJid().toEscapedString());
|
||||
serviceIntent.putExtra("password", accountConfiguration.password);
|
||||
Compatibility.startService(activity, serviceIntent);
|
||||
final Intent intent = new Intent(activity, EditAccountActivity.class);
|
||||
intent.putExtra("jid", jid.asBareJid().toEscapedString());
|
||||
intent.putExtra("init", true);
|
||||
activity.startActivity(intent);
|
||||
}
|
||||
|
||||
}
|
|
@ -8,4 +8,5 @@
|
|||
<string name="magic_create_text_on_x">You have been invited to %1$s. We will guide you through the process of creating an account.\nWhen picking %1$s as a provider you 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="improperly_formatted_provisioning">Improperly formatted provisioning code</string>
|
||||
</resources>
|
|
@ -170,6 +170,7 @@ public class XmppConnectionService extends Service {
|
|||
public static final String ACTION_FCM_MESSAGE_RECEIVED = "fcm_message_received";
|
||||
public static final String ACTION_DISMISS_CALL = "dismiss_call";
|
||||
public static final String ACTION_END_CALL = "end_call";
|
||||
public static final String ACTION_PROVISION_ACCOUNT = "provision_account";
|
||||
private static final String ACTION_POST_CONNECTIVITY_CHANGE = "eu.siacs.conversations.POST_CONNECTIVITY_CHANGE";
|
||||
|
||||
private static final String SETTING_LAST_ACTIVITY_TS = "last_activity_timestamp";
|
||||
|
@ -659,6 +660,15 @@ public class XmppConnectionService extends Service {
|
|||
mJingleConnectionManager.endRtpSession(sessionId);
|
||||
}
|
||||
break;
|
||||
case ACTION_PROVISION_ACCOUNT: {
|
||||
final String address = intent.getStringExtra("address");
|
||||
final String password = intent.getStringExtra("password");
|
||||
if (QuickConversationsService.isQuicksy() || Strings.isNullOrEmpty(address) || Strings.isNullOrEmpty(password)) {
|
||||
break;
|
||||
}
|
||||
provisionAccount(address, password);
|
||||
break;
|
||||
}
|
||||
case ACTION_DISMISS_ERROR_NOTIFICATIONS:
|
||||
dismissErrorNotifications();
|
||||
break;
|
||||
|
@ -2180,6 +2190,14 @@ public class XmppConnectionService extends Service {
|
|||
}
|
||||
}
|
||||
|
||||
private void provisionAccount(final String address, final String password) {
|
||||
final Jid jid = Jid.ofEscaped(address);
|
||||
final Account account = new Account(jid, password);
|
||||
account.setOption(Account.OPTION_DISABLED, true);
|
||||
Log.d(Config.LOGTAG,jid.asBareJid().toEscapedString()+": provisioning account");
|
||||
createAccount(account);
|
||||
}
|
||||
|
||||
public void createAccountFromKey(final String alias, final OnAccountCreated callback) {
|
||||
new Thread(() -> {
|
||||
try {
|
||||
|
|
|
@ -11,12 +11,16 @@ import android.support.v4.content.ContextCompat;
|
|||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import eu.siacs.conversations.R;
|
||||
import eu.siacs.conversations.persistance.DatabaseBackend;
|
||||
import eu.siacs.conversations.services.QuickConversationsService;
|
||||
import eu.siacs.conversations.utils.ProvisioningUtils;
|
||||
import eu.siacs.conversations.utils.SignupUtils;
|
||||
import eu.siacs.conversations.utils.XmppUri;
|
||||
import eu.siacs.conversations.xmpp.Jid;
|
||||
|
@ -24,29 +28,45 @@ import eu.siacs.conversations.xmpp.Jid;
|
|||
public class UriHandlerActivity extends AppCompatActivity {
|
||||
|
||||
public static final String ACTION_SCAN_QR_CODE = "scan_qr_code";
|
||||
private static final String EXTRA_ALLOW_PROVISIONING = "extra_allow_provisioning";
|
||||
private static final int REQUEST_SCAN_QR_CODE = 0x1234;
|
||||
private static final int REQUEST_CAMERA_PERMISSIONS_TO_SCAN = 0x6789;
|
||||
private static final Pattern VCARD_XMPP_PATTERN = Pattern.compile("\nIMPP([^:]*):(xmpp:.+)\n");
|
||||
private static final int REQUEST_CAMERA_PERMISSIONS_TO_SCAN_AND_PROVISION = 0x6790;
|
||||
private static final Pattern V_CARD_XMPP_PATTERN = Pattern.compile("\nIMPP([^:]*):(xmpp:.+)\n");
|
||||
private boolean handled = false;
|
||||
|
||||
public static void scan(Activity activity) {
|
||||
public static void scan(final Activity activity) {
|
||||
scan(activity, false);
|
||||
}
|
||||
|
||||
public static void scan(final Activity activity, final boolean provisioning) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M || ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
|
||||
Intent intent = new Intent(activity, UriHandlerActivity.class);
|
||||
final Intent intent = new Intent(activity, UriHandlerActivity.class);
|
||||
intent.setAction(UriHandlerActivity.ACTION_SCAN_QR_CODE);
|
||||
if (provisioning) {
|
||||
intent.putExtra(EXTRA_ALLOW_PROVISIONING, true);
|
||||
}
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
|
||||
activity.startActivity(intent);
|
||||
} else {
|
||||
activity.requestPermissions(new String[]{Manifest.permission.CAMERA}, REQUEST_CAMERA_PERMISSIONS_TO_SCAN);
|
||||
activity.requestPermissions(
|
||||
new String[]{Manifest.permission.CAMERA},
|
||||
provisioning ? REQUEST_CAMERA_PERMISSIONS_TO_SCAN_AND_PROVISION : REQUEST_CAMERA_PERMISSIONS_TO_SCAN
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public static void onRequestPermissionResult(Activity activity, int requestCode, int[] grantResults) {
|
||||
if (requestCode != REQUEST_CAMERA_PERMISSIONS_TO_SCAN) {
|
||||
if (requestCode != REQUEST_CAMERA_PERMISSIONS_TO_SCAN && requestCode != REQUEST_CAMERA_PERMISSIONS_TO_SCAN_AND_PROVISION) {
|
||||
return;
|
||||
}
|
||||
if (grantResults.length > 0) {
|
||||
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
scan(activity);
|
||||
if (requestCode == REQUEST_CAMERA_PERMISSIONS_TO_SCAN_AND_PROVISION) {
|
||||
scan(activity, true);
|
||||
} else {
|
||||
scan(activity);
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(activity, R.string.qr_code_scanner_needs_access_to_camera, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
@ -88,19 +108,19 @@ public class UriHandlerActivity extends AppCompatActivity {
|
|||
final List<Jid> accounts = DatabaseBackend.getInstance(this).getAccountJids(true);
|
||||
|
||||
if (SignupUtils.isSupportTokenRegistry() && xmppUri.isValidJid()) {
|
||||
final String preauth = xmppUri.getParameter("preauth");
|
||||
final String preAuth = xmppUri.getParameter(XmppUri.PARAMETER_PRE_AUTH);
|
||||
final Jid jid = xmppUri.getJid();
|
||||
if (xmppUri.isAction(XmppUri.ACTION_REGISTER)) {
|
||||
if (jid.getEscapedLocal() != null && accounts.contains(jid.asBareJid())) {
|
||||
Toast.makeText(this, R.string.account_already_exists, Toast.LENGTH_LONG).show();
|
||||
return;
|
||||
}
|
||||
intent = SignupUtils.getTokenRegistrationIntent(this, jid, preauth);
|
||||
intent = SignupUtils.getTokenRegistrationIntent(this, jid, preAuth);
|
||||
startActivity(intent);
|
||||
return;
|
||||
}
|
||||
if (xmppUri.isAction(XmppUri.ACTION_ROSTER) && "y".equals(xmppUri.getParameter("ibr"))) {
|
||||
intent = SignupUtils.getTokenRegistrationIntent(this, jid.getDomain(), preauth);
|
||||
if (xmppUri.isAction(XmppUri.ACTION_ROSTER) && "y".equals(xmppUri.getParameter(XmppUri.PARAMETER_IBR))) {
|
||||
intent = SignupUtils.getTokenRegistrationIntent(this, jid.getDomain(), preAuth);
|
||||
intent.putExtra(StartConversationActivity.EXTRA_INVITE_URI, xmppUri.toString());
|
||||
startActivity(intent);
|
||||
return;
|
||||
|
@ -194,22 +214,38 @@ public class UriHandlerActivity extends AppCompatActivity {
|
|||
finish();
|
||||
}
|
||||
|
||||
private boolean allowProvisioning() {
|
||||
final Intent launchIntent = getIntent();
|
||||
return launchIntent != null && launchIntent.getBooleanExtra(EXTRA_ALLOW_PROVISIONING, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
|
||||
public void onActivityResult(final int requestCode, final int resultCode, final Intent intent) {
|
||||
super.onActivityResult(requestCode, requestCode, intent);
|
||||
if (requestCode == REQUEST_SCAN_QR_CODE && resultCode == RESULT_OK) {
|
||||
String result = intent.getStringExtra(ScanActivity.INTENT_EXTRA_RESULT);
|
||||
if (result != null) {
|
||||
if (result.startsWith("BEGIN:VCARD\n")) {
|
||||
Matcher matcher = VCARD_XMPP_PATTERN.matcher(result);
|
||||
if (matcher.find()) {
|
||||
result = matcher.group(2);
|
||||
}
|
||||
}
|
||||
Uri uri = Uri.parse(result);
|
||||
handleUri(uri, true);
|
||||
final String result = intent.getStringExtra(ScanActivity.INTENT_EXTRA_RESULT);
|
||||
if (Strings.isNullOrEmpty(result)) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
if (result.startsWith("BEGIN:VCARD\n")) {
|
||||
final Matcher matcher = V_CARD_XMPP_PATTERN.matcher(result);
|
||||
if (matcher.find()) {
|
||||
handleUri(Uri.parse(matcher.group(2)), true);
|
||||
}
|
||||
finish();
|
||||
return;
|
||||
} else if (QuickConversationsService.isConversations() && looksLikeJsonObject(result) && allowProvisioning()) {
|
||||
ProvisioningUtils.provision(this, result);
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
handleUri(Uri.parse(result), true);
|
||||
}
|
||||
finish();
|
||||
}
|
||||
|
||||
private static boolean looksLikeJsonObject(final String input) {
|
||||
return input.charAt(0) == '{' && input.charAt(input.length() - 1) == '}';
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package eu.siacs.conversations.utils;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
|
@ -139,4 +140,15 @@ public class Compatibility {
|
|||
Log.d(Config.LOGTAG, context.getClass().getSimpleName() + " was unable to start service");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressLint("UnsupportedChromeOsCameraSystemFeature")
|
||||
public static boolean hasFeatureCamera(final Context context) {
|
||||
final PackageManager packageManager = context.getPackageManager();
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
return packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY);
|
||||
} else {
|
||||
return packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,8 @@ public class XmppUri {
|
|||
public static final String ACTION_MESSAGE = "message";
|
||||
public static final String ACTION_REGISTER = "register";
|
||||
public static final String ACTION_ROSTER = "roster";
|
||||
public static final String PARAMETER_PRE_AUTH = "preauth";
|
||||
public static final String PARAMETER_IBR = "ibr";
|
||||
private static final String OMEMO_URI_PARAM = "omemo-sid-";
|
||||
protected Uri uri;
|
||||
protected String jid;
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
package eu.siacs.conversations.utils;
|
||||
|
||||
import eu.siacs.conversations.ui.UriHandlerActivity;
|
||||
|
||||
public class ProvisioningUtils {
|
||||
public static void provision(UriHandlerActivity uriHandlerActivity, String result) {
|
||||
throw new IllegalStateException("Quicksy does not support provisioning");
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue