verify activity

Daniel Gultsch created

Change summary

src/main/java/eu/siacs/conversations/entities/Account.java                    |   1 
src/main/java/eu/siacs/conversations/utils/AccountUtils.java                  |  10 
src/main/res/values/strings.xml                                               |   5 
src/quick/AndroidManifest.xml                                                 |   5 
src/quick/java/eu/siacs/conversations/services/QuickConversationsService.java |   6 
src/quick/java/eu/siacs/conversations/ui/EnterPhoneNumberActivity.java        |   5 
src/quick/java/eu/siacs/conversations/ui/VerifyActivity.java                  |  54 
src/quick/java/eu/siacs/conversations/ui/util/PinEntryWrapper.java            |  93 
src/quick/java/eu/siacs/conversations/utils/PhoneNumberUtilWrapper.java       |  11 
src/quick/java/eu/siacs/conversations/utils/SignupUtils.java                  |  16 
src/quick/res/layout/activity_verify.xml                                      | 163 
11 files changed, 365 insertions(+), 4 deletions(-)

Detailed changes

src/main/java/eu/siacs/conversations/entities/Account.java 🔗

@@ -54,6 +54,7 @@ public class Account extends AbstractEntity {
     public static final int OPTION_REQUIRES_ACCESS_MODE_CHANGE = 5;
     public static final int OPTION_LOGGED_IN_SUCCESSFULLY = 6;
     public static final int OPTION_HTTP_UPLOAD_AVAILABLE = 7;
+    public static final int OPTION_UNVERIFIED = 8;
     private static final String KEY_PGP_SIGNATURE = "pgp_signature";
     private static final String KEY_PGP_ID = "pgp_id";
     public final HashSet<Pair<String, String>> inProgressDiscoFetches = new HashSet<>();

src/main/java/eu/siacs/conversations/utils/AccountUtils.java 🔗

@@ -32,6 +32,14 @@ public class AccountUtils {
         return null;
     }
 
+    public static Account getFirst(XmppConnectionService service) {
+        final List<Account> accounts = service.getAccounts();
+        for(Account account : accounts) {
+            return account;
+        }
+        return null;
+    }
+
     public static Account getPendingAccount(XmppConnectionService service) {
         Account pending = null;
         for (Account account : service.getAccounts()) {
@@ -53,7 +61,7 @@ public class AccountUtils {
     }
 
     public static void launchManageAccount(XmppActivity xmppActivity) {
-        Account account = getFirstEnabled(xmppActivity.xmppConnectionService);
+        Account account = getFirst(xmppActivity.xmppConnectionService);
         xmppActivity.switchToAccount(account);
     }
 

src/main/res/values/strings.xml 🔗

@@ -759,4 +759,9 @@
     <string name="not_a_valid_phone_number">%s is not a valid phone number.</string>
     <string name="please_enter_your_phone_number">Please enter your phone number.</string>
     <string name="search_countries">Search countries</string>
+    <string name="verify_x">Verify %s</string>
+    <string name="we_have_sent_you_an_sms"><![CDATA[We have sent you an SMS to <b>%s</b>.]]></string>
+    <string name="please_enter_pin">Please enter the 6 digit pin below.</string>
+    <string name="resend_sms">Resend SMS</string>
+    <string name="back">back</string>
 </resources>

src/quick/AndroidManifest.xml 🔗

@@ -14,5 +14,10 @@
             android:label="@string/choose_a_country"
             android:launchMode="singleTask" />
 
+        <activity
+            android:name=".ui.VerifyActivity"
+            android:launchMode="singleTask"
+            android:label="@string/verify_your_phone_number"/>
+
     </application>
 </manifest>

src/quick/java/eu/siacs/conversations/services/QuickConversationsService.java 🔗

@@ -3,12 +3,14 @@ package eu.siacs.conversations.services;
 
 import android.util.Log;
 
+import java.security.SecureRandom;
 import java.util.Collections;
 import java.util.Set;
 import java.util.WeakHashMap;
 
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.utils.CryptoHelper;
 import eu.siacs.conversations.utils.PhoneNumberUtilWrapper;
 import io.michaelrocks.libphonenumber.android.Phonenumber;
 import rocks.xmpp.addr.Jid;
@@ -39,7 +41,9 @@ public class QuickConversationsService {
     public void requestVerification(Phonenumber.PhoneNumber phoneNumber) {
         String local = PhoneNumberUtilWrapper.normalize(service, phoneNumber);
         Log.d(Config.LOGTAG,"requesting verification for "+PhoneNumberUtilWrapper.normalize(service,phoneNumber));
-        Account account = new Account(Jid.of(local,"quick.conversations.im",null),"foo");
+        Account account = new Account(Jid.of(local,"quick.conversations.im",null), CryptoHelper.createPassword(new SecureRandom()));
+        account.setOption(Account.OPTION_DISABLED, true);
+        account.setOption(Account.OPTION_UNVERIFIED, true);
         service.createAccount(account);
         synchronized (mOnVerificationRequested) {
             for(OnVerificationRequested onVerificationRequested : mOnVerificationRequested) {

src/quick/java/eu/siacs/conversations/ui/VerifyActivity.java 🔗

@@ -0,0 +1,54 @@
+package eu.siacs.conversations.ui;
+
+import android.content.Intent;
+import android.databinding.DataBindingUtil;
+import android.os.Bundle;
+import android.support.v7.widget.Toolbar;
+import android.text.Html;
+import android.view.View;
+
+import eu.siacs.conversations.R;
+import eu.siacs.conversations.databinding.ActivityVerifyBinding;
+import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.ui.util.PinEntryWrapper;
+import eu.siacs.conversations.utils.AccountUtils;
+import eu.siacs.conversations.utils.PhoneNumberUtilWrapper;
+
+public class VerifyActivity extends XmppActivity {
+
+    private ActivityVerifyBinding binding;
+    private Account account;
+    private PinEntryWrapper pinEntryWrapper;
+
+    @Override
+    protected void onCreate(final Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        this.binding = DataBindingUtil.setContentView(this, R.layout.activity_verify);
+        setSupportActionBar((Toolbar) this.binding.toolbar);
+        this.pinEntryWrapper = new PinEntryWrapper(binding.pinBox);
+        binding.back.setOnClickListener(this::onBackButton);
+    }
+
+    private void onBackButton(View view) {
+        if (this.account != null) {
+            xmppConnectionService.deleteAccount(account);
+            Intent intent = new Intent(this,EnterPhoneNumberActivity.class);
+            startActivity(intent);
+            finish();
+        }
+    }
+
+    @Override
+    protected void refreshUiReal() {
+
+    }
+
+    @Override
+    void onBackendConnected() {
+        this.account = AccountUtils.getFirst(xmppConnectionService);
+        if (this.account == null) {
+            return;
+        }
+        this.binding.weHaveSent.setText(Html.fromHtml(getString(R.string.we_have_sent_you_an_sms, PhoneNumberUtilWrapper.prettyPhoneNumber(this, this.account.getJid()))));
+    }
+}

src/quick/java/eu/siacs/conversations/ui/util/PinEntryWrapper.java 🔗

@@ -0,0 +1,93 @@
+package eu.siacs.conversations.ui.util;
+
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.KeyEvent;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class PinEntryWrapper {
+
+    private final List<EditText> digits = new ArrayList<>();
+
+    private final TextWatcher textWatcher = new TextWatcher() {
+        @Override
+        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+
+        }
+
+        @Override
+        public void onTextChanged(CharSequence s, int start, int before, int count) {
+
+        }
+
+        @Override
+        public void afterTextChanged(Editable s) {
+            int current = -1;
+            for (int i = 0; i < digits.size(); ++i) {
+                if (s.hashCode() == digits.get(i).getText().hashCode()) {
+                    current = i;
+                }
+            }
+            if (current == -1) {
+                return;
+            }
+            if (s.length() > 0) {
+                if (current < digits.size() - 1) {
+                    digits.get(current + 1).requestFocus();
+                }
+            } else {
+                int focusOn = current;
+                for (int i = current - 1; i >= 0; --i) {
+                    if (digits.get(i).getText().length() == 0) {
+                        focusOn = i;
+                    } else {
+                        break;
+                    }
+                }
+                digits.get(focusOn).requestFocus();
+            }
+        }
+    };
+
+    private final View.OnKeyListener keyListener = (v, keyCode, event) -> {
+        if (event.getAction() != KeyEvent.ACTION_DOWN) {
+            return false;
+        }
+        if (v instanceof EditText) {
+            final EditText editText = (EditText) v;
+            if (keyCode == KeyEvent.KEYCODE_DEL && editText.getText().length() == 0) {
+                final int current = digits.indexOf(editText);
+                for (int i = current - 1; i >= 0; --i) {
+                    if (digits.get(i).getText().length() > 0) {
+                        digits.get(i).getText().clear();
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    };
+
+    public PinEntryWrapper(LinearLayout linearLayout) {
+        for (int i = 0; i < linearLayout.getChildCount(); ++i) {
+            View view = linearLayout.getChildAt(i);
+            if (view instanceof EditText) {
+                this.digits.add((EditText) view);
+            }
+        }
+        registerListeners();
+    }
+
+    private void registerListeners() {
+        for (EditText editText : digits) {
+            editText.addTextChangedListener(textWatcher);
+            editText.setOnKeyListener(keyListener);
+        }
+    }
+
+}

src/quick/java/eu/siacs/conversations/utils/PhoneNumberUtilWrapper.java 🔗

@@ -10,6 +10,7 @@ import java.util.Locale;
 import io.michaelrocks.libphonenumber.android.NumberParseException;
 import io.michaelrocks.libphonenumber.android.PhoneNumberUtil;
 import io.michaelrocks.libphonenumber.android.Phonenumber;
+import rocks.xmpp.addr.Jid;
 
 public class PhoneNumberUtilWrapper {
 
@@ -40,6 +41,16 @@ public class PhoneNumberUtilWrapper {
         return locale.getCountry();
     }
 
+    public static String prettyPhoneNumber(Context context, Jid jid) {
+        PhoneNumberUtil phoneNumberUtil = getInstance(context);
+        try {
+            Phonenumber.PhoneNumber phoneNumber = phoneNumberUtil.parse(jid.getEscapedLocal(), "de");
+            return phoneNumberUtil.format(phoneNumber, PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL);
+        } catch (Exception e) {
+            return jid.getEscapedLocal();
+        }
+    }
+
     public static String normalize(Context context, String number) throws NumberParseException {
         return normalize(context, getInstance(context).parse(number, getUserCountry(context)));
     }

src/quick/java/eu/siacs/conversations/utils/SignupUtils.java 🔗

@@ -5,8 +5,11 @@ import android.content.Intent;
 import android.util.Log;
 
 import eu.siacs.conversations.Config;
+import eu.siacs.conversations.entities.Account;
 import eu.siacs.conversations.ui.ConversationsActivity;
 import eu.siacs.conversations.ui.EnterPhoneNumberActivity;
+import eu.siacs.conversations.ui.StartConversationActivity;
+import eu.siacs.conversations.ui.VerifyActivity;
 
 public class SignupUtils {
 
@@ -16,7 +19,18 @@ public class SignupUtils {
     }
 
     public static Intent getRedirectionIntent(ConversationsActivity activity) {
-        final Intent intent = getSignUpIntent(activity);
+        final Intent intent;
+        final Account account = AccountUtils.getFirst(activity.xmppConnectionService);
+        if (account != null) {
+            if (account.isOptionSet(Account.OPTION_UNVERIFIED)) {
+                intent = new Intent(activity, VerifyActivity.class);
+            } else {
+                intent = new Intent(activity, StartConversationActivity.class);
+            }
+        } else {
+            intent = getSignUpIntent(activity);
+
+        }
         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
         return intent;
     }

src/quick/res/layout/activity_verify.xml 🔗

@@ -0,0 +1,163 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="?attr/color_background_primary"
+        android:orientation="vertical">
+
+        <include
+            android:id="@+id/toolbar"
+            layout="@layout/toolbar"/>
+        <ScrollView
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:fillViewport="true">
+        <RelativeLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content">
+
+            <LinearLayout
+                android:id="@+id/instructions"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentTop="true"
+                android:layout_centerHorizontal="true"
+                android:gravity="center_horizontal"
+                android:orientation="vertical"
+                android:padding="16dp">
+
+                <TextView
+                    android:id="@+id/we_have_sent"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/we_have_sent_you_an_sms"
+                    android:textAppearance="@style/TextAppearance.Conversations.Subhead" />
+
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/please_enter_pin"
+                    android:textAppearance="@style/TextAppearance.Conversations.Body1" />
+            </LinearLayout>
+
+            <LinearLayout
+                android:id="@+id/pin_box"
+                android:layout_width="230sp"
+                android:layout_height="wrap_content"
+                android:layout_below="@+id/instructions"
+                android:layout_centerHorizontal="true">
+                <EditText
+                    android:layout_width="35sp"
+                    android:layout_height="wrap_content"
+                    android:digits="1234567890"
+                    android:inputType="number"
+                    android:maxLength="1"
+                    android:textSize="40sp"
+                    android:gravity="center"
+                    android:textIsSelectable="false"
+                    />
+                <Space
+                    android:layout_width="0dp"
+                    android:layout_height="0dp"
+                    android:layout_weight="1"/>
+                <EditText
+                    android:layout_width="35sp"
+                    android:layout_height="wrap_content"
+                    android:digits="1234567890"
+                    android:inputType="number"
+                    android:maxLength="1"
+                    android:textSize="40sp"
+                    android:gravity="center"
+                    android:textIsSelectable="false"
+                    />
+                <Space
+                    android:layout_width="0dp"
+                    android:layout_height="0dp"
+                    android:layout_weight="1"/>
+                <EditText
+                    android:layout_width="35sp"
+                    android:layout_height="wrap_content"
+                    android:digits="1234567890"
+                    android:inputType="number"
+                    android:maxLength="1"
+                    android:textSize="40sp"
+                    android:gravity="center"
+                    android:textIsSelectable="false"
+                    />
+                <Space
+                    android:layout_width="0dp"
+                    android:layout_height="0dp"
+                    android:layout_weight="1"/>
+                <EditText
+                    android:layout_width="35sp"
+                    android:layout_height="wrap_content"
+                    android:digits="1234567890"
+                    android:inputType="number"
+                    android:maxLength="1"
+                    android:textSize="40sp"
+                    android:gravity="center"
+                    android:textIsSelectable="false"
+                    />
+                <Space
+                    android:layout_width="0dp"
+                    android:layout_height="0dp"
+                    android:layout_weight="1"/>
+                <EditText
+                    android:layout_width="35sp"
+                    android:layout_height="wrap_content"
+                    android:digits="1234567890"
+                    android:inputType="number"
+                    android:maxLength="1"
+                    android:textSize="40sp"
+                    android:gravity="center"
+                    android:textIsSelectable="false"
+                    />
+                <Space
+                    android:layout_width="0dp"
+                    android:layout_height="0dp"
+                    android:layout_weight="1"/>
+                <EditText
+                    android:layout_width="35sp"
+                    android:layout_height="wrap_content"
+                    android:digits="1234567890"
+                    android:inputType="number"
+                    android:maxLength="1"
+                    android:textSize="40sp"
+                    android:gravity="center"
+                    android:textIsSelectable="false"
+                    />
+            </LinearLayout>
+            <Button
+                android:id="@+id/next"
+                android:layout_alignParentBottom="true"
+                android:layout_alignParentEnd="true"
+                style="@style/Widget.Conversations.Button.Borderless"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/next"
+                android:textColor="?colorAccent"/>
+
+            <Button
+                android:id="@+id/back"
+                android:layout_alignParentBottom="true"
+                android:layout_alignParentStart="true"
+                style="@style/Widget.Conversations.Button.Borderless"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/back"
+                android:textColor="?android:textColorSecondary" />
+                <Button
+                    android:layout_centerHorizontal="true"
+                    android:layout_below="@+id/pin_box"
+                    android:id="@+id/resend_sms"
+                    style="@style/Widget.Conversations.Button.Borderless"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/resend_sms"
+                    android:textColor="?android:textColorSecondary"/>
+        </RelativeLayout>
+        </ScrollView>
+    </LinearLayout>
+</layout>