From d538011677d0e41443e6a67606cc23d4d23f17ca Mon Sep 17 00:00:00 2001 From: hmalik144 Date: Sat, 8 Feb 2020 17:59:37 +0000 Subject: [PATCH] Initial commit --- .gitignore | 14 + .idea/codeStyles/Project.xml | 125 ++++++++ .idea/codeStyles/codeStyleConfig.xml | 5 + .idea/dictionaries/h_mal.xml | 3 + .idea/gradle.xml | 19 ++ .idea/misc.xml | 17 + .idea/runConfigurations.xml | 12 + app/.gitignore | 1 + app/build.gradle | 65 ++++ app/google-services.json | 48 +++ app/proguard-rules.pro | 21 ++ .../ExampleInstrumentedTest.kt | 24 ++ .../ui/main/TestUserLogingTest.kt | 292 ++++++++++++++++++ app/src/main/AndroidManifest.xml | 28 ++ .../firebase_mobile_signin_app/AppClass.kt | 34 ++ .../data/firebase/FirebaseSource.kt | 33 ++ .../data/network/MyApi.kt | 44 +++ .../network/NetworkConnectionInterceptor.kt | 38 +++ .../data/network/SafeApiRequest.kt | 29 ++ .../data/network/model/ResponseObject.kt | 7 + .../data/network/model/User.kt | 9 + .../data/repository/Repository.kt | 38 +++ .../ui/CompletionListener.kt | 8 + .../ui/details/DetailsUpdateActivity.kt | 161 ++++++++++ .../ui/details/DetailsViewModel.kt | 119 +++++++ .../ui/details/DetailsViewModelFactory.kt | 15 + .../ui/main/MainActivity.kt | 66 ++++ .../ui/main/MainViewModel.kt | 90 ++++++ .../ui/main/MainViewModelFactory.kt | 15 + .../ui/main/SignUpFragment.kt | 30 ++ .../ui/main/VerifyFragment.kt | 35 +++ .../drawable-v24/ic_launcher_foreground.xml | 34 ++ .../res/drawable/ic_launcher_background.xml | 170 ++++++++++ .../res/layout/activity_details_update.xml | 102 ++++++ app/src/main/res/layout/fragment_sign_up.xml | 85 +++++ app/src/main/res/layout/main_activity.xml | 29 ++ app/src/main/res/layout/main_fragment.xml | 72 +++++ app/src/main/res/menu/menu.xml | 7 + .../res/mipmap-anydpi-v26/ic_launcher.xml | 5 + .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 + app/src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 2963 bytes .../res/mipmap-hdpi/ic_launcher_round.png | Bin 0 -> 4905 bytes app/src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 2060 bytes .../res/mipmap-mdpi/ic_launcher_round.png | Bin 0 -> 2783 bytes app/src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 4490 bytes .../res/mipmap-xhdpi/ic_launcher_round.png | Bin 0 -> 6895 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 6387 bytes .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin 0 -> 10413 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 9128 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 0 -> 15132 bytes app/src/main/res/values/colors.xml | 6 + app/src/main/res/values/dimens.xml | 5 + app/src/main/res/values/strings.xml | 15 + app/src/main/res/values/styles.xml | 11 + .../ExampleUnitTest.kt | 17 + build.gradle | 29 ++ gradle.properties | 21 ++ gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 54329 bytes gradle/wrapper/gradle-wrapper.properties | 6 + gradlew | 172 +++++++++++ gradlew.bat | 84 +++++ settings.gradle | 2 + 62 files changed, 2322 insertions(+) create mode 100644 .gitignore create mode 100644 .idea/codeStyles/Project.xml create mode 100644 .idea/codeStyles/codeStyleConfig.xml create mode 100644 .idea/dictionaries/h_mal.xml create mode 100644 .idea/gradle.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/runConfigurations.xml create mode 100644 app/.gitignore create mode 100644 app/build.gradle create mode 100644 app/google-services.json create mode 100644 app/proguard-rules.pro create mode 100644 app/src/androidTest/java/com/example/h_mal/firebase_mobile_signin_app/ExampleInstrumentedTest.kt create mode 100644 app/src/androidTest/java/com/example/h_mal/firebase_mobile_signin_app/ui/main/TestUserLogingTest.kt create mode 100644 app/src/main/AndroidManifest.xml create mode 100644 app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/AppClass.kt create mode 100644 app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/data/firebase/FirebaseSource.kt create mode 100644 app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/data/network/MyApi.kt create mode 100644 app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/data/network/NetworkConnectionInterceptor.kt create mode 100644 app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/data/network/SafeApiRequest.kt create mode 100644 app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/data/network/model/ResponseObject.kt create mode 100644 app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/data/network/model/User.kt create mode 100644 app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/data/repository/Repository.kt create mode 100644 app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/ui/CompletionListener.kt create mode 100644 app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/ui/details/DetailsUpdateActivity.kt create mode 100644 app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/ui/details/DetailsViewModel.kt create mode 100644 app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/ui/details/DetailsViewModelFactory.kt create mode 100644 app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/ui/main/MainActivity.kt create mode 100644 app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/ui/main/MainViewModel.kt create mode 100644 app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/ui/main/MainViewModelFactory.kt create mode 100644 app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/ui/main/SignUpFragment.kt create mode 100644 app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/ui/main/VerifyFragment.kt create mode 100644 app/src/main/res/drawable-v24/ic_launcher_foreground.xml create mode 100644 app/src/main/res/drawable/ic_launcher_background.xml create mode 100644 app/src/main/res/layout/activity_details_update.xml create mode 100644 app/src/main/res/layout/fragment_sign_up.xml create mode 100644 app/src/main/res/layout/main_activity.xml create mode 100644 app/src/main/res/layout/main_fragment.xml create mode 100644 app/src/main/res/menu/menu.xml create mode 100644 app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml create mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher_round.png create mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher_round.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher_round.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png create mode 100644 app/src/main/res/values/colors.xml create mode 100644 app/src/main/res/values/dimens.xml create mode 100644 app/src/main/res/values/strings.xml create mode 100644 app/src/main/res/values/styles.xml create mode 100644 app/src/test/java/com/example/h_mal/firebase_mobile_signin_app/ExampleUnitTest.kt create mode 100644 build.gradle create mode 100644 gradle.properties create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100644 gradlew create mode 100644 gradlew.bat create mode 100644 settings.gradle diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..603b140 --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..45b5654 --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,125 @@ + + + + + + + + + + + +
+ + + + xmlns:android + + ^$ + + + +
+
+ + + + xmlns:.* + + ^$ + + + BY_NAME + +
+
+ + + + .*:id + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + .*:name + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + name + + ^$ + + + +
+
+ + + + style + + ^$ + + + +
+
+ + + + .* + + ^$ + + + BY_NAME + +
+
+ + + + .* + + http://schemas.android.com/apk/res/android + + + ANDROID_ATTRIBUTE_ORDER + +
+
+ + + + .* + + .* + + + BY_NAME + +
+
+
+
+ + +
+
\ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..79ee123 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/dictionaries/h_mal.xml b/.idea/dictionaries/h_mal.xml new file mode 100644 index 0000000..f32ad56 --- /dev/null +++ b/.idea/dictionaries/h_mal.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..169fd0d --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,19 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..d5727af --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml new file mode 100644 index 0000000..7f68460 --- /dev/null +++ b/.idea/runConfigurations.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..8425288 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,65 @@ +apply plugin: 'com.android.application' + +apply plugin: 'kotlin-android' + +apply plugin: 'kotlin-android-extensions' + +apply plugin: 'com.google.gms.google-services' + +//kotlin kapt plugin +apply plugin: 'kotlin-kapt' + +android { + compileSdkVersion 29 + defaultConfig { + applicationId "com.example.h_mal.firebase_mobile_signin_app" + minSdkVersion 19 + targetSdkVersion 29 + versionCode 1 + versionName "1.0" + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + dataBinding { + enabled = true + } +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + implementation 'androidx.appcompat:appcompat:1.1.0' + implementation 'androidx.core:core-ktx:1.2.0' + implementation 'androidx.constraintlayout:constraintlayout:1.1.3' + implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' + implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0' + implementation 'com.google.firebase:firebase-auth:16.0.5' + implementation 'androidx.legacy:legacy-support-v4:1.0.0' + testImplementation 'junit:junit:4.12' + androidTestImplementation 'androidx.test.ext:junit:1.1.1' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' + + //Retrofit and GSON + implementation 'com.squareup.retrofit2:retrofit:2.6.0' + implementation 'com.squareup.retrofit2:converter-gson:2.6.0' + + //New Material Design + implementation 'com.google.android.material:material:1.1.0-alpha10' + + //Kodein Dependency Injection + implementation "org.kodein.di:kodein-di-generic-jvm:6.2.1" + implementation "org.kodein.di:kodein-di-framework-android-x:6.2.1" + + implementation 'com.xwray:groupie:2.3.0' + implementation 'com.xwray:groupie-kotlin-android-extensions:2.3.0' + implementation 'com.xwray:groupie-databinding:2.3.0' + + implementation 'android.arch.lifecycle:extensions:1.1.1' + androidTestImplementation 'androidx.test:rules:1.3.0-alpha03' + +} diff --git a/app/google-services.json b/app/google-services.json new file mode 100644 index 0000000..21a709c --- /dev/null +++ b/app/google-services.json @@ -0,0 +1,48 @@ +{ + "project_info": { + "project_number": "610272918106", + "firebase_url": "https://test-project-hc.firebaseio.com", + "project_id": "test-project-hc", + "storage_bucket": "test-project-hc.appspot.com" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:610272918106:android:9c6fa2c3b332865842b663", + "android_client_info": { + "package_name": "com.example.h_mal.firebase_mobile_signin_app" + } + }, + "oauth_client": [ + { + "client_id": "610272918106-d7703a3pagh06ild1fd2toq3jih62dir.apps.googleusercontent.com", + "client_type": 1, + "android_info": { + "package_name": "com.example.h_mal.firebase_mobile_signin_app", + "certificate_hash": "e11218597efab2fc238c8e033689be3e09bc5dfb" + } + }, + { + "client_id": "610272918106-4kc0f9ja8vr041brdrop901fojgodojd.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyAfLSz-vIQPzgdkZa8_KZLkCH2CQ1D22iI" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "610272918106-4kc0f9ja8vr041brdrop901fojgodojd.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..f1b4245 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/app/src/androidTest/java/com/example/h_mal/firebase_mobile_signin_app/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/example/h_mal/firebase_mobile_signin_app/ExampleInstrumentedTest.kt new file mode 100644 index 0000000..700055b --- /dev/null +++ b/app/src/androidTest/java/com/example/h_mal/firebase_mobile_signin_app/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.example.h_mal.firebase_mobile_signin_app + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.example.h_mal.firebase_mobile_signin_app", appContext.packageName) + } +} diff --git a/app/src/androidTest/java/com/example/h_mal/firebase_mobile_signin_app/ui/main/TestUserLogingTest.kt b/app/src/androidTest/java/com/example/h_mal/firebase_mobile_signin_app/ui/main/TestUserLogingTest.kt new file mode 100644 index 0000000..b19ed07 --- /dev/null +++ b/app/src/androidTest/java/com/example/h_mal/firebase_mobile_signin_app/ui/main/TestUserLogingTest.kt @@ -0,0 +1,292 @@ +package com.example.h_mal.firebase_mobile_signin_app.ui.main + + +import android.view.View +import android.view.ViewGroup +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.UiController +import androidx.test.espresso.ViewAction +import androidx.test.espresso.action.ViewActions.* +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.RootMatchers.withDecorView +import androidx.test.espresso.matcher.ViewMatchers.* +import androidx.test.filters.LargeTest +import androidx.test.rule.ActivityTestRule +import androidx.test.runner.AndroidJUnit4 +import com.example.h_mal.firebase_mobile_signin_app.R +import org.hamcrest.Description +import org.hamcrest.Matcher +import org.hamcrest.Matchers.* +import org.hamcrest.TypeSafeMatcher +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + + +@LargeTest +@RunWith(AndroidJUnit4::class) +class TestUserLogingTest { + + @Rule + @JvmField + var mActivityTestRule = ActivityTestRule(MainActivity::class.java) + + @Test + fun testUserLoginTest() { + val appCompatEditText = onView( + allOf( + withId(R.id.phone_number), + childAtPosition( + allOf( + withId(R.id.lin_lay), + childAtPosition( + withId(R.id.container), + 0 + ) + ), + 1 + ), + isDisplayed() + ) + ) + appCompatEditText.perform(replaceText("784"), closeSoftKeyboard()) + + val appCompatButton = onView( + allOf( + withId(R.id.login), withText("Sign in"), + childAtPosition( + allOf( + withId(R.id.container), + childAtPosition( + withId(R.id.container), + 0 + ) + ), + 1 + ), + isDisplayed() + ) + ) + appCompatButton.perform(click()) + + testToast("Invalid Phone number") + + val appCompatEditText2 = onView( + allOf( + withId(R.id.phone_number), withText("784"), + childAtPosition( + allOf( + withId(R.id.lin_lay), + childAtPosition( + withId(R.id.container), + 0 + ) + ), + 1 + ), + isDisplayed() + ) + ) + appCompatEditText2.perform(click()) + + val appCompatEditText3 = onView( + allOf( + withId(R.id.phone_number), withText("784"), + childAtPosition( + allOf( + withId(R.id.lin_lay), + childAtPosition( + withId(R.id.container), + 0 + ) + ), + 1 + ), + isDisplayed() + ) + ) + appCompatEditText3.perform(longClick()) + + val appCompatEditText4 = onView( + allOf( + withId(R.id.phone_number), withText("784"), + childAtPosition( + allOf( + withId(R.id.lin_lay), + childAtPosition( + withId(R.id.container), + 0 + ) + ), + 1 + ), + isDisplayed() + ) + ) + appCompatEditText4.perform(replaceText("447849979363")) + + val appCompatEditText5 = onView( + allOf( + withId(R.id.phone_number), withText("447849979363"), + childAtPosition( + allOf( + withId(R.id.lin_lay), + childAtPosition( + withId(R.id.container), + 0 + ) + ), + 1 + ), + isDisplayed() + ) + ) + appCompatEditText5.perform(closeSoftKeyboard()) + + val appCompatButton2 = onView( + allOf( + withId(R.id.login), withText("Sign in"), + childAtPosition( + allOf( + withId(R.id.container), + childAtPosition( + withId(R.id.container), + 0 + ) + ), + 1 + ), + isDisplayed() + ) + ) + appCompatButton2.perform(click()) + + onView(isRoot()).perform(waitFor(2000)) + + val appCompatButton3 = onView( + allOf( + withId(R.id.login), withText("Sign In"), + childAtPosition( + allOf( + withId(R.id.container), + childAtPosition( + withId(R.id.container), + 0 + ) + ), + 2 + ), + isDisplayed() + ) + ) + appCompatButton3.perform(click()) + + val appCompatEditText6 = onView( + allOf( + withId(R.id.verification_code), + childAtPosition( + allOf( + withId(R.id.container), + childAtPosition( + withId(R.id.container), + 0 + ) + ), + 1 + ), + isDisplayed() + ) + ) + appCompatEditText6.perform(replaceText("123456"), closeSoftKeyboard()) + + val appCompatButton4 = onView( + allOf( + withId(R.id.login), withText("Sign In"), + childAtPosition( + allOf( + withId(R.id.container), + childAtPosition( + withId(R.id.container), + 0 + ) + ), + 2 + ), + isDisplayed() + ) + ) + appCompatButton4.perform(click()) + + onView(isRoot()).perform(waitFor(600)); + + val overflowMenuButton = onView( + allOf( + withContentDescription("More options"), + childAtPosition( + childAtPosition( + withId(R.id.action_bar), + 1 + ), + 0 + ), + isDisplayed() + ) + ) + overflowMenuButton.perform(click()) + + val appCompatTextView = onView( + allOf( + withId(R.id.title), withText("Sign out"), + childAtPosition( + childAtPosition( + withId(R.id.content), + 0 + ), + 0 + ), + isDisplayed() + ) + ) + appCompatTextView.perform(click()) + } + + private fun testToast(toastText:String){ + onView(withText(toastText)) + .inRoot(withDecorView(not(`is`(mActivityTestRule.activity.window.decorView)))) + .check(matches(isDisplayed())) + } + + fun waitFor(delay: Long): ViewAction? { + return object : ViewAction { + override fun getConstraints(): Matcher { + return isRoot() + } + + override fun getDescription(): String { + return "wait for " + delay + "milliseconds" + } + + override fun perform(uiController: UiController, view: View?) { + uiController.loopMainThreadForAtLeast(delay) + } + } + } + + private fun childAtPosition( + parentMatcher: Matcher, position: Int + ): Matcher { + + return object : TypeSafeMatcher() { + override fun describeTo(description: Description) { + description.appendText("Child at position $position in parent ") + parentMatcher.describeTo(description) + } + + public override fun matchesSafely(view: View): Boolean { + val parent = view.parent + return parent is ViewGroup && parentMatcher.matches(parent) + && view == parent.getChildAt(position) + } + } + } +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..9ef4cc3 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/AppClass.kt b/app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/AppClass.kt new file mode 100644 index 0000000..0f5c026 --- /dev/null +++ b/app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/AppClass.kt @@ -0,0 +1,34 @@ +package com.example.h_mal.firebase_mobile_signin_app + +import android.app.Application +import com.example.h_mal.firebase_mobile_signin_app.data.repository.Repository +import com.example.h_mal.firebase_mobile_signin_app.data.firebase.FirebaseSource +import com.example.h_mal.firebase_mobile_signin_app.data.network.MyApi +import com.example.h_mal.firebase_mobile_signin_app.data.network.NetworkConnectionInterceptor +import com.example.h_mal.firebase_mobile_signin_app.ui.details.DetailsViewModel +import com.example.h_mal.firebase_mobile_signin_app.ui.details.DetailsViewModelFactory +import com.example.h_mal.firebase_mobile_signin_app.ui.main.MainViewModelFactory +import org.kodein.di.Kodein +import org.kodein.di.KodeinAware +import org.kodein.di.android.x.androidXModule +import org.kodein.di.generic.bind +import org.kodein.di.generic.instance +import org.kodein.di.generic.provider +import org.kodein.di.generic.singleton + +class AppClass : Application(), KodeinAware{ + + + override val kodein= Kodein.lazy { + import(androidXModule(this@AppClass)) + + bind() from singleton { FirebaseSource() } + bind() from singleton { NetworkConnectionInterceptor(instance()) } + bind() from singleton { MyApi(instance()) } + bind() from singleton { Repository(instance(),instance()) } + + bind() from provider { MainViewModelFactory(instance()) } + bind() from provider { DetailsViewModelFactory(instance()) } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/data/firebase/FirebaseSource.kt b/app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/data/firebase/FirebaseSource.kt new file mode 100644 index 0000000..5e0af97 --- /dev/null +++ b/app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/data/firebase/FirebaseSource.kt @@ -0,0 +1,33 @@ +package com.example.h_mal.firebase_mobile_signin_app.data.firebase + +import com.google.android.gms.tasks.TaskExecutors +import com.google.firebase.auth.FirebaseAuth +import com.google.firebase.auth.PhoneAuthCredential +import com.google.firebase.auth.PhoneAuthProvider +import com.google.firebase.auth.PhoneAuthProvider.OnVerificationStateChangedCallbacks +import java.util.concurrent.TimeUnit + +class FirebaseSource { + + private val firebaseAuth: FirebaseAuth by lazy { + FirebaseAuth.getInstance() + } + + fun logout() = firebaseAuth.signOut() + + fun currentUser() = firebaseAuth.currentUser + + fun getUid() = firebaseAuth.currentUser!!.uid + + fun signInWithPhoneNumber(credentials: PhoneAuthCredential) = firebaseAuth.signInWithCredential(credentials) + + fun sendVerificationCode(phoneNo: String, mCallback : OnVerificationStateChangedCallbacks) + = PhoneAuthProvider.getInstance().verifyPhoneNumber( + phoneNo, + 60, + TimeUnit.SECONDS, + TaskExecutors.MAIN_THREAD, + mCallback) + + +} \ No newline at end of file diff --git a/app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/data/network/MyApi.kt b/app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/data/network/MyApi.kt new file mode 100644 index 0000000..f5c9395 --- /dev/null +++ b/app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/data/network/MyApi.kt @@ -0,0 +1,44 @@ +package com.example.h_mal.firebase_mobile_signin_app.data.network + +import com.example.h_mal.firebase_mobile_signin_app.data.network.model.ResponseObject +import com.example.h_mal.firebase_mobile_signin_app.data.network.model.User +import okhttp3.OkHttpClient +import okhttp3.ResponseBody +import retrofit2.Call +import retrofit2.Response +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import retrofit2.http.* + +interface MyApi { + + @PUT("updateUser") + suspend fun updateUser( + @Body user: User + ): Response + + @GET("getUser") + suspend fun getUser( + @Query("userId") userId: String + ) : Response + + companion object{ + operator fun invoke( + networkConnectionInterceptor: NetworkConnectionInterceptor + ) : MyApi{ + + val okkHttpclient = OkHttpClient.Builder() + .addInterceptor(networkConnectionInterceptor) + .build() + + return Retrofit.Builder() + .client(okkHttpclient) + .baseUrl("https://us-central1-test-project-hc.cloudfunctions.net/") + .addConverterFactory(GsonConverterFactory.create()) + .build() + .create(MyApi::class.java) + } + } + +} + diff --git a/app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/data/network/NetworkConnectionInterceptor.kt b/app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/data/network/NetworkConnectionInterceptor.kt new file mode 100644 index 0000000..57d8af0 --- /dev/null +++ b/app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/data/network/NetworkConnectionInterceptor.kt @@ -0,0 +1,38 @@ +package com.example.h_mal.firebase_mobile_signin_app.data.network + +import android.content.Context +import android.net.ConnectivityManager +import android.net.NetworkCapabilities +import okhttp3.Interceptor +import okhttp3.Response +import java.io.IOException + +class NetworkConnectionInterceptor( + context: Context +) : Interceptor { + + private val applicationContext = context.applicationContext + + override fun intercept(chain: Interceptor.Chain): Response { + if (!isInternetAvailable()) + throw IOException("Make sure you have an active data connection") + return chain.proceed(chain.request()) + } + + private fun isInternetAvailable(): Boolean { + var result = false + val connectivityManager = + applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager? + connectivityManager?.let { + it.getNetworkCapabilities(connectivityManager.activeNetwork)?.apply { + result = when { + hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> true + hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> true + else -> false + } + } + } + return result + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/data/network/SafeApiRequest.kt b/app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/data/network/SafeApiRequest.kt new file mode 100644 index 0000000..8e7579c --- /dev/null +++ b/app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/data/network/SafeApiRequest.kt @@ -0,0 +1,29 @@ +package com.example.h_mal.firebase_mobile_signin_app.data.network + +import org.json.JSONException +import org.json.JSONObject +import retrofit2.Response +import java.io.IOException + +abstract class SafeApiRequest { + + suspend fun apiRequest(call: suspend () -> Response) : T{ + val response = call.invoke() + if(response.isSuccessful){ + return response.body()!! + }else{ + val error = response.errorBody()?.string() + + val message = StringBuilder() + error?.let{ + try{ + message.append(JSONObject(it).getString("message")) + }catch(e: JSONException){ } + message.append("\n") + } + message.append("Error Code: ${response.code()}") + throw IOException(message.toString()) + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/data/network/model/ResponseObject.kt b/app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/data/network/model/ResponseObject.kt new file mode 100644 index 0000000..61b013d --- /dev/null +++ b/app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/data/network/model/ResponseObject.kt @@ -0,0 +1,7 @@ +package com.example.h_mal.firebase_mobile_signin_app.data.network.model + +data class ResponseObject( + val status: String?, + val message: String?, + val user: User? +) \ No newline at end of file diff --git a/app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/data/network/model/User.kt b/app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/data/network/model/User.kt new file mode 100644 index 0000000..d387c5e --- /dev/null +++ b/app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/data/network/model/User.kt @@ -0,0 +1,9 @@ +package com.example.h_mal.firebase_mobile_signin_app.data.network.model + +data class User( + val userId: String?, + val firstName: String?, + val lastName: String?, + val email: String?, + val avatar: String? +) \ No newline at end of file diff --git a/app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/data/repository/Repository.kt b/app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/data/repository/Repository.kt new file mode 100644 index 0000000..6c2a562 --- /dev/null +++ b/app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/data/repository/Repository.kt @@ -0,0 +1,38 @@ +package com.example.h_mal.firebase_mobile_signin_app.data.repository + +import com.example.h_mal.firebase_mobile_signin_app.data.firebase.FirebaseSource +import com.example.h_mal.firebase_mobile_signin_app.data.network.MyApi +import com.example.h_mal.firebase_mobile_signin_app.data.network.SafeApiRequest +import com.example.h_mal.firebase_mobile_signin_app.data.network.model.ResponseObject +import com.example.h_mal.firebase_mobile_signin_app.data.network.model.User +import com.google.android.gms.tasks.Task +import com.google.firebase.auth.AuthResult +import com.google.firebase.auth.PhoneAuthProvider + +class Repository( + private val firebaseSource: FirebaseSource, + private val api: MyApi +): SafeApiRequest() { + + + fun signIn(verificationId : String, code: String): Task { + val credential = PhoneAuthProvider.getCredential(verificationId, code) + + return firebaseSource.signInWithPhoneNumber(credential) + } + + fun getCode(string: String, callback: PhoneAuthProvider.OnVerificationStateChangedCallbacks) + = firebaseSource.sendVerificationCode(string, callback) + + fun getUid() = firebaseSource.getUid() + + fun logoutUser() = firebaseSource.logout() + + suspend fun getUser(uid: String): ResponseObject{ + return apiRequest { api.getUser(uid) } + } + + suspend fun updateUser(user: User): ResponseObject{ + return apiRequest { api.updateUser(user) } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/ui/CompletionListener.kt b/app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/ui/CompletionListener.kt new file mode 100644 index 0000000..222eb3a --- /dev/null +++ b/app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/ui/CompletionListener.kt @@ -0,0 +1,8 @@ +package com.example.h_mal.firebase_mobile_signin_app.ui + +interface CompletionListener { + fun onStarted() + fun onChange() + fun onSuccess() + fun onFailure(message: String) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/ui/details/DetailsUpdateActivity.kt b/app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/ui/details/DetailsUpdateActivity.kt new file mode 100644 index 0000000..1b54e4c --- /dev/null +++ b/app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/ui/details/DetailsUpdateActivity.kt @@ -0,0 +1,161 @@ +package com.example.h_mal.firebase_mobile_signin_app.ui.details + +import android.Manifest.permission.READ_EXTERNAL_STORAGE +import android.Manifest.permission.WRITE_EXTERNAL_STORAGE +import android.content.Intent +import android.content.pm.PackageManager +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.os.Build +import android.os.Bundle +import android.util.Base64 +import android.view.Menu +import android.view.MenuItem +import android.view.View +import android.widget.Toast +import androidx.appcompat.app.AppCompatActivity +import androidx.databinding.DataBindingUtil +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModelProvider +import com.example.h_mal.firebase_mobile_signin_app.R +import com.example.h_mal.firebase_mobile_signin_app.databinding.ActivityDetailsUpdateBinding +import com.example.h_mal.firebase_mobile_signin_app.ui.CompletionListener +import com.example.h_mal.firebase_mobile_signin_app.ui.main.MainActivity +import kotlinx.android.synthetic.main.activity_details_update.* +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import org.kodein.di.KodeinAware +import org.kodein.di.android.kodein +import org.kodein.di.generic.instance + + +class DetailsUpdateActivity : AppCompatActivity(),CompletionListener, KodeinAware { + + private val IMAGE_PICK_CODE: Int = 1000 + private val PERMISSION_CODE_WRITE: Int = 1002 + private val PERMISSION_CODE_READ: Int = 1001 + + override val kodein by kodein() + private val factory: DetailsViewModelFactory by instance() + + lateinit var viewModel: DetailsViewModel + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + viewModel = ViewModelProvider(this, factory).get(DetailsViewModel::class.java) + val binding: ActivityDetailsUpdateBinding = DataBindingUtil.setContentView(this, R.layout.activity_details_update) + binding.viewmodel = viewModel + + viewModel.completionListener = this + + viewModel.initialise() + + viewModel.currentUser.observe(this, Observer { + email.setText(it.email) + first_name.setText(it.firstName) + last_name.setText(it.lastName) + + convertB64ToImage(it.avatar) + }) + + image_button.setOnClickListener { + checkPermissionForImage() + } + } + + //create a menu to navigate to other activities + override fun onCreateOptionsMenu(menu: Menu?): Boolean { + menuInflater.inflate(R.menu.menu, menu) + return super.onCreateOptionsMenu(menu) + } + + override fun onOptionsItemSelected(item: MenuItem?): Boolean { + when (item?.itemId){ + R.id.sign_out ->{ + viewModel.logOutUser() + Intent(this, MainActivity::class.java).also { + it.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK + startActivity(it) + } + } + } + + return super.onOptionsItemSelected(item) + } + + private fun checkPermissionForImage() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && + (checkSelfPermission(READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED) + && (checkSelfPermission(WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED) + ) { + + val permission = arrayOf(READ_EXTERNAL_STORAGE) + val permissionCoarse = arrayOf(WRITE_EXTERNAL_STORAGE) + + requestPermissions(permission, PERMISSION_CODE_READ) + requestPermissions(permissionCoarse, PERMISSION_CODE_WRITE) + + } else { + pickImageFromGallery() + } + + + } + + private fun pickImageFromGallery() { + Intent(Intent.ACTION_PICK).apply { + this.type = "image/*" + startActivityForResult(this, IMAGE_PICK_CODE) + } + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + if (resultCode == RESULT_OK && requestCode == IMAGE_PICK_CODE) { + data?.data?.let {uri -> + viewModel.turnUriToString(this, uri) + image_button.setImageURI(uri) + return + } + Toast.makeText(this,"No image selected", Toast.LENGTH_LONG).show() + } + } + + private fun convertB64ToImage(b64String: String?){ + if (b64String.isNullOrEmpty()){ + return + } + + CoroutineScope(Dispatchers.Default).launch { + val decodedString: ByteArray = + Base64.decode(b64String, Base64.DEFAULT) + val bitmap = BitmapFactory.decodeByteArray(decodedString, 0, decodedString.size) + withContext(Dispatchers.Main){ + image_button.setImageBitmap(bitmap) + } + } + } + + override fun onStarted() { + progress_bar.visibility = View.VISIBLE + } + + override fun onSuccess() { + progress_bar.visibility = View.GONE + } + + override fun onFailure(message: String) { + progress_bar.visibility = View.GONE + Toast.makeText(this,message, Toast.LENGTH_LONG).show() + } + + override fun onChange() { + viewModel.imageB64String?.let { + convertB64ToImage(it) + } + progress_bar.visibility = View.GONE + } +} diff --git a/app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/ui/details/DetailsViewModel.kt b/app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/ui/details/DetailsViewModel.kt new file mode 100644 index 0000000..1e577a3 --- /dev/null +++ b/app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/ui/details/DetailsViewModel.kt @@ -0,0 +1,119 @@ +package com.example.h_mal.firebase_mobile_signin_app.ui.details + +import android.content.Context +import android.content.Intent +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.net.Uri +import android.util.Base64 +import android.view.View +import android.widget.Toast +import androidx.core.app.ActivityCompat.startActivity +import androidx.core.app.ActivityCompat.startActivityForResult +import androidx.core.content.ContextCompat +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.example.h_mal.firebase_mobile_signin_app.data.network.model.User +import com.example.h_mal.firebase_mobile_signin_app.data.repository.Repository +import com.example.h_mal.firebase_mobile_signin_app.ui.CompletionListener +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import java.io.ByteArrayOutputStream +import java.io.FileNotFoundException + + +class DetailsViewModel( + private val repository: Repository +) : ViewModel() { + + var completionListener : CompletionListener? = null + + val currentUser = MutableLiveData() + + var firstName: String? = null + var lastName: String? = null + var email: String? = null + var imageB64String: String? = null + + fun onUpdateButtonClick(view: View){ + completionListener?.onStarted() + if(firstName.isNullOrEmpty() || lastName.isNullOrEmpty() || email.isNullOrEmpty()){ + completionListener?.onFailure("names or email field empty") + return + } + + if (imageB64String.isNullOrEmpty()){ + completionListener?.onFailure("image is invalid") + return + } + + CoroutineScope(Dispatchers.Main).launch { + try{ + val uid = repository.getUid() + val user = User(uid, firstName!!, lastName!!, email!!, imageB64String!!) + + val response = repository.updateUser(user) + + response.user?.let { + currentUser.value = it + completionListener?.onSuccess() + return@launch + } + completionListener?.onFailure("failed to update user") + }catch (e : Exception){ + completionListener?.onFailure(e.message!!) + } + + } + } + + fun logOutUser() = repository.logoutUser() + + fun initialise(){ + completionListener?.onStarted() + CoroutineScope(Dispatchers.Main).launch { + try{ + val uid = repository.getUid() + val responseObject = repository.getUser(uid) + responseObject.user?.let { + imageB64String = it.avatar + + currentUser.value = it + completionListener?.onSuccess() + + return@launch + } + completionListener?.onFailure("no data saved for this user") + }catch (e : Exception){ + completionListener?.onFailure(e.message!!) + } + + } + } + + fun turnUriToString(context: Context, uri: Uri){ + completionListener?.onStarted() + CoroutineScope(Dispatchers.Default).launch { + try { + val imageStream = context.contentResolver.openInputStream(uri) + + val selectedImage = BitmapFactory.decodeStream(imageStream) + val baos = ByteArrayOutputStream() + selectedImage.compress(Bitmap.CompressFormat.JPEG, 100, baos) + val b: ByteArray = baos.toByteArray() + + imageB64String = Base64.encodeToString(b, Base64.DEFAULT) + withContext(Dispatchers.Main){ + completionListener?.onChange() + } + }catch (e: FileNotFoundException){ + withContext(Dispatchers.Main){ + completionListener?.onFailure(e.message!!) + } + } + } + } + +} diff --git a/app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/ui/details/DetailsViewModelFactory.kt b/app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/ui/details/DetailsViewModelFactory.kt new file mode 100644 index 0000000..202c85c --- /dev/null +++ b/app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/ui/details/DetailsViewModelFactory.kt @@ -0,0 +1,15 @@ +package com.example.h_mal.firebase_mobile_signin_app.ui.details + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.example.h_mal.firebase_mobile_signin_app.data.repository.Repository + +@Suppress("UNCHECKED_CAST") +class DetailsViewModelFactory ( + private val repository: Repository +): ViewModelProvider.NewInstanceFactory() { + + override fun create(modelClass: Class): T { + return DetailsViewModel(repository) as T + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/ui/main/MainActivity.kt b/app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/ui/main/MainActivity.kt new file mode 100644 index 0000000..f84b4e1 --- /dev/null +++ b/app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/ui/main/MainActivity.kt @@ -0,0 +1,66 @@ +package com.example.h_mal.firebase_mobile_signin_app.ui.main + +import androidx.appcompat.app.AppCompatActivity +import android.os.Bundle +import android.view.View +import android.widget.Toast +import androidx.lifecycle.ViewModelProvider +import com.example.h_mal.firebase_mobile_signin_app.R +import com.example.h_mal.firebase_mobile_signin_app.ui.CompletionListener +import kotlinx.android.synthetic.main.main_activity.* +import org.kodein.di.KodeinAware +import org.kodein.di.android.kodein +import org.kodein.di.generic.instance + +class MainActivity : AppCompatActivity(), + CompletionListener, KodeinAware { + + override val kodein by kodein() + private val factory: MainViewModelFactory by instance() + + companion object{ + lateinit var viewModel: MainViewModel + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.main_activity) + if (savedInstanceState == null) { + supportFragmentManager.beginTransaction() + .replace(R.id.container, VerifyFragment()) + .commit() + } + + viewModel = ViewModelProvider(this, factory).get(MainViewModel::class.java) + viewModel.completionListener = this + } + + override fun onBackPressed() { + if (supportFragmentManager.backStackEntryCount > 1 ){ + supportFragmentManager.popBackStack() + return + } + super.onBackPressed() + } + + override fun onStarted() { + progress_bar.visibility = View.VISIBLE + } + + override fun onChange() { + supportFragmentManager.beginTransaction() + .addToBackStack("firstFragment") + .replace(R.id.container, SignUpFragment()) + .commit() + } + + override fun onSuccess() { + progress_bar.visibility = View.GONE + } + + override fun onFailure(message: String) { + progress_bar.visibility = View.GONE + Toast.makeText(this, message, Toast.LENGTH_LONG).show() + } + +} diff --git a/app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/ui/main/MainViewModel.kt b/app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/ui/main/MainViewModel.kt new file mode 100644 index 0000000..f416bf0 --- /dev/null +++ b/app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/ui/main/MainViewModel.kt @@ -0,0 +1,90 @@ +package com.example.h_mal.firebase_mobile_signin_app.ui.main + +import android.content.Intent +import android.util.Log +import android.view.View +import androidx.lifecycle.ViewModel +import com.example.h_mal.firebase_mobile_signin_app.data.repository.Repository +import com.example.h_mal.firebase_mobile_signin_app.ui.CompletionListener +import com.example.h_mal.firebase_mobile_signin_app.ui.details.DetailsUpdateActivity +import com.google.firebase.FirebaseException +import com.google.firebase.auth.PhoneAuthCredential +import com.google.firebase.auth.PhoneAuthProvider.ForceResendingToken +import com.google.firebase.auth.PhoneAuthProvider.OnVerificationStateChangedCallbacks + +class MainViewModel( + private val repository: Repository +) : ViewModel() { + + var completionListener : CompletionListener? = null + + var phoneNumber: String? = null + var verificationCode: String? = null + + var verificationId: String? = null + + fun onVerificationNumberClick(view: View){ + completionListener?.onStarted() + if(phoneNumber.isNullOrEmpty()){ + completionListener?.onFailure("Invalid Phone Number") + return + } + + if (phoneNumber!!.count() < 6){ + completionListener?.onFailure("Invalid Phone number") + return + } + + repository.getCode("+$phoneNumber", mCallBack) + } + + fun onSignInClick(view: View){ + if(verificationCode.isNullOrEmpty() || verificationId.isNullOrEmpty()){ + completionListener?.onFailure("Invalid verification code or ID") + return + } + + repository.signIn(verificationId!!,verificationCode!!).addOnCompleteListener{ + if (it.isSuccessful){ + Intent(view.context, DetailsUpdateActivity::class.java).apply { + this.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK + view.context.startActivity(this) + } + }else{ + completionListener?.onFailure(it.exception?.message!!) + } + } + } + + private val mCallBack: OnVerificationStateChangedCallbacks = + object : OnVerificationStateChangedCallbacks() { + override fun onCodeSent( + s: String, + forceResendingToken: ForceResendingToken + ) { + super.onCodeSent(s, forceResendingToken) + verificationId = s + completionListener?.onChange() + + if (phoneNumber == "447849979363"){ + //test accounts do not complete verifications + //lets stop the spinner + completionListener?.onSuccess() + } + } + + override fun onVerificationCompleted(phoneAuthCredential: PhoneAuthCredential) { + completionListener?.onSuccess() + phoneAuthCredential.smsCode?.let { + verificationCode = it + return + } + completionListener?.onFailure("Failed To Verify") + } + + override fun onVerificationFailed(e: FirebaseException) { + completionListener?.onFailure(e.message!!) + } + } + +} diff --git a/app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/ui/main/MainViewModelFactory.kt b/app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/ui/main/MainViewModelFactory.kt new file mode 100644 index 0000000..b91b4bb --- /dev/null +++ b/app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/ui/main/MainViewModelFactory.kt @@ -0,0 +1,15 @@ +package com.example.h_mal.firebase_mobile_signin_app.ui.main + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.example.h_mal.firebase_mobile_signin_app.data.repository.Repository + +@Suppress("UNCHECKED_CAST") +class MainViewModelFactory ( + private val repository: Repository +): ViewModelProvider.NewInstanceFactory() { + + override fun create(modelClass: Class): T { + return MainViewModel(repository) as T + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/ui/main/SignUpFragment.kt b/app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/ui/main/SignUpFragment.kt new file mode 100644 index 0000000..2c3a028 --- /dev/null +++ b/app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/ui/main/SignUpFragment.kt @@ -0,0 +1,30 @@ +package com.example.h_mal.firebase_mobile_signin_app.ui.main + +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.databinding.DataBindingUtil +import com.example.h_mal.firebase_mobile_signin_app.ui.main.MainActivity.Companion.viewModel + +import com.example.h_mal.firebase_mobile_signin_app.R +import com.example.h_mal.firebase_mobile_signin_app.databinding.FragmentSignUpBinding +import org.kodein.di.KodeinAware +import org.kodein.di.android.x.kodein +import org.kodein.di.generic.instance + + +class SignUpFragment : Fragment(){ + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + val binding: FragmentSignUpBinding = DataBindingUtil.inflate(inflater,R.layout.fragment_sign_up, container, false) + binding.viewmodel = viewModel + + return binding.root + } + +} diff --git a/app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/ui/main/VerifyFragment.kt b/app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/ui/main/VerifyFragment.kt new file mode 100644 index 0000000..9fe675b --- /dev/null +++ b/app/src/main/java/com/example/h_mal/firebase_mobile_signin_app/ui/main/VerifyFragment.kt @@ -0,0 +1,35 @@ +package com.example.h_mal.firebase_mobile_signin_app.ui.main + + +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.databinding.DataBindingUtil +import com.example.h_mal.firebase_mobile_signin_app.ui.main.MainActivity.Companion.viewModel + +import com.example.h_mal.firebase_mobile_signin_app.R +import com.example.h_mal.firebase_mobile_signin_app.databinding.MainFragmentBinding +import org.kodein.di.KodeinAware +import org.kodein.di.android.x.kodein +import org.kodein.di.generic.instance + +class VerifyFragment : Fragment(), KodeinAware{ + + override val kodein by kodein() + private val factory: MainViewModelFactory by instance() + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + + val binding: MainFragmentBinding = DataBindingUtil.inflate(inflater,R.layout.main_fragment, container, false) +// val viewModel = ViewModelProvider(this, factory).get(MainViewModel::class.java) + binding.viewmodel = viewModel + + return binding.root + } + +} diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..1f6bb29 --- /dev/null +++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..0d025f9 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_details_update.xml b/app/src/main/res/layout/activity_details_update.xml new file mode 100644 index 0000000..a1d5c34 --- /dev/null +++ b/app/src/main/res/layout/activity_details_update.xml @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + +