automatically receive Quicksy SMS. fixes #3962

requires new version of QuicksyServer
This commit is contained in:
Daniel Gultsch 2021-01-19 15:45:41 +01:00
parent 624bb565a8
commit 26a4598f3c
11 changed files with 137 additions and 10 deletions

View file

@ -13,8 +13,7 @@ before_script:
- mkdir libs - mkdir libs
- wget -O libs/libwebrtc-m87.aar https://gultsch.de/files/libwebrtc-m87.aar - wget -O libs/libwebrtc-m87.aar https://gultsch.de/files/libwebrtc-m87.aar
script: script:
- ./gradlew assembleConversationsFreeSystemRelease - ./gradlew assembleDebug
- ./gradlew assembleQuicksyFreeCompatRelease
before_install: before_install:
- yes | sdkmanager "platforms;android-28" - yes | sdkmanager "platforms;android-28"

View file

@ -26,6 +26,8 @@ configurations {
conversationsFreeCompatImplementation conversationsFreeCompatImplementation
conversationsPlaystoreCompatImplementation conversationsPlaystoreCompatImplementation
conversationsPlaystoreSystemImplementation conversationsPlaystoreSystemImplementation
quicksyPlaystoreCompatImplementation
quicksyPlaystoreSystemImplementation
quicksyFreeCompatImplementation quicksyFreeCompatImplementation
quicksyImplementation quicksyImplementation
} }
@ -41,6 +43,8 @@ dependencies {
} }
conversationsPlaystoreCompatImplementation("com.android.installreferrer:installreferrer:2.2") conversationsPlaystoreCompatImplementation("com.android.installreferrer:installreferrer:2.2")
conversationsPlaystoreSystemImplementation("com.android.installreferrer:installreferrer:2.2") conversationsPlaystoreSystemImplementation("com.android.installreferrer:installreferrer:2.2")
quicksyPlaystoreCompatImplementation 'com.google.android.gms:play-services-auth-api-phone:17.0.0'
quicksyPlaystoreSystemImplementation 'com.google.android.gms:play-services-auth-api-phone:17.0.0'
implementation 'org.sufficientlysecure:openpgp-api:10.0' implementation 'org.sufficientlysecure:openpgp-api:10.0'
implementation 'com.theartofdev.edmodo:android-image-cropper:2.8.0' implementation 'com.theartofdev.edmodo:android-image-cropper:2.8.0'
implementation 'androidx.appcompat:appcompat:1.2.0' implementation 'androidx.appcompat:appcompat:1.2.0'
@ -156,14 +160,21 @@ android {
} }
sourceSets { sourceSets {
quicksyFreeSystem {
java {
srcDir 'src/quicksyFree/java'
}
}
quicksyFreeCompat { quicksyFreeCompat {
java { java {
srcDir 'src/freeCompat/java' srcDir 'src/freeCompat/java'
srcDir 'src/quicksyFree/java'
} }
} }
quicksyPlaystoreCompat { quicksyPlaystoreCompat {
java { java {
srcDir 'src/playstoreCompat/java' srcDir 'src/playstoreCompat/java'
srcDir 'src/quicksyPlaystore/java'
} }
res { res {
srcDir 'src/playstoreCompat/res' srcDir 'src/playstoreCompat/res'
@ -171,6 +182,9 @@ android {
} }
} }
quicksyPlaystoreSystem { quicksyPlaystoreSystem {
java {
srcDir 'src/quicksyPlaystore/java'
}
res { res {
srcDir 'src/quicksyPlaystore/res' srcDir 'src/quicksyPlaystore/res'
} }

View file

@ -1,5 +1,10 @@
package eu.siacs.conversations.services; package eu.siacs.conversations.services;
import android.content.Intent;
import android.util.Log;
import eu.siacs.conversations.Config;
public class QuickConversationsService extends AbstractQuickConversationsService { public class QuickConversationsService extends AbstractQuickConversationsService {
QuickConversationsService(XmppConnectionService xmppConnectionService) { QuickConversationsService(XmppConnectionService xmppConnectionService) {
@ -25,4 +30,9 @@ public class QuickConversationsService extends AbstractQuickConversationsService
public void considerSyncBackground(boolean force) { public void considerSyncBackground(boolean force) {
} }
@Override
public void handleSmsReceived(Intent intent) {
Log.d(Config.LOGTAG,"ignoring received SMS");
}
} }

View file

@ -72,12 +72,14 @@
<service android:name=".services.XmppConnectionService" /> <service android:name=".services.XmppConnectionService" />
<receiver android:name=".services.EventReceiver"> <receiver android:name=".services.EventReceiver"
android:permission="com.google.android.gms.auth.api.phone.permission.SEND">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" /> <action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" /> <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
<action android:name="android.intent.action.ACTION_SHUTDOWN" /> <action android:name="android.intent.action.ACTION_SHUTDOWN" />
<action android:name="android.media.RINGER_MODE_CHANGED" /> <action android:name="android.media.RINGER_MODE_CHANGED" />
<action android:name="com.google.android.gms.auth.api.phone.SMS_RETRIEVED"/>
</intent-filter> </intent-filter>
</receiver> </receiver>

View file

@ -1,9 +1,14 @@
package eu.siacs.conversations.services; package eu.siacs.conversations.services;
import android.content.Intent;
import eu.siacs.conversations.BuildConfig; import eu.siacs.conversations.BuildConfig;
public abstract class AbstractQuickConversationsService { public abstract class AbstractQuickConversationsService {
public static final String SMS_RETRIEVED_ACTION = "com.google.android.gms.auth.api.phone.SMS_RETRIEVED";
protected final XmppConnectionService service; protected final XmppConnectionService service;
public AbstractQuickConversationsService(XmppConnectionService service) { public AbstractQuickConversationsService(XmppConnectionService service) {
@ -25,4 +30,6 @@ public abstract class AbstractQuickConversationsService {
public abstract boolean isSynchronizing(); public abstract boolean isSynchronizing();
public abstract void considerSyncBackground(boolean force); public abstract void considerSyncBackground(boolean force);
public abstract void handleSmsReceived(Intent intent);
} }

View file

@ -27,7 +27,7 @@ public class EventReceiver extends BroadcastReceiver {
if (extras != null) { if (extras != null) {
intentForService.putExtras(extras); intentForService.putExtras(extras);
} }
if ("ui".equals(action) || hasEnabledAccounts(context)) { if ("ui".equals(action) || QuickConversationsService.SMS_RETRIEVED_ACTION.equals(action) || hasEnabledAccounts(context)) {
Compatibility.startService(context, intentForService); Compatibility.startService(context, intentForService);
} else { } else {
Log.d(Config.LOGTAG, "EventReceiver ignored action " + intentForService.getAction()); Log.d(Config.LOGTAG, "EventReceiver ignored action " + intentForService.getAction());

View file

@ -32,10 +32,6 @@ import android.os.SystemClock;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.provider.ContactsContract; import android.provider.ContactsContract;
import android.security.KeyChain; import android.security.KeyChain;
import androidx.annotation.BoolRes;
import androidx.annotation.IntegerRes;
import androidx.core.app.RemoteInput;
import androidx.core.content.ContextCompat;
import android.telephony.PhoneStateListener; import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager; import android.telephony.TelephonyManager;
import android.text.TextUtils; import android.text.TextUtils;
@ -44,6 +40,11 @@ import android.util.Log;
import android.util.LruCache; import android.util.LruCache;
import android.util.Pair; import android.util.Pair;
import androidx.annotation.BoolRes;
import androidx.annotation.IntegerRes;
import androidx.core.app.RemoteInput;
import androidx.core.content.ContextCompat;
import com.google.common.base.Objects; import com.google.common.base.Objects;
import com.google.common.base.Strings; import com.google.common.base.Strings;
@ -135,6 +136,7 @@ import eu.siacs.conversations.utils.WakeLockHelper;
import eu.siacs.conversations.utils.XmppUri; import eu.siacs.conversations.utils.XmppUri;
import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xml.Namespace; import eu.siacs.conversations.xml.Namespace;
import eu.siacs.conversations.xmpp.Jid;
import eu.siacs.conversations.xmpp.OnBindListener; import eu.siacs.conversations.xmpp.OnBindListener;
import eu.siacs.conversations.xmpp.OnContactStatusChanged; import eu.siacs.conversations.xmpp.OnContactStatusChanged;
import eu.siacs.conversations.xmpp.OnIqPacketReceived; import eu.siacs.conversations.xmpp.OnIqPacketReceived;
@ -159,7 +161,6 @@ import eu.siacs.conversations.xmpp.stanzas.IqPacket;
import eu.siacs.conversations.xmpp.stanzas.MessagePacket; import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
import eu.siacs.conversations.xmpp.stanzas.PresencePacket; import eu.siacs.conversations.xmpp.stanzas.PresencePacket;
import me.leolin.shortcutbadger.ShortcutBadger; import me.leolin.shortcutbadger.ShortcutBadger;
import eu.siacs.conversations.xmpp.Jid;
public class XmppConnectionService extends Service { public class XmppConnectionService extends Service {
@ -633,6 +634,9 @@ public class XmppConnectionService extends Service {
if (action != null) { if (action != null) {
final String uuid = intent.getStringExtra("uuid"); final String uuid = intent.getStringExtra("uuid");
switch (action) { switch (action) {
case QuickConversationsService.SMS_RETRIEVED_ACTION:
mQuickConversationsService.handleSmsReceived(intent);
break;
case ConnectivityManager.CONNECTIVITY_ACTION: case ConnectivityManager.CONNECTIVITY_ACTION:
if (hasInternetConnection()) { if (hasInternetConnection()) {
if (Config.POST_CONNECTIVITY_CHANGE_PING_INTERVAL > 0) { if (Config.POST_CONNECTIVITY_CHANGE_PING_INTERVAL > 0) {

View file

@ -1,8 +1,10 @@
package eu.siacs.conversations.services; package eu.siacs.conversations.services;
import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle;
import android.os.SystemClock; import android.os.SystemClock;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.util.Log; import android.util.Log;
@ -47,6 +49,7 @@ import eu.siacs.conversations.utils.AccountUtils;
import eu.siacs.conversations.utils.CryptoHelper; import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.utils.PhoneNumberUtilWrapper; import eu.siacs.conversations.utils.PhoneNumberUtilWrapper;
import eu.siacs.conversations.utils.SerialSingleThreadExecutor; import eu.siacs.conversations.utils.SerialSingleThreadExecutor;
import eu.siacs.conversations.utils.SmsRetrieverWrapper;
import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xml.Namespace; import eu.siacs.conversations.xml.Namespace;
import eu.siacs.conversations.xmpp.Jid; import eu.siacs.conversations.xmpp.Jid;
@ -122,6 +125,7 @@ public class QuickConversationsService extends AbstractQuickConversationsService
public void requestVerification(Phonenumber.PhoneNumber phoneNumber) { public void requestVerification(Phonenumber.PhoneNumber phoneNumber) {
final String e164 = PhoneNumberUtilWrapper.normalize(service, phoneNumber); final String e164 = PhoneNumberUtilWrapper.normalize(service, phoneNumber);
if (mVerificationRequestInProgress.compareAndSet(false, true)) { if (mVerificationRequestInProgress.compareAndSet(false, true)) {
SmsRetrieverWrapper.start(service);
new Thread(() -> { new Thread(() -> {
try { try {
final URL url = new URL(BASE_URL + "/authentication/" + e164); final URL url = new URL(BASE_URL + "/authentication/" + e164);
@ -322,6 +326,28 @@ public class QuickConversationsService extends AbstractQuickConversationsService
}); });
} }
@Override
public void handleSmsReceived(final Intent intent) {
final Bundle extras = intent.getExtras();
final String pin = SmsRetrieverWrapper.extractPin(extras);
if (pin == null) {
Log.d(Config.LOGTAG, "unable to extract Pin from received SMS");
return;
}
final Account account = AccountUtils.getFirst(service);
if (account == null) {
Log.d(Config.LOGTAG, "no account configured to process PIN received by SMS");
return;
}
verify(account, pin);
synchronized (mOnVerification) {
for (OnVerification onVerification : mOnVerification) {
onVerification.startBackgroundVerification(pin);
}
}
}
private void considerSync(boolean forced) { private void considerSync(boolean forced) {
Map<String, PhoneNumberContact> contacts = PhoneNumberContact.load(service); Map<String, PhoneNumberContact> contacts = PhoneNumberContact.load(service);
@ -429,11 +455,13 @@ public class QuickConversationsService extends AbstractQuickConversationsService
void onVerificationSucceeded(); void onVerificationSucceeded();
void onVerificationRetryAt(long timestamp); void onVerificationRetryAt(long timestamp);
void startBackgroundVerification(String pin);
} }
private static class Attempt { private static class Attempt {
private final long timestamp; private final long timestamp;
private int hash; private final int hash;
private static final Attempt NULL = new Attempt(0, 0); private static final Attempt NULL = new Attempt(0, 0);

View file

@ -316,6 +316,12 @@ public class VerifyActivity extends XmppActivity implements ClipboardManager.OnP
runOnUiThread(VERIFICATION_TIMEOUT_UPDATER); runOnUiThread(VERIFICATION_TIMEOUT_UPDATER);
} }
@Override
public void startBackgroundVerification(String pin) {
pinEntryWrapper.setPin(pin);
setVerifyingState(true);
}
//send sms again button callback //send sms again button callback
@Override @Override
public void onVerificationRequestFailed(int code) { public void onVerificationRequestFailed(int code) {
@ -329,6 +335,7 @@ public class VerifyActivity extends XmppActivity implements ClipboardManager.OnP
@Override @Override
public void onVerificationRequested() { public void onVerificationRequested() {
runOnUiThread(() -> { runOnUiThread(() -> {
pinEntryWrapper.clear();
setRequestingVerificationState(false); setRequestingVerificationState(false);
AlertDialog.Builder builder = new AlertDialog.Builder(this); AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage(R.string.we_have_sent_you_another_sms); builder.setMessage(R.string.we_have_sent_you_another_sms);

View file

@ -0,0 +1,16 @@
package eu.siacs.conversations.utils;
import android.os.Bundle;
import eu.siacs.conversations.services.XmppConnectionService;
public class SmsRetrieverWrapper {
public static void start(XmppConnectionService service) {
//nop
}
public static String extractPin(Bundle extras) {
return null;
}
}

View file

@ -0,0 +1,40 @@
package eu.siacs.conversations.utils;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import com.google.android.gms.auth.api.phone.SmsRetriever;
import com.google.android.gms.auth.api.phone.SmsRetrieverClient;
import com.google.android.gms.common.api.CommonStatusCodes;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.tasks.Task;
import com.google.common.base.Strings;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import eu.siacs.conversations.Config;
public class SmsRetrieverWrapper {
public static void start(final Context context) {
final SmsRetrieverClient client = SmsRetriever.getClient(context);
final Task<Void> task = client.startSmsRetriever();
task.addOnSuccessListener(aVoid -> Log.d(Config.LOGTAG, "successfully started SMS retriever"));
task.addOnFailureListener(e -> Log.d(Config.LOGTAG, "unable to start SMS retriever", e));
}
public static String extractPin(Bundle extras) {
final Status status = extras == null ? null : (Status) extras.get(SmsRetriever.EXTRA_STATUS);
if (status != null && status.getStatusCode() == CommonStatusCodes.SUCCESS) {
Log.d(Config.LOGTAG, "Verification SMS received with status success");
final String message = extras.getString(SmsRetriever.EXTRA_SMS_MESSAGE);
final Matcher m = Pattern.compile("(?<!\\d)\\d{6}(?!\\d)").matcher(Strings.nullToEmpty(message));
if (m.find()) {
return m.group();
}
}
return null;
}
}