> {
+ return mClaimResponseLiveData
+ }
+
+ fun claim(phone: String, udid: String) {
+ if (ValidatorUtils.phoneValidator(phone)) {
+ val disposable = userRepository.claim(phone, udid)
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .doOnSubscribe { mClaimResponseLiveData.value = MyResponse.loading() }
+ .subscribe({
+ when (it.code()) {
+ 200 -> mClaimResponseLiveData.value = MyResponse.success(it.body()!!)
+ 400 -> mClaimResponseLiveData.value = MyResponse.failed(BadRequestException())
+ 500 -> mClaimResponseLiveData.value = MyResponse.failed(ServerException())
+ 710 -> mClaimResponseLiveData.value =
+ MyResponse.failed(InvalidUdidOrPhoneException())
+ }
+ }, { throwable ->
+ mClaimResponseLiveData.value = MyResponse.failed(throwable)
+ })
+ mCompositeDisposable.add(disposable)
+ } else
+ mClaimResponseLiveData.value = MyResponse.failed(InvalidPhoneNumberException())
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/de/netalic/peacock/util/CommonUtils.kt b/app/src/main/java/de/netalic/peacock/util/CommonUtils.kt
new file mode 100644
index 0000000..99a819d
--- /dev/null
+++ b/app/src/main/java/de/netalic/peacock/util/CommonUtils.kt
@@ -0,0 +1,20 @@
+package de.netalic.peacock.util
+
+import android.app.Activity
+import android.view.inputmethod.InputMethodManager
+import androidx.fragment.app.FragmentActivity
+
+
+class CommonUtils {
+
+ companion object {
+ fun hideSoftKeyboard(activity: FragmentActivity) {
+ val inputMethodManager = activity.getSystemService(
+ Activity.INPUT_METHOD_SERVICE
+ ) as InputMethodManager
+ inputMethodManager.hideSoftInputFromWindow(
+ activity.currentFocus!!.windowToken, 0
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/de/netalic/peacock/util/CustomPhoneFormatTextWatcherUtils.java b/app/src/main/java/de/netalic/peacock/util/CustomPhoneFormatTextWatcherUtils.java
new file mode 100644
index 0000000..b614416
--- /dev/null
+++ b/app/src/main/java/de/netalic/peacock/util/CustomPhoneFormatTextWatcherUtils.java
@@ -0,0 +1,187 @@
+package de.netalic.peacock.util;
+
+import android.telephony.PhoneNumberUtils;
+import android.text.Editable;
+import android.text.Selection;
+import android.text.TextWatcher;
+import android.util.Log;
+import com.google.i18n.phonenumbers.AsYouTypeFormatter;
+import com.google.i18n.phonenumbers.PhoneNumberUtil;
+
+import java.util.Locale;
+
+/**
+ * Watches a {@link android.widget.TextView} and if a phone number is entered
+ * will format it.
+ *
+ * Stop formatting when the user
+ *
+ * - Inputs non-dialable characters
+ * - Removes the separator in the middle of string.
+ *
+ *
+ * The formatting will be restarted once the text is cleared.
+ * This class get phone code and locale
+ */
+public class CustomPhoneFormatTextWatcherUtils implements TextWatcher {
+
+ /**
+ * Indicates the change was caused by ourselves.
+ */
+ private boolean mSelfChange = false;
+
+ /**
+ * Indicates the formatting has been stopped.
+ */
+ private boolean mStopFormatting;
+
+ private AsYouTypeFormatter mFormatter;
+
+
+ private String code;
+
+ /**
+ * The formatting is based on the current system locale and future locale changes
+ * may not take effect on this instance.
+ */
+ public CustomPhoneFormatTextWatcherUtils() {
+
+ this(Locale.getDefault().getCountry(), "");
+ }
+
+ /**
+ * The formatting is based on the given countryCode
.
+ *
+ * @param countryCode the ISO 3166-1 two-letter country code that indicates the country/region
+ * where the phone number is being entered.
+ * @hide
+ */
+ public CustomPhoneFormatTextWatcherUtils(String countryCode, String code) {
+
+ if (countryCode == null) throw new IllegalArgumentException();
+ mFormatter = PhoneNumberUtil.getInstance().getAsYouTypeFormatter(countryCode);
+ this.code = code;
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count,
+ int after) {
+
+ if (mSelfChange || mStopFormatting) {
+ return;
+ }
+ // If the user manually deleted any non-dialable characters, stop formatting
+ if (count > 0 && hasSeparator(s, start, count)) {
+ stopFormatting();
+ }
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+
+ if (mSelfChange || mStopFormatting) {
+ return;
+ }
+ // If the user inserted any non-dialable characters, stop formatting
+ if (count > 0 && hasSeparator(s, start, count)) {
+ stopFormatting();
+ }
+ }
+
+ @Override
+ public synchronized void afterTextChanged(Editable s) {
+
+ if (mStopFormatting) {
+ // Restart the formatting when all texts were clear.
+ mStopFormatting = !(s.length() == 0);
+ return;
+ }
+ if (mSelfChange) {
+ // Ignore the change caused by s.replace().
+ return;
+ }
+ String formatted = reformat(s, Selection.getSelectionEnd(s));
+ if (formatted != null) {
+ int rememberedPos = formatted.length();
+ Log.v("rememberedPos", "" + rememberedPos);
+ mSelfChange = true;
+ s.replace(0, s.length(), formatted, 0, formatted.length());
+
+
+ // The text could be changed by other TextWatcher after we changed it. If we found the
+ // text is not the one we were expecting, just give up calling setSelection().
+ if (formatted.equals(s.toString())) {
+ Selection.setSelection(s, rememberedPos);
+ }
+ mSelfChange = false;
+ }
+ }
+
+ /**
+ * Generate the formatted number by ignoring all non-dialable chars and stick the cursor to the
+ * nearest dialable char to the left. For instance, if the number is (650) 123-45678 and '4' is
+ * removed then the cursor should be behind '3' instead of '-'.
+ */
+ private String reformat(CharSequence s, int cursor) {
+ // The index of char to the leftward of the cursor.
+ int curIndex = cursor - 1;
+ String formatted = null;
+ mFormatter.clear();
+ char lastNonSeparator = 0;
+ boolean hasCursor = false;
+
+ String countryCallingCode = this.code;
+ s = countryCallingCode + s;
+ int len = s.length();
+ for (int i = 0; i < len; i++) {
+ char c = s.charAt(i);
+ if (PhoneNumberUtils.isNonSeparator(c)) {
+ if (lastNonSeparator != 0) {
+ formatted = getFormattedNumber(lastNonSeparator, hasCursor);
+ hasCursor = false;
+ }
+ lastNonSeparator = c;
+ }
+ if (i == curIndex) {
+ hasCursor = true;
+ }
+ }
+ if (lastNonSeparator != 0) {
+ Log.v("lastNonSeparator", "" + lastNonSeparator);
+ formatted = getFormattedNumber(lastNonSeparator, hasCursor);
+ }
+
+ if (formatted.length() > countryCallingCode.length()) {
+ if (formatted.charAt(countryCallingCode.length()) == ' ')
+ return formatted.substring(countryCallingCode.length() + 1);
+ return formatted.substring(countryCallingCode.length());
+ }
+
+ return formatted.substring(formatted.length());
+ }
+
+ private String getFormattedNumber(char lastNonSeparator, boolean hasCursor) {
+
+ return hasCursor ? mFormatter.inputDigitAndRememberPosition(lastNonSeparator)
+ : mFormatter.inputDigit(lastNonSeparator);
+ }
+
+ private void stopFormatting() {
+
+ mStopFormatting = true;
+ mFormatter.clear();
+ }
+
+ private boolean hasSeparator(final CharSequence s, final int start, final int count) {
+
+ for (int i = start; i < start + count; i++) {
+ char c = s.charAt(i);
+ if (!PhoneNumberUtils.isNonSeparator(c)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+}
+
diff --git a/app/src/main/java/de/netalic/peacock/util/PhoneInfoUtils.kt b/app/src/main/java/de/netalic/peacock/util/PhoneInfoUtils.kt
new file mode 100644
index 0000000..a880a72
--- /dev/null
+++ b/app/src/main/java/de/netalic/peacock/util/PhoneInfoUtils.kt
@@ -0,0 +1,34 @@
+package de.netalic.peacock.util
+
+import android.content.Context
+import android.provider.Settings
+import java.security.MessageDigest
+
+
+class PhoneInfoUtils {
+
+ companion object {
+ private const val sSHA_1 = "SHA-1"
+ private const val sHEX_CHARS = "0123456789ABCDEF"
+
+ fun getPhoneUdid(context: Context): String {
+ val phoneId = Settings.System.getString(context.contentResolver, Settings.Secure.ANDROID_ID)
+
+ return hashString(sSHA_1, phoneId)
+ }
+
+ private fun hashString(type: String, input: String): String {
+ val bytes = MessageDigest.getInstance(type).digest(input.toByteArray())
+ val result = StringBuilder(bytes.size * 2)
+
+ bytes.forEach {
+ val i = it.toInt()
+ result.append(sHEX_CHARS[i shr 4 and 0x0f])
+ result.append(sHEX_CHARS[i and 0x0f])
+ }
+
+ return result.toString()
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/de/netalic/peacock/util/ValidatorUtils.kt b/app/src/main/java/de/netalic/peacock/util/ValidatorUtils.kt
new file mode 100644
index 0000000..3ce4533
--- /dev/null
+++ b/app/src/main/java/de/netalic/peacock/util/ValidatorUtils.kt
@@ -0,0 +1,13 @@
+package de.netalic.peacock.util
+
+class ValidatorUtils {
+
+ companion object {
+ //ToDo-tina min and max size of phone number
+ private val mPhonePatternMarcher = "[+0-9 ]{5,15}".toRegex()
+
+ fun phoneValidator(phoneNumber: String): Boolean {
+ return mPhonePatternMarcher.matches(phoneNumber)
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/res/drawable/all_buttonselector.xml b/app/src/main/res/drawable/all_buttonselector.xml
new file mode 100644
index 0000000..0709fdb
--- /dev/null
+++ b/app/src/main/res/drawable/all_buttonselector.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/codeverification_buttonselector.xml b/app/src/main/res/drawable/codeverification_buttonselector.xml
new file mode 100644
index 0000000..4903637
--- /dev/null
+++ b/app/src/main/res/drawable/codeverification_buttonselector.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/codeverification_buttontextcolorselector.xml b/app/src/main/res/drawable/codeverification_buttontextcolorselector.xml
new file mode 100644
index 0000000..25ddc61
--- /dev/null
+++ b/app/src/main/res/drawable/codeverification_buttontextcolorselector.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/codeverification_resendtextcolorselector.xml b/app/src/main/res/drawable/codeverification_resendtextcolorselector.xml
new file mode 100644
index 0000000..4a5a222
--- /dev/null
+++ b/app/src/main/res/drawable/codeverification_resendtextcolorselector.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/everywhere_colorprimarydarkbackgroundwithcornerradius.xml b/app/src/main/res/drawable/everywhere_colorprimarydarkbackgroundwithcornerradius.xml
new file mode 100644
index 0000000..5744224
--- /dev/null
+++ b/app/src/main/res/drawable/everywhere_colorprimarydarkbackgroundwithcornerradius.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/everywhere_colortertiarybackgroundwithcornerradius.xml b/app/src/main/res/drawable/everywhere_colortertiarybackgroundwithcornerradius.xml
new file mode 100644
index 0000000..802d56f
--- /dev/null
+++ b/app/src/main/res/drawable/everywhere_colortertiarybackgroundwithcornerradius.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/everywhere_icon.xml b/app/src/main/res/drawable/everywhere_icon.xml
new file mode 100644
index 0000000..0e30870
--- /dev/null
+++ b/app/src/main/res/drawable/everywhere_icon.xml
@@ -0,0 +1,6 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/everywhere_whitbackgroundwithcornerradius.xml b/app/src/main/res/drawable/everywhere_whitbackgroundwithcornerradius.xml
new file mode 100644
index 0000000..63c3282
--- /dev/null
+++ b/app/src/main/res/drawable/everywhere_whitbackgroundwithcornerradius.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_arrow_forward.xml b/app/src/main/res/drawable/ic_arrow_forward.xml
new file mode 100644
index 0000000..f233031
--- /dev/null
+++ b/app/src/main/res/drawable/ic_arrow_forward.xml
@@ -0,0 +1,4 @@
+
+
+
diff --git a/app/src/main/res/drawable/registration_buttonovalshapepurple.xml b/app/src/main/res/drawable/registration_buttonovalshapepurple.xml
new file mode 100644
index 0000000..1e9f1dc
--- /dev/null
+++ b/app/src/main/res/drawable/registration_buttonovalshapepurple.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/registration_buttonovalshapewhite.xml b/app/src/main/res/drawable/registration_buttonovalshapewhite.xml
new file mode 100644
index 0000000..fe6c3d0
--- /dev/null
+++ b/app/src/main/res/drawable/registration_buttonovalshapewhite.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/registration_circleshape.xml b/app/src/main/res/drawable/registration_circleshape.xml
new file mode 100644
index 0000000..6063613
--- /dev/null
+++ b/app/src/main/res/drawable/registration_circleshape.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/registration_cornershape.xml b/app/src/main/res/drawable/registration_cornershape.xml
new file mode 100644
index 0000000..aa08eed
--- /dev/null
+++ b/app/src/main/res/drawable/registration_cornershape.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/temp.xml b/app/src/main/res/drawable/temp.xml
new file mode 100644
index 0000000..3b80fe6
--- /dev/null
+++ b/app/src/main/res/drawable/temp.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/font/roboto_bold.ttf b/app/src/main/res/font/roboto_bold.ttf
new file mode 100644
index 0000000..e612852
Binary files /dev/null and b/app/src/main/res/font/roboto_bold.ttf differ
diff --git a/app/src/main/res/font/roboto_medium.ttf b/app/src/main/res/font/roboto_medium.ttf
new file mode 100644
index 0000000..86d1c52
Binary files /dev/null and b/app/src/main/res/font/roboto_medium.ttf differ
diff --git a/app/src/main/res/font/roboto_regular.ttf b/app/src/main/res/font/roboto_regular.ttf
new file mode 100644
index 0000000..cb8ffcf
Binary files /dev/null and b/app/src/main/res/font/roboto_regular.ttf differ
diff --git a/app/src/main/res/layout/activity_mainhost.xml b/app/src/main/res/layout/activity_mainhost.xml
new file mode 100644
index 0000000..8c01cfc
--- /dev/null
+++ b/app/src/main/res/layout/activity_mainhost.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_codeverification.xml b/app/src/main/res/layout/fragment_codeverification.xml
new file mode 100644
index 0000000..a2485ac
--- /dev/null
+++ b/app/src/main/res/layout/fragment_codeverification.xml
@@ -0,0 +1,147 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_patternlogin.xml b/app/src/main/res/layout/fragment_patternlogin.xml
new file mode 100644
index 0000000..8c643d9
--- /dev/null
+++ b/app/src/main/res/layout/fragment_patternlogin.xml
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_registration.xml b/app/src/main/res/layout/fragment_registration.xml
new file mode 100644
index 0000000..5e444d7
--- /dev/null
+++ b/app/src/main/res/layout/fragment_registration.xml
@@ -0,0 +1,98 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/navigation/graph_main.xml b/app/src/main/res/navigation/graph_main.xml
new file mode 100644
index 0000000..2ee21ca
--- /dev/null
+++ b/app/src/main/res/navigation/graph_main.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-v23/styles.xml b/app/src/main/res/values-v23/styles.xml
new file mode 100644
index 0000000..3fc33ac
--- /dev/null
+++ b/app/src/main/res/values-v23/styles.xml
@@ -0,0 +1,9 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index 69b2233..e84377c 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -1,6 +1,22 @@
- #008577
- #00574B
+ #F4F3F5
+ #E6E6E6
+ #FFFFFF
+
#D81B60
+
+ #7A6AE9
+ #D4D1F1
+
+ #000000
+
+ #00141E
+ #F2F1F6
+ #8B989F
+ #91999C
+
+ #F2F1F6
+
+
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..3fb67f4
--- /dev/null
+++ b/app/src/main/res/values/dimens.xml
@@ -0,0 +1,43 @@
+
+
+
+ 16sp
+ 16sp
+ 16sp
+ 14sp
+ 14sp
+ 12sp
+
+ 8dp
+ 16dp
+ 24dp
+ 32dp
+
+ 8dp
+
+ 0dp
+
+ 0dp
+ 90dp
+
+ 3
+
+ 25dp
+
+
+ 8dp
+ 5dp
+ 100dp
+ 28dp
+ 20dp
+ 10dp
+ 16dp
+ 30dp
+ 2dp
+ 4dp
+
+ 10dp
+
+ 20sp
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 2a2f6f0..9496d3c 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,3 +1,26 @@
-
+
Peacock
+ Authorization
+ Draw a sample pattern for more secure
+ Draw your pattern again
+ Switch to Password
+ -
+ Registration
+ Enter your phone number, we will send you OTP to verify later.
+ Continue
+ - - - - - - - - - -
+ (+98)
+ (%1$s)
+ Step %1$s/4
+
+ VALUE
+
+ Steps 3/4
+ Code Verification
+ Enter 6 digit number that sent to
+ Continue
+ Re_Send code
+ RESEND
+ in
+
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 5885930..d8146a9 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -1,11 +1,100 @@
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/test/java/de/netalic/peacock/ExampleUnitTest.kt b/app/src/test/java/de/netalic/peacock/ExampleUnitTest.kt
deleted file mode 100644
index 5935b93..0000000
--- a/app/src/test/java/de/netalic/peacock/ExampleUnitTest.kt
+++ /dev/null
@@ -1,17 +0,0 @@
-package de.netalic.peacock
-
-import org.junit.Test
-
-import org.junit.Assert.*
-
-/**
- * Example local unit test, which will execute on the development machine (host).
- *
- * See [testing documentation](http://d.android.com/tools/testing).
- */
-class ExampleUnitTest {
- @Test
- fun addition_isCorrect() {
- assertEquals(4, 2 + 2)
- }
-}
diff --git a/app/src/test/java/de/netalic/peacock/base/BaseTest.kt b/app/src/test/java/de/netalic/peacock/base/BaseTest.kt
new file mode 100644
index 0000000..0990899
--- /dev/null
+++ b/app/src/test/java/de/netalic/peacock/base/BaseTest.kt
@@ -0,0 +1,50 @@
+package de.netalic.peacock.base
+
+import io.reactivex.Scheduler
+import io.reactivex.android.plugins.RxAndroidPlugins
+import io.reactivex.disposables.Disposable
+import io.reactivex.internal.schedulers.ExecutorScheduler
+import io.reactivex.plugins.RxJavaPlugins
+import org.junit.AfterClass
+import org.junit.BeforeClass
+import java.util.concurrent.Executor
+import java.util.concurrent.TimeUnit
+
+open class BaseTest {
+
+ companion object {
+
+ @JvmStatic
+ @BeforeClass
+ fun setUpClass() {
+
+ val immediate = object : Scheduler() {
+ override fun scheduleDirect(run: Runnable, delay: Long, unit: TimeUnit): Disposable {
+ // this prevents StackOverflowErrors when scheduling with a delay
+ return super.scheduleDirect(run, 0, unit)
+ }
+
+ override fun createWorker(): Worker {
+ return ExecutorScheduler.ExecutorWorker(Executor { it.run() }, true)
+ }
+ }
+ RxJavaPlugins.setInitIoSchedulerHandler { scheduler -> immediate }
+ RxJavaPlugins.setInitComputationSchedulerHandler { scheduler -> immediate }
+ RxJavaPlugins.setInitNewThreadSchedulerHandler { scheduler -> immediate }
+ RxJavaPlugins.setInitSingleSchedulerHandler { scheduler -> immediate }
+ RxAndroidPlugins.setInitMainThreadSchedulerHandler { scheduler -> immediate }
+ }
+
+ @JvmStatic
+ @AfterClass
+ fun tearDownClass() {
+ RxJavaPlugins.reset()
+ RxAndroidPlugins.reset()
+ }
+
+ fun resetSchedulers() {
+ setUpClass()
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/app/src/test/java/de/netalic/peacock/data/repository/UserRepositoryTest.kt b/app/src/test/java/de/netalic/peacock/data/repository/UserRepositoryTest.kt
new file mode 100644
index 0000000..7bab5b8
--- /dev/null
+++ b/app/src/test/java/de/netalic/peacock/data/repository/UserRepositoryTest.kt
@@ -0,0 +1,69 @@
+package de.netalic.peacock.data.repository
+
+import de.netalic.peacock.base.BaseTest
+import de.netalic.peacock.data.model.UserModel
+import de.netalic.peacock.data.webservice.ApiInterface
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.MockitoAnnotations
+
+class UserRepositoryTest : BaseTest() {
+
+ val sUser = UserModel(
+
+ mName = "salimi",
+ mPhone = "+989211499302",
+ mUdid = "D89707AC55BAED9E8F23B826FB2A28E96095A190",
+ mFirebaseToken = "eyJ0eXAiOiAiSldUIiwgImFsZyI6ICJSUzI1NiIsICJraWQiOiAiODk0NTkyNzQzMzlkMzNlZmNmNTE3MDc" +
+ "4NGM5ZGU1MjUzMjEyOWVmZiJ9.eyJpc3MiOiAiZmlyZWJhc2UtYWRtaW5zZGstaXp1MTNAYWxwaGEtZDY0ZTQuaWFtLmd" +
+ "zZXJ2aWNlYWNjb3VudC5jb20iLCAic3ViIjogImZpcmViYXNlLWFkbWluc2RrLWl6dTEzQGFscGhhLWQ2NGU0LmlhbS5nc" +
+ "2VydmljZWFjY291bnQuY29tIiwgImF1ZCI6ICJodHRwczovL2lkZW50aXR5dG9vbGtpdC5nb29nbGVhcGlzLmNvbS9nb" +
+ "29nbGUuaWRlbnRpdHkuaWRlbnRpdHl0b29sa2l0LnYxLklkZW50aXR5VG9vbGtpdCIsICJ1aWQiOiAiKzk4OTIxMTQ5OTM" +
+ "wMiIsICJpYXQiOiAxNTYzOTYwNDU3LCAiZXhwIjogMTU2Mzk2NDA1N30.HOUVBzwbmGwsglQHukGwrijlUuSZ241KdN2Eo" +
+ "l3Gy80mmd4Kxoc58m3VhL71AWv3WS99eE7uz6xctl--yLPilhN3WJ_z2nxySqkhxiZ9OtaH_U8sTek63SJgfINeTFzJFp" +
+ "WHkT_DlQNPTVoH_AqbXjh0gZwdpVdMyoLmmuJf-WIqx2y7BdwudCTiAqY_RoK7DdDwS8Jf28J-czpWi7Q4neUo1pC0WLi" +
+ "986u9n0mZcfIhWoVB_fV0A2-fWRV6yhT647sfHntC2eSg-OJZKO-MAyBsgKDIZm_ubX7m3LHD6rahpnUHtY8m33eJyD-" +
+ "EfZcKboRWalJkmje69abirvep1A",
+ mActivateToken = "082016",
+ mDeviceType = "android"
+ )
+
+
+ private lateinit var mUserRepository: UserRepository
+
+ @Mock
+ private lateinit var apiInterface: ApiInterface
+
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ mUserRepository = UserRepository(apiInterface)
+ }
+
+ @After
+ fun tearDown() {
+ Mockito.reset(apiInterface)
+ }
+
+ @Test
+ fun claimUser_claimToApi() {
+ mUserRepository.claim(sUser.mPhone, sUser.mUdid)
+ Mockito.verify(apiInterface).claim(sUser.mPhone, sUser.mUdid)
+ }
+
+ @Test
+ fun bindUser_bindToApi() {
+
+ mUserRepository.bind(sUser)
+ Mockito.verify(apiInterface).bind(
+ sUser.mPhone, sUser.mUdid, sUser.mName!!, sUser.mDeviceType,
+ sUser.mFirebaseToken, sUser.mActivateToken
+ )
+
+ }
+
+}
diff --git a/app/src/test/java/de/netalic/peacock/ui/login/pattern/PatternViewModelTest.kt b/app/src/test/java/de/netalic/peacock/ui/login/pattern/PatternViewModelTest.kt
new file mode 100644
index 0000000..7671c2d
--- /dev/null
+++ b/app/src/test/java/de/netalic/peacock/ui/login/pattern/PatternViewModelTest.kt
@@ -0,0 +1,55 @@
+package de.netalic.peacock.ui.login.pattern
+
+import androidx.arch.core.executor.testing.InstantTaskExecutorRule
+import de.netalic.peacock.util.LiveDataTestUtil
+import org.junit.Assert
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+
+class PatternViewModelTest {
+
+ @get:Rule
+ var instantExecutorRule = InstantTaskExecutorRule()
+
+ private lateinit var mPatternViewModel: PatternViewModel
+
+ @Before
+ fun setUp() {
+ mPatternViewModel = PatternViewModel()
+ }
+
+ @Test
+ fun patternViewModel_drawPatternSuccess() {
+
+ val pattern = "123456"
+ mPatternViewModel.onPatternListener(pattern)
+ Assert.assertEquals(
+ LiveDataTestUtil.getValue(mPatternViewModel.getResponse()).data,
+ ResponseStatus.FIRST_SUCCESS
+ )
+ mPatternViewModel.onPatternListener(pattern)
+ Assert.assertEquals(
+ LiveDataTestUtil.getValue(mPatternViewModel.getResponse()).data,
+ ResponseStatus.SECOND_SUCCESS
+ )
+ }
+
+ @Test
+ fun patternViewModel_drawPatternFailed() {
+
+ val firstPattern = "123456"
+ val secondPattern = "654321"
+
+ mPatternViewModel.onPatternListener(firstPattern)
+ Assert.assertEquals(
+ LiveDataTestUtil.getValue(mPatternViewModel.getResponse()).data,
+ ResponseStatus.FIRST_SUCCESS
+ )
+ mPatternViewModel.onPatternListener(secondPattern)
+ Assert.assertEquals(
+ LiveDataTestUtil.getValue(mPatternViewModel.getResponse()).data,
+ ResponseStatus.FAILED
+ )
+ }
+}
\ No newline at end of file
diff --git a/app/src/test/java/de/netalic/peacock/ui/registration/CodeVerificationViewModelTest.kt b/app/src/test/java/de/netalic/peacock/ui/registration/CodeVerificationViewModelTest.kt
new file mode 100644
index 0000000..ff8230e
--- /dev/null
+++ b/app/src/test/java/de/netalic/peacock/ui/registration/CodeVerificationViewModelTest.kt
@@ -0,0 +1,285 @@
+package de.netalic.peacock.ui.registration
+
+import androidx.arch.core.executor.testing.InstantTaskExecutorRule
+import de.netalic.peacock.base.BaseTest
+import de.netalic.peacock.data.exception.ActivationCodeIsNotValid
+import de.netalic.peacock.data.exception.BadRequestException
+import de.netalic.peacock.data.exception.InvalidDeviceName
+import de.netalic.peacock.data.exception.InvalidUdidOrPhone
+import de.netalic.peacock.data.model.Status
+import de.netalic.peacock.data.model.UserModel
+import de.netalic.peacock.data.repository.UserRepository
+import de.netalic.peacock.util.LiveDataTestUtil
+import io.reactivex.Single
+import io.reactivex.plugins.RxJavaPlugins
+import io.reactivex.schedulers.TestScheduler
+import io.reactivex.subjects.PublishSubject
+import okhttp3.MediaType
+import okhttp3.ResponseBody
+import org.junit.Assert
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.MockitoAnnotations
+import retrofit2.Response
+import java.util.concurrent.TimeUnit
+
+
+class CodeVerificationViewModelTest : BaseTest() {
+
+
+ @get:Rule
+ val instantExecutorRule = InstantTaskExecutorRule()
+
+ @Mock
+ private lateinit var mUserRepository: UserRepository
+
+ private lateinit var mCodeVerificationViewModel: CodeVerificationViewModel
+
+ private val mResponseBody = ResponseBody.create(
+ MediaType.parse("text/plain"), ""
+ )
+ private val mUser = UserModel(
+
+ mName = "salimi",
+ mPhone = "+989211499302",
+ mUdid = "D89707AC55BAED9E8F23B826FB2A28E96095A190",
+ mFirebaseToken = "eyJ0eXAiOiAiSldUIiwgImFsZyI6ICJSUzI1NiIsICJraWQiOiAiODk0NTkyNzQzMzlkMzNlZmNmNTE3MDc" +
+ "4NGM5ZGU1MjUzMjEyOWVmZiJ9.eyJpc3MiOiAiZmlyZWJhc2UtYWRtaW5zZGstaXp1MTNAYWxwaGEtZDY0ZTQuaWFtLmd" +
+ "zZXJ2aWNlYWNjb3VudC5jb20iLCAic3ViIjogImZpcmViYXNlLWFkbWluc2RrLWl6dTEzQGFscGhhLWQ2NGU0LmlhbS5nc" +
+ "2VydmljZWFjY291bnQuY29tIiwgImF1ZCI6ICJodHRwczovL2lkZW50aXR5dG9vbGtpdC5nb29nbGVhcGlzLmNvbS9nb" +
+ "29nbGUuaWRlbnRpdHkuaWRlbnRpdHl0b29sa2l0LnYxLklkZW50aXR5VG9vbGtpdCIsICJ1aWQiOiAiKzk4OTIxMTQ5OTM" +
+ "wMiIsICJpYXQiOiAxNTYzOTYwNDU3LCAiZXhwIjogMTU2Mzk2NDA1N30.HOUVBzwbmGwsglQHukGwrijlUuSZ241KdN2Eo" +
+ "l3Gy80mmd4Kxoc58m3VhL71AWv3WS99eE7uz6xctl--yLPilhN3WJ_z2nxySqkhxiZ9OtaH_U8sTek63SJgfINeTFzJFp" +
+ "WHkT_DlQNPTVoH_AqbXjh0gZwdpVdMyoLmmuJf-WIqx2y7BdwudCTiAqY_RoK7DdDwS8Jf28J-czpWi7Q4neUo1pC0WLi" +
+ "986u9n0mZcfIhWoVB_fV0A2-fWRV6yhT647sfHntC2eSg-OJZKO-MAyBsgKDIZm_ubX7m3LHD6rahpnUHtY8m33eJyD-" +
+ "EfZcKboRWalJkmje69abirvep1A",
+ mActivateToken = "082016",
+ mDeviceType = "android"
+ )
+
+ @Before
+ fun setup() {
+
+ MockitoAnnotations.initMocks(this)
+ mCodeVerificationViewModel = CodeVerificationViewModel(mUserRepository)
+
+ }
+
+ @Test
+ fun binUser_showSuccess() {
+
+ Mockito.`when`(mUserRepository.bind(mUser)).thenReturn(Single.just(Response.success(mResponseBody)))
+ mCodeVerificationViewModel.bind(mUser)
+ Mockito.verify(mUserRepository).bind(mUser)
+ Assert.assertEquals(
+ LiveDataTestUtil.getValue(mCodeVerificationViewModel.getBindLiveData()).status,
+ Status.SUCCESS
+ )
+
+ Assert.assertEquals(LiveDataTestUtil.getValue(mCodeVerificationViewModel.getBindLiveData()).data, mResponseBody)
+ Assert.assertNull(LiveDataTestUtil.getValue(mCodeVerificationViewModel.getBindLiveData()).throwable)
+ }
+
+ @Test
+ fun bindUser_showBadRequest() {
+
+ val delayer = PublishSubject.create()
+
+ val singleResponse = Single.just(
+ Response.error(400, mResponseBody)
+ ).delaySubscription(delayer)
+
+ Mockito.`when`(mUserRepository.bind(mUser)).thenReturn(singleResponse)
+
+ mCodeVerificationViewModel.bind(mUser)
+
+ Mockito.verify(mUserRepository).bind(mUser)
+
+ Assert.assertEquals(
+ LiveDataTestUtil.getValue(mCodeVerificationViewModel.getBindLiveData()).status,
+ Status.LOADING
+ )
+
+ delayer.onComplete()
+
+ Assert.assertEquals(
+ LiveDataTestUtil.getValue(mCodeVerificationViewModel.getBindLiveData()).status,
+ Status.FAILED
+ )
+
+ Assert.assertEquals(
+ LiveDataTestUtil.getValue(mCodeVerificationViewModel.getBindLiveData())
+ .throwable!!::class.java, BadRequestException::class.java
+ )
+
+ Assert.assertEquals(
+ LiveDataTestUtil.getValue(mCodeVerificationViewModel.getBindLiveData()).data,
+ null
+ )
+ }
+
+ @Test
+ fun bindUser_invalidUdidOrPhone() {
+
+ val delayer = PublishSubject.create()
+
+ val singleResponse = Single.just(
+ Response.error(710, mResponseBody)
+ ).delaySubscription(delayer)
+
+ Mockito.`when`(mUserRepository.bind(mUser)).thenReturn(singleResponse)
+
+ mCodeVerificationViewModel.bind(mUser)
+
+ Mockito.verify(mUserRepository).bind(mUser)
+
+ Assert.assertEquals(
+ LiveDataTestUtil.getValue(mCodeVerificationViewModel.getBindLiveData()).status,
+ Status.LOADING
+ )
+
+ delayer.onComplete()
+
+ Assert.assertEquals(
+ LiveDataTestUtil.getValue(mCodeVerificationViewModel.getBindLiveData()).status,
+ Status.FAILED
+ )
+
+ Assert.assertEquals(
+ LiveDataTestUtil.getValue(mCodeVerificationViewModel.getBindLiveData())
+ .throwable!!::class.java, InvalidUdidOrPhone::class.java
+ )
+
+ Assert.assertEquals(
+ LiveDataTestUtil.getValue(mCodeVerificationViewModel.getBindLiveData()).data,
+ null
+ )
+ }
+
+ @Test
+ fun bindUser_invalidDeviceName() {
+
+ val delayer = PublishSubject.create()
+
+ val singleResponse = Single.just(
+ Response.error(716, mResponseBody)
+ ).delaySubscription(delayer)
+
+ Mockito.`when`(mUserRepository.bind(mUser)).thenReturn(singleResponse)
+
+ mCodeVerificationViewModel.bind(mUser)
+
+ Mockito.verify(mUserRepository).bind(mUser)
+
+ Assert.assertEquals(
+ LiveDataTestUtil.getValue(mCodeVerificationViewModel.getBindLiveData()).status,
+ Status.LOADING
+ )
+
+ delayer.onComplete()
+
+ Assert.assertEquals(
+ LiveDataTestUtil.getValue(mCodeVerificationViewModel.getBindLiveData()).status,
+ Status.FAILED
+ )
+
+ Assert.assertEquals(
+ LiveDataTestUtil.getValue(mCodeVerificationViewModel.getBindLiveData())
+ .throwable!!::class.java, InvalidDeviceName::class.java
+ )
+
+ Assert.assertEquals(
+ LiveDataTestUtil.getValue(mCodeVerificationViewModel.getBindLiveData()).data,
+ null
+ )
+ }
+
+ @Test
+ fun bindUser_activationCodeIsNotValid() {
+
+ val delayer = PublishSubject.create()
+
+ val singleResponse = Single.just(
+ Response.error(711, mResponseBody)
+ ).delaySubscription(delayer)
+
+ Mockito.`when`(mUserRepository.bind(mUser)).thenReturn(singleResponse)
+
+ mCodeVerificationViewModel.bind(mUser)
+
+ Mockito.verify(mUserRepository).bind(mUser)
+
+ Assert.assertEquals(
+ LiveDataTestUtil.getValue(mCodeVerificationViewModel.getBindLiveData()).status,
+ Status.LOADING
+ )
+
+ delayer.onComplete()
+
+ Assert.assertEquals(
+ LiveDataTestUtil.getValue(mCodeVerificationViewModel.getBindLiveData()).status,
+ Status.FAILED
+ )
+
+ Assert.assertEquals(
+ LiveDataTestUtil.getValue(mCodeVerificationViewModel.getBindLiveData())
+ .throwable!!::class.java, ActivationCodeIsNotValid::class.java
+ )
+
+ Assert.assertEquals(
+ LiveDataTestUtil.getValue(mCodeVerificationViewModel.getBindLiveData()).data,
+ null
+ )
+ }
+
+ @Test
+ fun setTimer() {
+
+ val testScheduler = TestScheduler()
+
+ RxJavaPlugins.setComputationSchedulerHandler { scheduler -> testScheduler }
+
+ mCodeVerificationViewModel.setTimer(3)
+
+ testScheduler.advanceTimeBy(1, TimeUnit.SECONDS)
+
+ Assert.assertEquals(
+ LiveDataTestUtil.getValue(mCodeVerificationViewModel.getTimerLiveData()).data.toString(),
+ String.format("%02d:%02d ", 0, 3)
+ )
+ Assert.assertEquals(
+ LiveDataTestUtil.getValue(mCodeVerificationViewModel.getTimerLiveData()).status,
+ Status.SUCCESS
+ )
+ Assert.assertNull(LiveDataTestUtil.getValue(mCodeVerificationViewModel.getTimerLiveData()).throwable)
+
+ testScheduler.advanceTimeBy(1, TimeUnit.SECONDS)
+
+ Assert.assertEquals(
+ LiveDataTestUtil.getValue(mCodeVerificationViewModel.getTimerLiveData()).data.toString(),
+ String.format("%02d:%02d ", 0, 2)
+ )
+ Assert.assertEquals(
+ LiveDataTestUtil.getValue(mCodeVerificationViewModel.getTimerLiveData()).status,
+ Status.SUCCESS
+ )
+ Assert.assertNull(LiveDataTestUtil.getValue(mCodeVerificationViewModel.getTimerLiveData()).throwable)
+
+ testScheduler.advanceTimeBy(1, TimeUnit.SECONDS)
+
+ Assert.assertEquals(
+ LiveDataTestUtil.getValue(mCodeVerificationViewModel.getTimerLiveData()).data.toString(),
+ "RESEND"
+ )
+ Assert.assertEquals(
+ LiveDataTestUtil.getValue(mCodeVerificationViewModel.getTimerLiveData()).status,
+ Status.SUCCESS
+ )
+ Assert.assertNull(LiveDataTestUtil.getValue(mCodeVerificationViewModel.getTimerLiveData()).throwable)
+
+ resetSchedulers()
+ }
+}
\ No newline at end of file
diff --git a/app/src/test/java/de/netalic/peacock/ui/registration/PhoneInputViewModelTest.kt b/app/src/test/java/de/netalic/peacock/ui/registration/PhoneInputViewModelTest.kt
new file mode 100644
index 0000000..0bb3087
--- /dev/null
+++ b/app/src/test/java/de/netalic/peacock/ui/registration/PhoneInputViewModelTest.kt
@@ -0,0 +1,223 @@
+package de.netalic.peacock.ui.registration
+
+import androidx.arch.core.executor.testing.InstantTaskExecutorRule
+import de.netalic.peacock.base.BaseTest
+import de.netalic.peacock.data.exception.BadRequestException
+import de.netalic.peacock.data.exception.InvalidPhoneNumberException
+import de.netalic.peacock.data.exception.InvalidUdidOrPhoneException
+import de.netalic.peacock.data.exception.ServerException
+import de.netalic.peacock.data.model.Status
+import de.netalic.peacock.data.model.UserModel
+import de.netalic.peacock.data.repository.UserRepository
+import de.netalic.peacock.util.LiveDataTestUtil
+import io.reactivex.Single
+import io.reactivex.subjects.PublishSubject
+import okhttp3.MediaType
+import okhttp3.ResponseBody
+import org.junit.*
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.MockitoAnnotations
+import retrofit2.Response
+
+class PhoneInputViewModelTest : BaseTest() {
+
+
+ companion object {
+ val sUser = UserModel(
+
+ mName = "salimi",
+ mPhone = "+989211499302",
+ mUdid = "D89707AC55BAED9E8F23B826FB2A28E96095A190",
+ mFirebaseToken = "eyJ0eXAiOiAiSldUIiwgImFsZyI6ICJSUzI1NiIsICJraWQiOiAiODk0NTkyNzQzMzlkMzNlZmNmNTE3MDc" +
+ "4NGM5ZGU1MjUzMjEyOWVmZiJ9.eyJpc3MiOiAiZmlyZWJhc2UtYWRtaW5zZGstaXp1MTNAYWxwaGEtZDY0ZTQuaWFtLmd" +
+ "zZXJ2aWNlYWNjb3VudC5jb20iLCAic3ViIjogImZpcmViYXNlLWFkbWluc2RrLWl6dTEzQGFscGhhLWQ2NGU0LmlhbS5nc" +
+ "2VydmljZWFjY291bnQuY29tIiwgImF1ZCI6ICJodHRwczovL2lkZW50aXR5dG9vbGtpdC5nb29nbGVhcGlzLmNvbS9nb" +
+ "29nbGUuaWRlbnRpdHkuaWRlbnRpdHl0b29sa2l0LnYxLklkZW50aXR5VG9vbGtpdCIsICJ1aWQiOiAiKzk4OTIxMTQ5OTM" +
+ "wMiIsICJpYXQiOiAxNTYzOTYwNDU3LCAiZXhwIjogMTU2Mzk2NDA1N30.HOUVBzwbmGwsglQHukGwrijlUuSZ241KdN2Eo" +
+ "l3Gy80mmd4Kxoc58m3VhL71AWv3WS99eE7uz6xctl--yLPilhN3WJ_z2nxySqkhxiZ9OtaH_U8sTek63SJgfINeTFzJFp" +
+ "WHkT_DlQNPTVoH_AqbXjh0gZwdpVdMyoLmmuJf-WIqx2y7BdwudCTiAqY_RoK7DdDwS8Jf28J-czpWi7Q4neUo1pC0WLi" +
+ "986u9n0mZcfIhWoVB_fV0A2-fWRV6yhT647sfHntC2eSg-OJZKO-MAyBsgKDIZm_ubX7m3LHD6rahpnUHtY8m33eJyD-" +
+ "EfZcKboRWalJkmje69abirvep1A",
+ mActivateToken = "082016",
+ mDeviceType = "android"
+ )
+ val wrongUserPhone = UserModel(
+ "",
+ "salimi",
+ "a89211499302",
+ "D89707AC55BAED9E8F23B826FB2A28E96095A190",
+ "eyJ0eXAiOiAiSldUIiwgImFsZyI6ICJSUzI1NiIsICJraWQiOiAiODk0NTkyNzQzMzlkMzNlZmNmNTE3MDc" +
+ "4NGM5ZGU1MjUzMjEyOWVmZiJ9.eyJpc3MiOiAiZmlyZWJhc2UtYWRtaW5zZGstaXp1MTNAYWxwaGEtZDY0ZTQuaWFtLmd" +
+ "zZXJ2aWNlYWNjb3VudC5jb20iLCAic3ViIjogImZpcmViYXNlLWFkbWluc2RrLWl6dTEzQGFscGhhLWQ2NGU0LmlhbS5nc" +
+ "2VydmljZWFjY291bnQuY29tIiwgImF1ZCI6ICJodHRwczovL2lkZW50aXR5dG9vbGtpdC5nb29nbGVhcGlzLmNvbS9nb" +
+ "29nbGUuaWRlbnRpdHkuaWRlbnRpdHl0b29sa2l0LnYxLklkZW50aXR5VG9vbGtpdCIsICJ1aWQiOiAiKzk4OTIxMTQ5OTM" +
+ "wMiIsICJpYXQiOiAxNTYzOTYwNDU3LCAiZXhwIjogMTU2Mzk2NDA1N30.HOUVBzwbmGwsglQHukGwrijlUuSZ241KdN2Eo" +
+ "l3Gy80mmd4Kxoc58m3VhL71AWv3WS99eE7uz6xctl--yLPilhN3WJ_z2nxySqkhxiZ9OtaH_U8sTek63SJgfINeTFzJFp" +
+ "WHkT_DlQNPTVoH_AqbXjh0gZwdpVdMyoLmmuJf-WIqx2y7BdwudCTiAqY_RoK7DdDwS8Jf28J-czpWi7Q4neUo1pC0WLi" +
+ "986u9n0mZcfIhWoVB_fV0A2-fWRV6yhT647sfHntC2eSg-OJZKO-MAyBsgKDIZm_ubX7m3LHD6rahpnUHtY8m33eJyD-" +
+ "EfZcKboRWalJkmje69abirvep1A",
+ "082016",
+ "android"
+ )
+ }
+
+ @get:Rule
+ var instantExecutorRule = InstantTaskExecutorRule()
+
+ @Mock
+ private lateinit var mUserRepository: UserRepository
+
+ private lateinit var mRegistrationViewModel: RegistrationViewModel
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ mRegistrationViewModel = RegistrationViewModel(mUserRepository)
+ }
+
+ @After
+ fun tearDown() {
+ Mockito.reset(mUserRepository)
+ }
+
+ @Test
+ fun claim_showSuccess() {
+ val delayer = PublishSubject.create()
+
+ val singleResponse = Single.just(
+ Response.success(200, sUser)
+ ).delaySubscription(delayer)
+ Mockito.`when`(mUserRepository.claim(sUser.mPhone, sUser.mUdid)).thenReturn(singleResponse)
+ mRegistrationViewModel.claim(sUser.mPhone, sUser.mUdid)
+ Mockito.verify(mUserRepository).claim(sUser.mPhone, sUser.mUdid)
+ Assert.assertEquals(
+ LiveDataTestUtil.getValue(mRegistrationViewModel.getClaimLiveData()).status,
+ Status.LOADING
+ )
+ delayer.onComplete()
+ Assert.assertEquals(LiveDataTestUtil.getValue(mRegistrationViewModel.getClaimLiveData()).status, Status.SUCCESS)
+ Assert.assertEquals(LiveDataTestUtil.getValue(mRegistrationViewModel.getClaimLiveData()).data, sUser)
+ Assert.assertNull(LiveDataTestUtil.getValue(mRegistrationViewModel.getClaimLiveData()).throwable)
+
+ }
+
+ @Test
+ fun claimUser_showBadRequestException() {
+ val delayer = PublishSubject.create()
+
+ val singleResponse = Single.just(
+ Response.error(
+ 400,
+ ResponseBody.create(MediaType.parse("text/plain"), "")
+ )
+ )
+ .delaySubscription(delayer)
+ Mockito.`when`(mUserRepository.claim(sUser.mPhone, sUser.mUdid)).thenReturn(singleResponse)
+ mRegistrationViewModel.claim(sUser.mPhone, sUser.mUdid)
+ Mockito.verify(mUserRepository).claim(sUser.mPhone, sUser.mUdid)
+ Assert.assertEquals(
+ LiveDataTestUtil.getValue(mRegistrationViewModel.getClaimLiveData()).status,
+ Status.LOADING
+ )
+ delayer.onComplete()
+ Assert.assertEquals(LiveDataTestUtil.getValue(mRegistrationViewModel.getClaimLiveData()).status, Status.FAILED)
+ Assert.assertEquals(
+ LiveDataTestUtil.getValue(mRegistrationViewModel.getClaimLiveData()).throwable!!::class.java,
+ BadRequestException::class.java
+ )
+ Assert.assertNull(LiveDataTestUtil.getValue(mRegistrationViewModel.getClaimLiveData()).data)
+ }
+
+ @Test
+ fun claimUser_showServerException() {
+ val delayer = PublishSubject.create()
+
+ val singleResponse = Single.just(
+ Response.error(
+ 500,
+ ResponseBody.create(MediaType.parse("text/plain"), "")
+ )
+ )
+ .delaySubscription(delayer)
+ Mockito.`when`(mUserRepository.claim(sUser.mPhone, sUser.mUdid)).thenReturn(singleResponse)
+ mRegistrationViewModel.claim(sUser.mPhone, sUser.mUdid)
+ Mockito.verify(mUserRepository).claim(sUser.mPhone, sUser.mUdid)
+ Assert.assertEquals(
+ LiveDataTestUtil.getValue(mRegistrationViewModel.getClaimLiveData()).status,
+ Status.LOADING
+ )
+ delayer.onComplete()
+ Assert.assertEquals(LiveDataTestUtil.getValue(mRegistrationViewModel.getClaimLiveData()).status, Status.FAILED)
+ Assert.assertEquals(
+ LiveDataTestUtil.getValue(mRegistrationViewModel.getClaimLiveData()).throwable!!::class.java,
+ ServerException::class.java
+ )
+ Assert.assertNull(LiveDataTestUtil.getValue(mRegistrationViewModel.getClaimLiveData()).data)
+ }
+
+
+ @Test
+ fun claimUser_showInvalidmUdidOrmPhoneException() {
+ val delayer = PublishSubject.create()
+
+ val singleResponse = Single.just(
+ Response.error(
+ 710,
+ ResponseBody.create(MediaType.parse("text/plain"), "")
+ )
+ )
+ .delaySubscription(delayer)
+ Mockito.`when`(mUserRepository.claim(sUser.mPhone, sUser.mUdid)).thenReturn(singleResponse)
+ mRegistrationViewModel.claim(sUser.mPhone, sUser.mUdid)
+ Mockito.verify(mUserRepository).claim(sUser.mPhone, sUser.mUdid)
+ Assert.assertEquals(
+ LiveDataTestUtil.getValue(mRegistrationViewModel.getClaimLiveData()).status,
+ Status.LOADING
+ )
+ delayer.onComplete()
+ Assert.assertEquals(LiveDataTestUtil.getValue(mRegistrationViewModel.getClaimLiveData()).status, Status.FAILED)
+ Assert.assertEquals(
+ LiveDataTestUtil.getValue(mRegistrationViewModel.getClaimLiveData()).throwable!!::class.java,
+ InvalidUdidOrPhoneException::class.java
+ )
+ Assert.assertNull(LiveDataTestUtil.getValue(mRegistrationViewModel.getClaimLiveData()).data)
+ }
+
+ @Test
+ fun claimUser_throwException() {
+ val delayer = PublishSubject.create()
+
+ val singleResponse = Single.error>(Exception())
+ .delaySubscription(delayer)
+
+ Mockito.`when`(mUserRepository.claim(sUser.mPhone, sUser.mUdid)).thenReturn(singleResponse)
+ mRegistrationViewModel.claim(sUser.mPhone, sUser.mUdid)
+ Mockito.verify(mUserRepository).claim(sUser.mPhone, sUser.mUdid)
+ Assert.assertEquals(
+ LiveDataTestUtil.getValue(mRegistrationViewModel.getClaimLiveData()).status,
+ Status.LOADING
+ )
+ delayer.onComplete()
+
+ Assert.assertEquals(LiveDataTestUtil.getValue(mRegistrationViewModel.getClaimLiveData()).status, Status.FAILED)
+ Assert.assertEquals(
+ LiveDataTestUtil.getValue(mRegistrationViewModel.getClaimLiveData()).throwable!!::class.java,
+ Exception::class.java
+ )
+ Assert.assertNull(LiveDataTestUtil.getValue(mRegistrationViewModel.getClaimLiveData()).data)
+ }
+
+ @Test
+ fun phoneValidator_invalidPhoneNumber() {
+
+ mRegistrationViewModel.claim(wrongUserPhone.mPhone, wrongUserPhone.mUdid)
+ Assert.assertEquals(LiveDataTestUtil.getValue(mRegistrationViewModel.getClaimLiveData()).status, Status.FAILED)
+ Assert.assertEquals(
+ LiveDataTestUtil.getValue(mRegistrationViewModel.getClaimLiveData()).throwable!!::class.java,
+ InvalidPhoneNumberException::class.java
+ )
+ Assert.assertNull(LiveDataTestUtil.getValue(mRegistrationViewModel.getClaimLiveData()).data)
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/test/java/de/netalic/peacock/util/LiveDataUtil.kt b/app/src/test/java/de/netalic/peacock/util/LiveDataUtil.kt
new file mode 100644
index 0000000..e4b9ef2
--- /dev/null
+++ b/app/src/test/java/de/netalic/peacock/util/LiveDataUtil.kt
@@ -0,0 +1,29 @@
+package de.netalic.peacock.util
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.Observer
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+
+object LiveDataTestUtil {
+
+ /**
+ * Get the value from a LiveData object. We're waiting for LiveData to emit, for 2 seconds.
+ * Once we got a notification via onChanged, we stop observing.
+ */
+ @Throws(InterruptedException::class)
+ fun getValue(liveData: LiveData): T {
+ val data = arrayOfNulls(1)
+ val latch = CountDownLatch(1)
+ val observer = object : Observer {
+ override fun onChanged(o: T?) {
+ data[0] = o
+ latch.countDown()
+ liveData.removeObserver(this)
+ }
+ }
+ liveData.observeForever(observer)
+ latch.await(2, TimeUnit.SECONDS)
+ return data[0] as T
+ }
+}
diff --git a/build.gradle b/build.gradle
index 438d280..27aa807 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,5 +1,3 @@
-// Top-level build file where you can add configuration options common to all sub-projects/modules.
-
buildscript {
ext.kotlin_version = '1.3.41'
repositories {
@@ -10,8 +8,7 @@ buildscript {
dependencies {
classpath 'com.android.tools.build:gradle:3.4.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
- // NOTE: Do not place your application dependencies here; they belong
- // in the individual module build.gradle files
+ classpath 'androidx.navigation:navigation-safe-args-gradle-plugin:2.1.0-beta02'
}
}
@@ -19,10 +16,11 @@ allprojects {
repositories {
google()
jcenter()
-
+ maven { url 'https://jitpack.io' }
}
}
+
task clean(type: Delete) {
delete rootProject.buildDir
-}
+}
\ No newline at end of file