diff --git a/build.gradle b/build.gradle
index f5c0071d3..aca9b99e0 100644
--- a/build.gradle
+++ b/build.gradle
@@ -23,6 +23,7 @@ configurations {
compatImplementation
fullFreeCompatImplementation
quickFreeCompatImplementation
+ quickImplementation
}
ext {
@@ -59,6 +60,7 @@ dependencies {
implementation 'org.osmdroid:osmdroid-android:6.0.1'
implementation 'org.hsluv:hsluv:0.2'
implementation 'org.conscrypt:conscrypt-android:1.3.0'
+ quickImplementation 'io.michaelrocks:libphonenumber-android:8.9.14'
}
ext {
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
index bd90c28ec..6e13f5af0 100644
--- a/src/main/res/values/strings.xml
+++ b/src/main/res/values/strings.xml
@@ -750,4 +750,12 @@
cancelledYou are already drafting a message.Feature not implemented
+ Invalid country code
+ Choose a country
+ phone number
+ Verify your phone number
+ Quick Conversations will send an SMS message (carrier charges may apply) to verify your phone number. Enter your country code and phone number:
+ %s
Is this OK, or would you like to edit the number?]]>
+ %s is not a valid phone number.
+ Please enter your phone number.
diff --git a/src/quick/AndroidManifest.xml b/src/quick/AndroidManifest.xml
new file mode 100644
index 000000000..4316f2f99
--- /dev/null
+++ b/src/quick/AndroidManifest.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
diff --git a/src/quick/java/eu/siacs/conversations/ui/EnterPhoneNumberActivity.java b/src/quick/java/eu/siacs/conversations/ui/EnterPhoneNumberActivity.java
new file mode 100644
index 000000000..d247139ac
--- /dev/null
+++ b/src/quick/java/eu/siacs/conversations/ui/EnterPhoneNumberActivity.java
@@ -0,0 +1,107 @@
+package eu.siacs.conversations.ui;
+
+import android.app.AlertDialog;
+import android.databinding.DataBindingUtil;
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.text.Editable;
+import android.text.Html;
+import android.text.TextUtils;
+import android.text.TextWatcher;
+import android.util.Log;
+import android.view.View;
+
+import eu.siacs.conversations.Config;
+import eu.siacs.conversations.R;
+import eu.siacs.conversations.databinding.ActivityEnterNumberBinding;
+import eu.siacs.conversations.ui.drawable.TextDrawable;
+import eu.siacs.conversations.utils.PhoneNumberUtilWrapper;
+import io.michaelrocks.libphonenumber.android.NumberParseException;
+import io.michaelrocks.libphonenumber.android.PhoneNumberUtil;
+import io.michaelrocks.libphonenumber.android.Phonenumber;
+
+public class EnterPhoneNumberActivity extends AppCompatActivity {
+
+ private ActivityEnterNumberBinding binding;
+ private String region = null;
+ private final TextWatcher countryCodeTextWatcher = 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 editable) {
+ final String text = editable.toString();
+ try {
+ final int code = Integer.parseInt(text);
+ region = PhoneNumberUtilWrapper.getInstance(EnterPhoneNumberActivity.this).getRegionCodeForCountryCode(code);
+ if ("ZZ".equals(region)) {
+ binding.country.setText(TextUtils.isEmpty(text) ? R.string.choose_a_country : R.string.invalid_country_code);
+ } else {
+ binding.number.requestFocus();
+ binding.country.setText(PhoneNumberUtilWrapper.getCountryForCode(region));
+ }
+ } catch (NumberFormatException e) {
+ binding.country.setText(TextUtils.isEmpty(text) ? R.string.choose_a_country : R.string.invalid_country_code);
+ }
+ }
+ };
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ this.binding = DataBindingUtil.setContentView(this, R.layout.activity_enter_number);
+ this.binding.countryCode.setCompoundDrawables(new TextDrawable(this.binding.countryCode, "+"), null, null, null);
+ this.binding.country.setOnClickListener(this::onSelectCountryClick);
+ this.binding.next.setOnClickListener(this::onNextClick);
+ setSupportActionBar((Toolbar) this.binding.toolbar);
+
+
+ this.binding.countryCode.addTextChangedListener(this.countryCodeTextWatcher);
+ this.region = PhoneNumberUtilWrapper.getUserCountry(this);
+ this.binding.countryCode.setText(String.valueOf(PhoneNumberUtilWrapper.getInstance(this).getCountryCodeForRegion(this.region)));
+ }
+
+ private void onNextClick(View v) {
+ final AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ try {
+ final Editable number = this.binding.number.getText();
+ final String input = number.toString();
+ final Phonenumber.PhoneNumber phoneNumber = PhoneNumberUtilWrapper.getInstance(this).parse(input, region);
+ this.binding.countryCode.setText(String.valueOf(phoneNumber.getCountryCode()));
+ number.clear();
+ number.append(String.valueOf(phoneNumber.getNationalNumber()));
+ final String formattedPhoneNumber = PhoneNumberUtilWrapper.getInstance(this).format(phoneNumber, PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL);
+
+ if (PhoneNumberUtilWrapper.getInstance(this).isValidNumber(phoneNumber)) {
+ builder.setMessage(Html.fromHtml(getString(R.string.we_will_be_verifying, formattedPhoneNumber)));
+ builder.setNegativeButton(R.string.edit, null);
+ builder.setPositiveButton(R.string.ok, (dialog, which) -> onPhoneNumberEntered(phoneNumber));
+ } else {
+ builder.setMessage(getString(R.string.not_a_valid_phone_number, formattedPhoneNumber));
+ builder.setPositiveButton(R.string.ok, null);
+ }
+ Log.d(Config.LOGTAG, phoneNumber.toString());
+ } catch (NumberParseException e) {
+ builder.setMessage(R.string.please_enter_your_phone_number);
+ builder.setPositiveButton(R.string.ok, null);
+ }
+ builder.create().show();
+ }
+
+ private void onSelectCountryClick(View view) {
+
+ }
+
+ private void onPhoneNumberEntered(Phonenumber.PhoneNumber phoneNumber) {
+
+ }
+
+}
diff --git a/src/quick/java/eu/siacs/conversations/ui/drawable/TextDrawable.java b/src/quick/java/eu/siacs/conversations/ui/drawable/TextDrawable.java
new file mode 100644
index 000000000..3b49962a6
--- /dev/null
+++ b/src/quick/java/eu/siacs/conversations/ui/drawable/TextDrawable.java
@@ -0,0 +1,240 @@
+package eu.siacs.conversations.ui.drawable; /**
+ * Copyright 2016 Ali Muzaffar
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.widget.TextView;
+
+import java.lang.ref.WeakReference;
+
+public class TextDrawable extends Drawable implements TextWatcher {
+ private WeakReference ref;
+ private String mText;
+ private Paint mPaint;
+ private Rect mHeightBounds;
+ private boolean mBindToViewPaint = false;
+ private float mPrevTextSize = 0;
+ private boolean mInitFitText = false;
+ private boolean mFitTextEnabled = false;
+
+ /**
+ * Create a TextDrawable using the given paint object and string
+ *
+ * @param paint
+ * @param s
+ */
+ public TextDrawable(Paint paint, String s) {
+ mText = s;
+ mPaint = new Paint(paint);
+ mHeightBounds = new Rect();
+ init();
+ }
+
+ /**
+ * Create a TextDrawable. This uses the given TextView to initialize paint and has initial text
+ * that will be drawn. Initial text can also be useful for reserving space that may otherwise
+ * not be available when setting compound drawables.
+ *
+ * @param tv The TextView / EditText using to initialize this drawable
+ * @param initialText Optional initial text to display
+ * @param bindToViewsText Should this drawable mirror the text in the TextView
+ * @param bindToViewsPaint Should this drawable mirror changes to Paint in the TextView, like textColor, typeface, alpha etc.
+ * Note, this will override any changes made using setColorFilter or setAlpha.
+ */
+ public TextDrawable(TextView tv, String initialText, boolean bindToViewsText, boolean bindToViewsPaint) {
+ this(tv.getPaint(), initialText);
+ ref = new WeakReference<>(tv);
+ if (bindToViewsText || bindToViewsPaint) {
+ if (bindToViewsText) {
+ tv.addTextChangedListener(this);
+ }
+ mBindToViewPaint = bindToViewsPaint;
+ }
+ }
+
+ /**
+ * Create a TextDrawable. This uses the given TextView to initialize paint and the text that
+ * will be drawn.
+ *
+ * @param tv The TextView / EditText using to initialize this drawable
+ * @param bindToViewsText Should this drawable mirror the text in the TextView
+ * @param bindToViewsPaint Should this drawable mirror changes to Paint in the TextView, like textColor, typeface, alpha etc.
+ * Note, this will override any changes made using setColorFilter or setAlpha.
+ */
+ public TextDrawable(TextView tv, boolean bindToViewsText, boolean bindToViewsPaint) {
+ this(tv, tv.getText().toString(), false, false);
+ }
+
+ /**
+ * Use the provided TextView/EditText to initialize the drawable.
+ * The Drawable will copy the Text and the Paint properties, however it will from that
+ * point on be independant of the TextView.
+ *
+ * @param tv a TextView or EditText or any of their children.
+ */
+ public TextDrawable(TextView tv) {
+ this(tv, false, false);
+ }
+
+ /**
+ * Use the provided TextView/EditText to initialize the drawable.
+ * The Drawable will copy the Paint properties, and use the provided text to initialise itself.
+ *
+ * @param tv a TextView or EditText or any of their children.
+ * @param s The String to draw
+ */
+ public TextDrawable(TextView tv, String s) {
+ this(tv, s, false, false);
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ if (mBindToViewPaint && ref.get() != null) {
+ Paint p = ref.get().getPaint();
+ canvas.drawText(mText, 0, getBounds().height(), p);
+ } else {
+ if (mInitFitText) {
+ fitTextAndInit();
+ }
+ canvas.drawText(mText, 0, getBounds().height(), mPaint);
+ }
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+ mPaint.setAlpha(alpha);
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter colorFilter) {
+ mPaint.setColorFilter(colorFilter);
+ }
+
+ @Override
+ public int getOpacity() {
+ int alpha = mPaint.getAlpha();
+ if (alpha == 0) {
+ return PixelFormat.TRANSPARENT;
+ } else if (alpha == 255) {
+ return PixelFormat.OPAQUE;
+ } else {
+ return PixelFormat.TRANSLUCENT;
+ }
+ }
+
+ private void init() {
+ Rect bounds = getBounds();
+ //We want to use some character to determine the max height of the text.
+ //Otherwise if we draw something like "..." they will appear centered
+ //Here I'm just going to use the entire alphabet to determine max height.
+ mPaint.getTextBounds("1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+", 0, 1, mHeightBounds);
+ //This doesn't account for leading or training white spaces.
+ //mPaint.getTextBounds(mText, 0, mText.length(), bounds);
+ float width = mPaint.measureText(mText);
+ bounds.top = mHeightBounds.top;
+ bounds.bottom = mHeightBounds.bottom;
+ bounds.right = (int) width;
+ bounds.left = 0;
+ setBounds(bounds);
+ }
+
+ public void setPaint(Paint paint) {
+ mPaint = new Paint(paint);
+ //Since this can change the font used, we need to recalculate bounds.
+ if (mFitTextEnabled && !mInitFitText) {
+ fitTextAndInit();
+ } else {
+ init();
+ }
+ invalidateSelf();
+ }
+
+ public Paint getPaint() {
+ return mPaint;
+ }
+
+ public void setText(String text) {
+ mText = text;
+ //Since this can change the bounds of the text, we need to recalculate.
+ if (mFitTextEnabled && !mInitFitText) {
+ fitTextAndInit();
+ } else {
+ init();
+ }
+ invalidateSelf();
+ }
+
+ public String getText() {
+ return mText;
+ }
+
+ @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) {
+ setText(s.toString());
+ }
+
+ /**
+ * Make the TextDrawable match the width of the View it's associated with.
+ *
+ * Note: While this option will not work if bindToViewPaint is true.
+ *
+ * @param fitText
+ */
+ public void setFillText(boolean fitText) {
+ mFitTextEnabled = fitText;
+ if (fitText) {
+ mPrevTextSize = mPaint.getTextSize();
+ if (ref.get() != null) {
+ if (ref.get().getWidth() > 0) {
+ fitTextAndInit();
+ } else {
+ mInitFitText = true;
+ }
+ }
+ } else {
+ if (mPrevTextSize > 0) {
+ mPaint.setTextSize(mPrevTextSize);
+ }
+ init();
+ }
+ }
+
+ private void fitTextAndInit() {
+ float fitWidth = ref.get().getWidth();
+ float textWidth = mPaint.measureText(mText);
+ float multi = fitWidth / textWidth;
+ mPaint.setTextSize(mPaint.getTextSize() * multi);
+ mInitFitText = false;
+ init();
+ }
+
+}
\ No newline at end of file
diff --git a/src/quick/java/eu/siacs/conversations/utils/PhoneNumberUtilWrapper.java b/src/quick/java/eu/siacs/conversations/utils/PhoneNumberUtilWrapper.java
new file mode 100644
index 000000000..854c0770b
--- /dev/null
+++ b/src/quick/java/eu/siacs/conversations/utils/PhoneNumberUtilWrapper.java
@@ -0,0 +1,53 @@
+package eu.siacs.conversations.utils;
+
+import android.content.Context;
+import android.telephony.TelephonyManager;
+
+import java.util.Locale;
+
+import io.michaelrocks.libphonenumber.android.PhoneNumberUtil;
+
+public class PhoneNumberUtilWrapper {
+
+ private static volatile PhoneNumberUtil instance;
+
+
+ public static String getCountryForCode(String code) {
+ Locale locale = new Locale("", code);
+ return locale.getDisplayCountry();
+ }
+
+ public static String getUserCountry(Context context) {
+ try {
+ final TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+ final String simCountry = tm.getSimCountryIso();
+ if (simCountry != null && simCountry.length() == 2) { // SIM country code is available
+ return simCountry.toUpperCase(Locale.US);
+ } else if (tm.getPhoneType() != TelephonyManager.PHONE_TYPE_CDMA) { // device is not 3G (would be unreliable)
+ String networkCountry = tm.getNetworkCountryIso();
+ if (networkCountry != null && networkCountry.length() == 2) { // network country code is available
+ return networkCountry.toUpperCase(Locale.US);
+ }
+ }
+ } catch (Exception e) {
+ // fallthrough
+ }
+ Locale locale = Locale.getDefault();
+ return locale.getCountry();
+ }
+
+ public static PhoneNumberUtil getInstance(final Context context) {
+ PhoneNumberUtil localInstance = instance;
+ if (localInstance == null) {
+ synchronized (PhoneNumberUtilWrapper.class){
+ localInstance = instance;
+ if (localInstance == null) {
+ instance = localInstance = PhoneNumberUtil.createInstance(context);
+ }
+
+ }
+ }
+ return localInstance;
+ }
+
+}
diff --git a/src/quick/java/eu/siacs/conversations/utils/SignupUtils.java b/src/quick/java/eu/siacs/conversations/utils/SignupUtils.java
index 6a874e713..314a8655e 100644
--- a/src/quick/java/eu/siacs/conversations/utils/SignupUtils.java
+++ b/src/quick/java/eu/siacs/conversations/utils/SignupUtils.java
@@ -2,16 +2,22 @@ package eu.siacs.conversations.utils;
import android.app.Activity;
import android.content.Intent;
+import android.util.Log;
+import eu.siacs.conversations.Config;
import eu.siacs.conversations.ui.ConversationsActivity;
+import eu.siacs.conversations.ui.EnterPhoneNumberActivity;
public class SignupUtils {
public static Intent getSignUpIntent(Activity activity) {
- return null;
+ final Intent intent = new Intent(activity, EnterPhoneNumberActivity.class);
+ return intent;
}
public static Intent getRedirectionIntent(ConversationsActivity activity) {
- return null;
+ final Intent intent = getSignUpIntent(activity);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ return intent;
}
}
\ No newline at end of file
diff --git a/src/quick/res/drawable-hdpi/ic_arrow_drop_down_black_18dp.png b/src/quick/res/drawable-hdpi/ic_arrow_drop_down_black_18dp.png
new file mode 100644
index 000000000..f15fe3a5b
Binary files /dev/null and b/src/quick/res/drawable-hdpi/ic_arrow_drop_down_black_18dp.png differ
diff --git a/src/quick/res/drawable-mdpi/ic_arrow_drop_down_black_18dp.png b/src/quick/res/drawable-mdpi/ic_arrow_drop_down_black_18dp.png
new file mode 100644
index 000000000..68ad72ffe
Binary files /dev/null and b/src/quick/res/drawable-mdpi/ic_arrow_drop_down_black_18dp.png differ
diff --git a/src/quick/res/drawable-xhdpi/ic_arrow_drop_down_black_18dp.png b/src/quick/res/drawable-xhdpi/ic_arrow_drop_down_black_18dp.png
new file mode 100644
index 000000000..2a5865da4
Binary files /dev/null and b/src/quick/res/drawable-xhdpi/ic_arrow_drop_down_black_18dp.png differ
diff --git a/src/quick/res/drawable-xxhdpi/ic_arrow_drop_down_black_18dp.png b/src/quick/res/drawable-xxhdpi/ic_arrow_drop_down_black_18dp.png
new file mode 100644
index 000000000..06cc5c9a5
Binary files /dev/null and b/src/quick/res/drawable-xxhdpi/ic_arrow_drop_down_black_18dp.png differ
diff --git a/src/quick/res/drawable-xxxhdpi/ic_arrow_drop_down_black_18dp.png b/src/quick/res/drawable-xxxhdpi/ic_arrow_drop_down_black_18dp.png
new file mode 100644
index 000000000..df2614b96
Binary files /dev/null and b/src/quick/res/drawable-xxxhdpi/ic_arrow_drop_down_black_18dp.png differ
diff --git a/src/quick/res/layout/activity_enter_number.xml b/src/quick/res/layout/activity_enter_number.xml
new file mode 100644
index 000000000..c91bb0a6a
--- /dev/null
+++ b/src/quick/res/layout/activity_enter_number.xml
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+