├── settings.gradle ├── .gitattributes ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── app ├── src │ ├── main │ │ ├── res │ │ │ ├── mipmap-hdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ ├── ic_launcher.xml │ │ │ │ └── ic_launcher_round.xml │ │ │ ├── layout │ │ │ │ ├── fragment_main.xml │ │ │ │ ├── activity_login.xml │ │ │ │ ├── activity_splash.xml │ │ │ │ ├── activity_register.xml │ │ │ │ ├── activity_detail_book.xml │ │ │ │ ├── row_book.xml │ │ │ │ ├── activity_home.xml │ │ │ │ ├── toolbar.xml │ │ │ │ ├── fragment_login.xml │ │ │ │ └── fragment_register.xml │ │ │ ├── values │ │ │ │ ├── dimen.xml │ │ │ │ ├── styles.xml │ │ │ │ ├── fonts.xml │ │ │ │ ├── strings.xml │ │ │ │ └── colors.xml │ │ │ ├── drawable │ │ │ │ ├── ic_close.xml │ │ │ │ └── ic_launcher_background.xml │ │ │ └── drawable-v24 │ │ │ │ └── ic_launcher_foreground.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── me │ │ │ │ └── booksdemoandroid │ │ │ │ ├── shared │ │ │ │ ├── k │ │ │ │ │ ├── K.kt │ │ │ │ │ ├── KIntent.kt │ │ │ │ │ ├── KCredential.kt │ │ │ │ │ └── KEnum.kt │ │ │ │ ├── pref │ │ │ │ │ └── Pref.kt │ │ │ │ ├── listner │ │ │ │ │ └── interface.kt │ │ │ │ ├── repositories │ │ │ │ │ ├── Result.kt │ │ │ │ │ ├── ApiFactory.kt │ │ │ │ │ ├── Resource.kt │ │ │ │ │ ├── BaseRepository.kt │ │ │ │ │ └── RetrofitFactory.kt │ │ │ │ ├── extension │ │ │ │ │ ├── View.kt │ │ │ │ │ ├── Null.kt │ │ │ │ │ ├── Coroutines.kt │ │ │ │ │ ├── Fragment.kt │ │ │ │ │ └── ViewModel.kt │ │ │ │ ├── fragment │ │ │ │ │ └── BaseFragment.kt │ │ │ │ └── helper │ │ │ │ │ ├── Connectivity.kt │ │ │ │ │ └── PreferenceHelper.kt │ │ │ │ ├── feature │ │ │ │ ├── home │ │ │ │ │ ├── model │ │ │ │ │ │ └── Book.kt │ │ │ │ │ ├── activity │ │ │ │ │ │ ├── DetailBookActivity.kt │ │ │ │ │ │ └── HomeActivity.kt │ │ │ │ │ ├── helper │ │ │ │ │ │ ├── HomeRepository.kt │ │ │ │ │ │ └── NavHome.kt │ │ │ │ │ ├── vm │ │ │ │ │ │ └── HomeViewModel.kt │ │ │ │ │ └── adapter │ │ │ │ │ │ └── BookAdapter.kt │ │ │ │ ├── auth │ │ │ │ │ ├── helper │ │ │ │ │ │ ├── AuthNav.kt │ │ │ │ │ │ └── AuthRepository.kt │ │ │ │ │ ├── activity │ │ │ │ │ │ ├── LoginActivity.kt │ │ │ │ │ │ └── RegisterActivity.kt │ │ │ │ │ ├── vm │ │ │ │ │ │ └── AuthViewModel.kt │ │ │ │ │ └── frag │ │ │ │ │ │ ├── RegisterFragment.kt │ │ │ │ │ │ └── LoginFragment.kt │ │ │ │ └── splash │ │ │ │ │ └── SplashActivity.kt │ │ │ │ └── App.kt │ │ └── AndroidManifest.xml │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── me │ │ │ └── booksdemoandroid │ │ │ └── ExampleUnitTest.kt │ └── androidTest │ │ └── java │ │ └── com │ │ └── me │ │ └── booksdemoandroid │ │ └── ExampleInstrumentedTest.kt ├── build │ └── generated │ │ └── source │ │ └── buildConfig │ │ └── mock │ │ └── debug │ │ └── com │ │ └── me │ │ └── booksdemoandroid │ │ └── BuildConfig.java ├── proguard-rules.pro └── build.gradle ├── .gitignore ├── LICENSE ├── gradle.properties ├── gradlew.bat ├── README.md └── gradlew /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SimpleBoilerplates/Android/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SimpleBoilerplates/Android/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SimpleBoilerplates/Android/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SimpleBoilerplates/Android/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/java/com/me/booksdemoandroid/shared/k/K.kt: -------------------------------------------------------------------------------- 1 | package com.me.booksdemoandroid.shared.k 2 | 3 | class K { 4 | companion object { 5 | } 6 | } -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SimpleBoilerplates/Android/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SimpleBoilerplates/Android/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/java/com/me/booksdemoandroid/shared/k/KIntent.kt: -------------------------------------------------------------------------------- 1 | package com.me.booksdemoandroid.shared.k 2 | 3 | 4 | enum class KIntent { 5 | PRODUCT 6 | } 7 | -------------------------------------------------------------------------------- /app/src/main/java/com/me/booksdemoandroid/shared/pref/Pref.kt: -------------------------------------------------------------------------------- 1 | package com.me.booksdemoandroid.shared.pref 2 | 3 | object Pref { 4 | var token = "" 5 | 6 | } -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SimpleBoilerplates/Android/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SimpleBoilerplates/Android/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SimpleBoilerplates/Android/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SimpleBoilerplates/Android/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SimpleBoilerplates/Android/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/java/com/me/booksdemoandroid/shared/k/KCredential.kt: -------------------------------------------------------------------------------- 1 | package com.me.booksdemoandroid.shared.k 2 | 3 | class KCredential { 4 | companion object { 5 | const val Url = "https://booksdemo.herokuapp.com" 6 | } 7 | } -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Jun 09 19:56:08 BDT 2019 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip 7 | -------------------------------------------------------------------------------- /app/src/main/java/com/me/booksdemoandroid/shared/listner/interface.kt: -------------------------------------------------------------------------------- 1 | package com.me.booksdemoandroid.shared.listner 2 | 3 | interface OnBackPressedListener { 4 | fun doBack() 5 | fun doBackWithStart() 6 | } 7 | 8 | interface OnItemClickListener { 9 | fun itemClicked(position: Int) 10 | } -------------------------------------------------------------------------------- /app/src/main/java/com/me/booksdemoandroid/shared/repositories/Result.kt: -------------------------------------------------------------------------------- 1 | package com.me.booksdemoandroid.shared.repositories 2 | 3 | sealed class Result { 4 | data class Success(val data: T) : Result() 5 | data class Error(val exception: Exception) : Result() 6 | } -------------------------------------------------------------------------------- /app/src/main/java/com/me/booksdemoandroid/shared/extension/View.kt: -------------------------------------------------------------------------------- 1 | package com.me.booksdemoandroid.shared.extension 2 | 3 | import android.view.View 4 | import com.google.android.material.snackbar.Snackbar 5 | 6 | fun View.showSnackBar(message: String, duration: Int) { 7 | Snackbar.make(this, message, duration).show() 8 | } -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/java/com/me/booksdemoandroid/shared/extension/Null.kt: -------------------------------------------------------------------------------- 1 | package com.me.booksdemoandroid.shared.extension 2 | 3 | fun T?.notNull(f: (it: T) -> Unit) { 4 | if (this != null) f(this) 5 | } 6 | 7 | 8 | inline fun whenNotNull(input: T?, callback: (T) -> R): R? { 9 | return input?.let(callback) 10 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_login.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_splash.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimen.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16dp 5 | 16dp 6 | 8dp 7 | 8 | 25sp 9 | 33sp 10 | 11sp 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/java/com/me/booksdemoandroid/feature/home/model/Book.kt: -------------------------------------------------------------------------------- 1 | package com.me.booksdemoandroid.feature.home.model 2 | import android.os.Parcelable 3 | import kotlinx.android.parcel.Parcelize 4 | 5 | 6 | @Parcelize 7 | data class Book( 8 | val createdAt: String, 9 | val description: String, 10 | val id: Int, 11 | val preview: String, 12 | val subTitle: String, 13 | val title: String, 14 | val updatedAt: String 15 | ): Parcelable -------------------------------------------------------------------------------- /app/src/test/java/com/me/booksdemoandroid/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.me.booksdemoandroid 2 | 3 | import org.junit.Assert.assertEquals 4 | import org.junit.Test 5 | 6 | /** 7 | * Example local unit test, which will execute on the development machine (host). 8 | * 9 | * See [testing documentation](http://d.android.com/tools/testing). 10 | */ 11 | class ExampleUnitTest { 12 | @Test 13 | fun addition_isCorrect() { 14 | assertEquals(4, 2 + 2) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/res/values/fonts.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | sans-serif-light 4 | sans-serif-medium 5 | sans-serif 6 | sans-serif-condensed 7 | sans-serif-black 8 | sans-serif-thin 9 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_register.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/java/com/me/booksdemoandroid/shared/k/KEnum.kt: -------------------------------------------------------------------------------- 1 | package com.me.booksdemoandroid.shared.k 2 | 3 | class KEnum { 4 | companion object { 5 | 6 | enum class LoadingType(val value: Int) { 7 | DownloadingBook(0), Loading(1), NoConnection(2), Error(3) 8 | } 9 | 10 | enum class SharedPref { 11 | LastReading, DevicesAddress, Token, ExpiresIn, LoginTime, Me 12 | } 13 | 14 | enum class BroadCast { 15 | LastReading 16 | } 17 | 18 | } 19 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_detail_book.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/build/generated/source/buildConfig/mock/debug/com/me/booksdemoandroid/BuildConfig.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Automatically generated file. DO NOT MODIFY 3 | */ 4 | package com.me.booksdemoandroid; 5 | 6 | public final class BuildConfig { 7 | public static final boolean DEBUG = Boolean.parseBoolean("true"); 8 | public static final String APPLICATION_ID = "com.me.booksdemoandroid.mock"; 9 | public static final String BUILD_TYPE = "debug"; 10 | public static final String FLAVOR = "mock"; 11 | public static final int VERSION_CODE = 1; 12 | public static final String VERSION_NAME = "1.0"; 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/res/layout/row_book.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 12 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | BooksDemoAndroid 3 | 4 | 5 | Login 6 | Register 7 | Type your user name 8 | Type your password 9 | Type your password again 10 | Type your mail address 11 | 12 | New User? Please 13 | Already an User? Please 14 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #0c0d0d 4 | #0c0d0d 5 | #535151 6 | 7 | #f2f2f2 8 | #ffffff 9 | #ffffff 10 | #D3D3D3 11 | 12 | 13 | #ffffff 14 | #0c0d0d 15 | #424242 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_home.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/java/com/me/booksdemoandroid/shared/repositories/ApiFactory.kt: -------------------------------------------------------------------------------- 1 | package com.me.booksdemoandroid.shared.repositories 2 | 3 | import com.me.booksdemoandroid.feature.auth.helper.AuthApi 4 | import com.me.booksdemoandroid.feature.home.helper.HomeApi 5 | import com.me.booksdemoandroid.shared.k.KCredential 6 | 7 | object ApiFactory { 8 | 9 | // val placeholderApi : PlaceholderApi = RetrofitFactory.retrofit(AppConstants.JSON_PLACEHOLDER_BASE_URL) 10 | // .create(PlaceholderApi::class.java) 11 | 12 | val authApi = RetrofitFactory.retrofit(KCredential.Url) 13 | .create(AuthApi::class.java) 14 | 15 | val homeApi = RetrofitFactory.retrofit(KCredential.Url) 16 | .create(HomeApi::class.java) 17 | } -------------------------------------------------------------------------------- /app/src/main/java/com/me/booksdemoandroid/shared/extension/Coroutines.kt: -------------------------------------------------------------------------------- 1 | package com.me.booksdemoandroid.shared.extension 2 | 3 | import kotlinx.coroutines.* 4 | 5 | object Coroutines { 6 | 7 | fun io(work: suspend (() -> T?)): Job = 8 | CoroutineScope(Dispatchers.IO).launch { 9 | work() 10 | } 11 | 12 | fun ioThenMain(work: suspend (() -> T?), callback: ((T?) -> Unit)? = null): Job = 13 | CoroutineScope(Dispatchers.Main).launch { 14 | val data = CoroutineScope(Dispatchers.IO).async { 15 | return@async work() 16 | }.await() 17 | callback?.let { 18 | it(data) 19 | } 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches/build_file_checksums.ser 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | .DS_Store 9 | /build 10 | /captures 11 | .externalNativeBuild 12 | *.log 13 | .idea/* 14 | # Built application files 15 | build/ 16 | 17 | # Crashlytics configuations 18 | com_crashlytics_export_strings.xml 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Gradle generated files 24 | .gradle/ 25 | 26 | .idea/ 27 | 28 | # Signing files 29 | .signing/ 30 | 31 | # OS-specific files 32 | .DS_Store 33 | .DS_Store? 34 | ._* 35 | .Spotlight-V100 36 | .Trashes 37 | ehthumbs.db 38 | Thumbs.db 39 | 40 | # IntelliJ files 41 | *.iml 42 | *.log 43 | -------------------------------------------------------------------------------- /app/src/main/java/com/me/booksdemoandroid/shared/repositories/Resource.kt: -------------------------------------------------------------------------------- 1 | package com.me.booksdemoandroid.shared.repositories 2 | 3 | enum class Status { 4 | SUCCESS, 5 | ERROR, 6 | LOADING 7 | } 8 | 9 | data class Resource(val status: Status, val data: T?, val message: String?) { 10 | companion object { 11 | fun success(data: T?): Resource { 12 | return Resource(Status.SUCCESS, data, null) 13 | } 14 | 15 | fun error(msg: String, data: T?): Resource { 16 | return Resource(Status.ERROR, data, msg) 17 | } 18 | 19 | fun loading(data: T?): Resource { 20 | return Resource(Status.LOADING, data, null) 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /app/src/main/java/com/me/booksdemoandroid/feature/auth/helper/AuthNav.kt: -------------------------------------------------------------------------------- 1 | package com.me.booksdemoandroid.feature.auth.helper 2 | 3 | import android.content.Context 4 | import android.content.Intent 5 | import com.me.booksdemoandroid.feature.auth.activity.LoginActivity 6 | import com.me.booksdemoandroid.feature.auth.activity.RegisterActivity 7 | 8 | class AuthNav { 9 | companion object { 10 | fun showLoginActivity(context: Context): Intent { 11 | val i = Intent(context, LoginActivity::class.java) 12 | return i 13 | } 14 | 15 | fun showRegisterActivity(context: Context): Intent { 16 | val i = Intent(context, RegisterActivity::class.java) 17 | return i 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /app/src/main/java/com/me/booksdemoandroid/feature/home/activity/DetailBookActivity.kt: -------------------------------------------------------------------------------- 1 | package com.me.booksdemoandroid.feature.home.activity 2 | 3 | import androidx.appcompat.app.AppCompatActivity 4 | import android.os.Bundle 5 | import com.me.booksdemoandroid.R 6 | import com.me.booksdemoandroid.feature.home.helper.NavHome 7 | import com.me.booksdemoandroid.feature.home.model.Book 8 | 9 | class DetailBookActivity : AppCompatActivity() { 10 | 11 | lateinit var book: Book 12 | 13 | override fun onCreate(savedInstanceState: Bundle?) { 14 | super.onCreate(savedInstanceState) 15 | setContentView(R.layout.activity_detail_book) 16 | intent?.let { 17 | book = it.getParcelableExtra(NavHome.BOOK) 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/me/booksdemoandroid/shared/extension/Fragment.kt: -------------------------------------------------------------------------------- 1 | package com.me.booksdemoandroid.shared.extension 2 | 3 | import androidx.appcompat.app.AppCompatActivity 4 | import androidx.fragment.app.Fragment 5 | import androidx.fragment.app.FragmentManager 6 | import androidx.fragment.app.FragmentTransaction 7 | 8 | 9 | inline fun FragmentManager.inTransaction(func: FragmentTransaction.() -> FragmentTransaction) { 10 | beginTransaction().func().commit() 11 | } 12 | 13 | fun AppCompatActivity.addFragment(fragment: Fragment, frameId: Int) { 14 | supportFragmentManager.inTransaction { add(frameId, fragment) } 15 | } 16 | 17 | 18 | fun AppCompatActivity.replaceFragment(fragment: Fragment, frameId: Int) { 19 | supportFragmentManager.inTransaction { replace(frameId, fragment) } 20 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/toolbar.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 16 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/me/booksdemoandroid/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.me.booksdemoandroid 2 | 3 | import androidx.test.InstrumentationRegistry 4 | import androidx.test.runner.AndroidJUnit4 5 | import org.junit.Assert.assertEquals 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | /** 10 | * Instrumented test, which will execute on an Android device. 11 | * 12 | * See [testing documentation](http://d.android.com/tools/testing). 13 | */ 14 | @RunWith(AndroidJUnit4::class) 15 | class ExampleInstrumentedTest { 16 | @Test 17 | fun useAppContext() { 18 | // Context of the app under test. 19 | val appContext = InstrumentationRegistry.getTargetContext() 20 | assertEquals("com.digi.booksdemoandroid", appContext.packageName) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_close.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/me/booksdemoandroid/feature/home/helper/HomeRepository.kt: -------------------------------------------------------------------------------- 1 | package com.me.booksdemoandroid.feature.home.helper 2 | 3 | import com.me.booksdemoandroid.shared.repositories.BaseRepository 4 | import kotlinx.coroutines.Deferred 5 | import org.json.JSONObject 6 | import retrofit2.Response 7 | import retrofit2.http.Body 8 | import retrofit2.http.POST 9 | 10 | 11 | interface HomeApi { 12 | @POST("/books") 13 | fun booksAsync(@Body jsonObject: JSONObject): Deferred> 14 | 15 | } 16 | 17 | class HomeRepository(private val api: HomeApi) : BaseRepository() { 18 | 19 | suspend fun books(): String? { 20 | 21 | val obj = JSONObject() 22 | 23 | return safeApiCall( 24 | call = { api.booksAsync(obj).await() }, 25 | errorMessage = "Error Fetching Popular Movies" 26 | ) 27 | 28 | } 29 | 30 | } -------------------------------------------------------------------------------- /app/src/main/java/com/me/booksdemoandroid/feature/home/helper/NavHome.kt: -------------------------------------------------------------------------------- 1 | package com.me.booksdemoandroid.feature.home.helper 2 | 3 | import android.content.Context 4 | import android.content.Intent 5 | import com.me.booksdemoandroid.feature.home.activity.DetailBookActivity 6 | import com.me.booksdemoandroid.feature.home.activity.HomeActivity 7 | import com.me.booksdemoandroid.feature.home.model.Book 8 | 9 | class NavHome { 10 | companion object { 11 | 12 | val BOOK = "BOOK" 13 | 14 | fun showHomeActivity(context: Context): Intent { 15 | val i = Intent(context, HomeActivity::class.java) 16 | return i 17 | } 18 | 19 | fun showBookDetailActivity(context: Context,book: Book): Intent { 20 | val i = Intent(context, DetailBookActivity::class.java) 21 | i.putExtra(BOOK,book) 22 | return i 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /app/src/main/java/com/me/booksdemoandroid/App.kt: -------------------------------------------------------------------------------- 1 | package com.me.booksdemoandroid 2 | 3 | import android.app.Application 4 | import android.content.Context 5 | import androidx.appcompat.app.AppCompatDelegate 6 | 7 | //import androidx.multidex.MultiDex 8 | 9 | 10 | class App : Application() { 11 | 12 | init { 13 | INSTANCE = this 14 | } 15 | 16 | companion object { 17 | //var currentUser: User? = null 18 | 19 | private var INSTANCE: App? = null 20 | 21 | fun applicationContext(): Context { 22 | return INSTANCE!!.applicationContext 23 | } 24 | 25 | var isAuthenticated: Boolean = false 26 | 27 | fun setUser() { 28 | 29 | } 30 | } 31 | 32 | override fun onCreate() { 33 | super.onCreate() 34 | setUser() 35 | AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO) 36 | } 37 | 38 | override fun attachBaseContext(base: Context) { 39 | super.attachBaseContext(base) 40 | // MultiDex.install(base) 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 sadman samee 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /app/src/main/java/com/me/booksdemoandroid/shared/extension/ViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.me.booksdemoandroid.shared.extension 2 | 3 | import androidx.fragment.app.Fragment 4 | import androidx.fragment.app.FragmentActivity 5 | import androidx.lifecycle.ViewModel 6 | import androidx.lifecycle.ViewModelProvider 7 | import androidx.lifecycle.ViewModelProviders 8 | 9 | inline fun Fragment.getViewModel(noinline creator: (() -> T)? = null): T { 10 | return if (creator == null) 11 | ViewModelProviders.of(this).get(T::class.java) 12 | else 13 | ViewModelProviders.of(this, BaseViewModelFactory(creator)).get(T::class.java) 14 | } 15 | 16 | inline fun FragmentActivity.getViewModel(noinline creator: (() -> T)? = null): T { 17 | return if (creator == null) 18 | ViewModelProviders.of(this).get(T::class.java) 19 | else 20 | ViewModelProviders.of(this, BaseViewModelFactory(creator)).get(T::class.java) 21 | } 22 | 23 | class BaseViewModelFactory(val creator: () -> T) : ViewModelProvider.Factory { 24 | override fun create(modelClass: Class): T { 25 | return creator() as T 26 | } 27 | } -------------------------------------------------------------------------------- /app/src/main/java/com/me/booksdemoandroid/shared/repositories/BaseRepository.kt: -------------------------------------------------------------------------------- 1 | package com.me.booksdemoandroid.shared.repositories 2 | 3 | import android.util.Log 4 | import retrofit2.Response 5 | import java.io.IOException 6 | 7 | open class BaseRepository { 8 | 9 | suspend fun safeApiCall(call: suspend () -> Response, errorMessage: String): T? { 10 | 11 | val result: Result = safeApiResult(call, errorMessage) 12 | var data: T? = null 13 | 14 | when (result) { 15 | is Result.Success -> 16 | data = result.data 17 | is Result.Error -> { 18 | Log.d("DataRepository", "$errorMessage & Exception - ${result.exception}") 19 | } 20 | } 21 | 22 | 23 | return data 24 | 25 | } 26 | 27 | private suspend fun safeApiResult( 28 | call: suspend () -> Response, 29 | errorMessage: String 30 | ): Result { 31 | val response = call.invoke() 32 | if (response.isSuccessful) return Result.Success(response.body()!!) 33 | 34 | return Result.Error(IOException("Error Occurred during getting safe Api result, Custom ERROR - $errorMessage")) 35 | } 36 | } -------------------------------------------------------------------------------- /app/src/main/java/com/me/booksdemoandroid/feature/auth/activity/LoginActivity.kt: -------------------------------------------------------------------------------- 1 | package com.me.booksdemoandroid.feature.auth.activity 2 | 3 | import android.os.Bundle 4 | import android.util.Log 5 | import androidx.appcompat.app.AppCompatActivity 6 | import com.me.booksdemoandroid.R 7 | import com.me.booksdemoandroid.feature.auth.frag.LoginFragment 8 | import com.me.booksdemoandroid.feature.auth.helper.AuthNav 9 | import com.me.booksdemoandroid.shared.extension.addFragment 10 | import com.me.booksdemoandroid.shared.listner.OnBackPressedListener 11 | 12 | 13 | class LoginActivity : AppCompatActivity(), OnBackPressedListener { 14 | 15 | override fun onCreate(savedInstanceState: Bundle?) { 16 | super.onCreate(savedInstanceState) 17 | setContentView(R.layout.activity_login) 18 | Log.d("LoginActivity", "addFragment") 19 | if (savedInstanceState == null) { 20 | addFragment(LoginFragment.newInstance(0), R.id.container) 21 | } 22 | } 23 | 24 | //OnBackPressedListener 25 | override fun doBack() { 26 | onBackPressed() 27 | finish() 28 | } 29 | 30 | override fun doBackWithStart() { 31 | startActivity(AuthNav.showRegisterActivity(this)) 32 | finish() 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx1536m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app's APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Automatically convert third-party libraries to use AndroidX 19 | android.enableJetifier=true 20 | # Kotlin code style for this project: "official" or "obsolete": 21 | kotlin.code.style=official 22 | android.enableR8=true 23 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /app/src/main/java/com/me/booksdemoandroid/feature/splash/SplashActivity.kt: -------------------------------------------------------------------------------- 1 | package com.me.booksdemoandroid.feature.splash 2 | 3 | import android.os.Bundle 4 | import androidx.appcompat.app.AppCompatActivity 5 | import com.me.booksdemoandroid.App 6 | import com.me.booksdemoandroid.R 7 | import com.me.booksdemoandroid.feature.auth.helper.AuthNav 8 | import com.me.booksdemoandroid.feature.home.helper.NavHome 9 | import com.me.booksdemoandroid.shared.helper.PreferenceHelper 10 | import com.me.booksdemoandroid.shared.helper.PreferenceHelper.get 11 | import com.me.booksdemoandroid.shared.k.KEnum 12 | import com.me.booksdemoandroid.shared.pref.Pref 13 | 14 | class SplashActivity : AppCompatActivity() { 15 | 16 | override fun onCreate(savedInstanceState: Bundle?) { 17 | super.onCreate(savedInstanceState) 18 | setContentView(R.layout.activity_splash) 19 | 20 | val prefs = PreferenceHelper.defaultPrefs(this) 21 | val token: String? = prefs[KEnum.Companion.SharedPref.Token.name] 22 | 23 | if (token != null) { 24 | Pref.token = token 25 | App.isAuthenticated = true 26 | startActivity(NavHome.showHomeActivity(this)) 27 | finish() 28 | } else { 29 | startActivity(AuthNav.showLoginActivity(this)) 30 | finish() 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/src/main/java/com/me/booksdemoandroid/feature/auth/helper/AuthRepository.kt: -------------------------------------------------------------------------------- 1 | package com.me.booksdemoandroid.feature.auth.helper 2 | 3 | import com.me.booksdemoandroid.shared.repositories.BaseRepository 4 | import kotlinx.coroutines.Deferred 5 | import org.json.JSONObject 6 | import retrofit2.Response 7 | import retrofit2.http.Body 8 | import retrofit2.http.POST 9 | 10 | interface AuthApi { 11 | @POST("/login") 12 | fun loginAsync(@Body jsonObject: JSONObject): Deferred> 13 | 14 | @POST("/signup") 15 | fun signUpAsync(@Body jsonObject: JSONObject): Deferred> 16 | 17 | } 18 | 19 | 20 | class AuthRepository(private val api: AuthApi) : BaseRepository() { 21 | 22 | suspend fun login(email: String, password: String): String? { 23 | 24 | val obj = JSONObject() 25 | obj.put("email", email) 26 | obj.put("password", password) 27 | 28 | return safeApiCall( 29 | call = { api.loginAsync(obj).await() }, 30 | errorMessage = "Error Fetching Popular Movies" 31 | ) 32 | 33 | } 34 | 35 | suspend fun signUp(name: String, email: String, password: String): String? { 36 | 37 | val obj = JSONObject() 38 | obj.put("name", name) 39 | obj.put("email", email) 40 | obj.put("password", password) 41 | 42 | return safeApiCall( 43 | call = { api.signUpAsync(obj).await() }, 44 | errorMessage = "Error Fetching Popular Movies" 45 | ) 46 | 47 | } 48 | } -------------------------------------------------------------------------------- /app/src/main/java/com/me/booksdemoandroid/shared/fragment/BaseFragment.kt: -------------------------------------------------------------------------------- 1 | package com.me.booksdemoandroid.shared.fragment 2 | 3 | 4 | import android.content.Context 5 | import android.os.Bundle 6 | import android.view.LayoutInflater 7 | import android.view.View 8 | import android.view.ViewGroup 9 | import androidx.fragment.app.Fragment 10 | import com.me.booksdemoandroid.R 11 | 12 | /** 13 | * Created by sadmansamee on 3/25/18. 14 | */ 15 | open class BaseFragment : Fragment() { 16 | 17 | lateinit var mFragmentNavigation: FragmentNavigation 18 | internal var mInt = 0 19 | 20 | override fun onCreate(savedInstanceState: Bundle?) { 21 | super.onCreate(savedInstanceState) 22 | val args = arguments 23 | if (args != null) { 24 | mInt = args.getInt(ARGS_INSTANCE) 25 | } 26 | } 27 | 28 | 29 | override fun onCreateView( 30 | inflater: LayoutInflater, 31 | container: ViewGroup?, 32 | savedInstanceState: Bundle? 33 | ): View? { 34 | return inflater.inflate(R.layout.fragment_main, container, false); 35 | } 36 | 37 | override fun onAttach(context: Context) { 38 | super.onAttach(context) 39 | if (context is FragmentNavigation) { 40 | mFragmentNavigation = context 41 | } 42 | } 43 | 44 | interface FragmentNavigation { 45 | fun pushFragment(fragment: androidx.fragment.app.Fragment) 46 | } 47 | 48 | companion object { 49 | val ARGS_INSTANCE = "com.gigi.argsInstance" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /app/src/main/java/com/me/booksdemoandroid/shared/helper/Connectivity.kt: -------------------------------------------------------------------------------- 1 | package com.me.booksdemoandroid.shared.helper 2 | 3 | import android.content.Context 4 | import android.net.ConnectivityManager 5 | import android.net.NetworkInfo 6 | import android.util.Log 7 | 8 | class Connectivity { 9 | private lateinit var connectivityManager: ConnectivityManager 10 | internal var wifiInfo: NetworkInfo? = null 11 | internal var mobileInfo: NetworkInfo? = null 12 | private var connected = false 13 | 14 | val isOnline: Boolean 15 | get() { 16 | try { 17 | connectivityManager = context 18 | .getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager 19 | 20 | val networkInfo = connectivityManager.activeNetworkInfo 21 | connected = networkInfo != null && networkInfo.isAvailable && 22 | networkInfo.isConnected 23 | return connected 24 | 25 | } catch (e: Exception) { 26 | println("CheckConnectivity Exception: " + e.message) 27 | Log.v("connectivity", e.toString()) 28 | } 29 | 30 | return connected 31 | } 32 | 33 | companion object { 34 | 35 | internal lateinit var context: Context 36 | 37 | 38 | private val instance = Connectivity() 39 | 40 | fun getInstance(ctx: Context): Connectivity { 41 | context = ctx.applicationContext 42 | return instance 43 | } 44 | } 45 | } 46 | 47 | -------------------------------------------------------------------------------- /app/src/main/java/com/me/booksdemoandroid/feature/auth/activity/RegisterActivity.kt: -------------------------------------------------------------------------------- 1 | package com.me.booksdemoandroid.feature.auth.activity 2 | 3 | import android.os.Bundle 4 | import android.view.View 5 | import androidx.appcompat.app.AppCompatActivity 6 | import com.me.booksdemoandroid.R 7 | import com.me.booksdemoandroid.feature.auth.frag.RegisterFragment 8 | import com.me.booksdemoandroid.feature.auth.helper.AuthNav 9 | import com.me.booksdemoandroid.shared.extension.addFragment 10 | import com.me.booksdemoandroid.shared.listner.OnBackPressedListener 11 | import kotlinx.android.synthetic.main.toolbar.* 12 | 13 | 14 | class RegisterActivity : AppCompatActivity(), OnBackPressedListener { 15 | 16 | 17 | override fun onCreate(savedInstanceState: Bundle?) { 18 | super.onCreate(savedInstanceState) 19 | setContentView(R.layout.activity_register) 20 | setToolBar("") 21 | 22 | 23 | if (savedInstanceState == null) { 24 | // Log.d(TAG,"addFragment") 25 | addFragment(RegisterFragment.newInstance(0), R.id.container) 26 | } 27 | } 28 | 29 | private fun setToolBar(title: String) { 30 | val decorView = window.decorView 31 | val uiOptions = View.SYSTEM_UI_FLAG_VISIBLE 32 | decorView.systemUiVisibility = uiOptions 33 | setSupportActionBar(toolbar) 34 | supportActionBar?.let { 35 | it.setDisplayHomeAsUpEnabled(true) 36 | it.setDisplayShowHomeEnabled(true) 37 | it.title = title 38 | } 39 | toolbar.setNavigationOnClickListener { onBackPressed() } 40 | 41 | } 42 | 43 | //OnBackPressedListener 44 | override fun doBack() { 45 | onBackPressed() 46 | finish() 47 | } 48 | 49 | override fun doBackWithStart() { 50 | startActivity(AuthNav.showLoginActivity(this)) 51 | finish() 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/me/booksdemoandroid/feature/home/vm/HomeViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.me.booksdemoandroid.feature.home.vm 2 | 3 | import androidx.annotation.UiThread 4 | import androidx.lifecycle.LiveData 5 | import androidx.lifecycle.MutableLiveData 6 | import androidx.lifecycle.ViewModel 7 | import com.me.booksdemoandroid.feature.home.helper.HomeRepository 8 | import com.me.booksdemoandroid.feature.home.model.Book 9 | import com.me.booksdemoandroid.shared.extension.Coroutines 10 | import com.me.booksdemoandroid.shared.repositories.ApiFactory 11 | import com.me.booksdemoandroid.shared.repositories.Resource 12 | import com.squareup.moshi.Moshi 13 | import org.json.JSONObject 14 | 15 | class HomeViewModel : ViewModel() { 16 | 17 | // private val _loading = MutableLiveData() 18 | // val loading: LiveData get() = _loading 19 | private val repository = HomeRepository(ApiFactory.homeApi) 20 | // private val _result = MutableLiveData>() 21 | // val result: LiveData> get() = _result 22 | private val _books = MutableLiveData>>() 23 | val books: LiveData>> get() = _books 24 | private val moshi = Moshi.Builder().build() 25 | 26 | @UiThread 27 | fun fetchBooks() { 28 | //_loading.value = true 29 | Coroutines.ioThenMain({ 30 | repository.books() 31 | }) { 32 | it?.let { it -> 33 | // _loading.value = false 34 | val jsonObject = JSONObject(it) 35 | if (!jsonObject.optBoolean("error")) { 36 | val jsonAdapter = moshi.adapter>(Array::class.java) 37 | _books.value = 38 | Resource.success(jsonAdapter.fromJson(jsonObject.optJSONArray("data").toString())) 39 | 40 | } else { 41 | _books.value = Resource.error(jsonObject.optString("message"), null) 42 | } 43 | } 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /app/src/main/java/com/me/booksdemoandroid/feature/auth/vm/AuthViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.me.booksdemoandroid.feature.auth.vm 2 | 3 | import androidx.annotation.UiThread 4 | import androidx.lifecycle.LiveData 5 | import androidx.lifecycle.MutableLiveData 6 | import androidx.lifecycle.ViewModel 7 | import com.me.booksdemoandroid.feature.auth.helper.AuthRepository 8 | import com.me.booksdemoandroid.shared.extension.Coroutines 9 | import com.me.booksdemoandroid.shared.repositories.ApiFactory 10 | import com.me.booksdemoandroid.shared.repositories.Resource 11 | import org.json.JSONObject 12 | 13 | 14 | class AuthViewModel : ViewModel() { 15 | 16 | private val repository = AuthRepository(ApiFactory.authApi) 17 | 18 | //private lateinit var result: MutableLiveData> 19 | 20 | private val _result = MutableLiveData>() 21 | val result: LiveData> get() = _result 22 | 23 | @UiThread 24 | fun login(email: String, password: String) { 25 | // _loading.value = true 26 | Coroutines.ioThenMain({ 27 | repository.login(email, password) 28 | }) { 29 | it?.let { 30 | //_loading.value = false 31 | val jsonObject = JSONObject(it) 32 | if (!jsonObject.optBoolean("error")) { 33 | _result.value = Resource.success(jsonObject.optString("token")) 34 | } else { 35 | _result.value = Resource.error(jsonObject.optString("message"), null) 36 | } 37 | } 38 | } 39 | } 40 | 41 | @UiThread 42 | fun signUp(name: String, email: String, password: String) { 43 | // _loading.value = true 44 | Coroutines.ioThenMain({ 45 | repository.signUp(name, email, password) 46 | }) { 47 | it?.let { 48 | //_loading.value = false 49 | //result = MutableLiveData() 50 | val jsonObject = JSONObject(it) 51 | if (!jsonObject.optBoolean("error")) { 52 | _result.value = Resource.success(jsonObject.optString("message")) 53 | } else { 54 | _result.value = Resource.error(jsonObject.optString("message"), null) 55 | } 56 | } 57 | } 58 | // return result 59 | } 60 | } -------------------------------------------------------------------------------- /app/src/main/java/com/me/booksdemoandroid/shared/helper/PreferenceHelper.kt: -------------------------------------------------------------------------------- 1 | package com.me.booksdemoandroid.shared.helper 2 | 3 | import android.content.Context 4 | import android.content.SharedPreferences 5 | import android.preference.PreferenceManager 6 | 7 | 8 | object PreferenceHelper { 9 | 10 | fun defaultPrefs(context: Context): SharedPreferences = 11 | PreferenceManager.getDefaultSharedPreferences(context) 12 | 13 | fun customPrefs(context: Context, name: String): SharedPreferences = 14 | context.getSharedPreferences(name, Context.MODE_PRIVATE) 15 | 16 | private inline fun SharedPreferences.edit(operation: (SharedPreferences.Editor) -> Unit) { 17 | val editor = this.edit() 18 | operation(editor) 19 | editor.apply() 20 | } 21 | 22 | /** 23 | * puts a key value pair in shared prefs if doesn't exists, otherwise updates value on given [key] 24 | */ 25 | operator fun SharedPreferences.set(key: String, value: Any?) { 26 | when (value) { 27 | is String? -> edit { it.putString(key, value) } 28 | is Int -> edit { it.putInt(key, value) } 29 | is Boolean -> edit { it.putBoolean(key, value) } 30 | is Float -> edit { it.putFloat(key, value) } 31 | is Long -> edit { it.putLong(key, value) } 32 | else -> throw UnsupportedOperationException("Not yet implemented") 33 | } 34 | } 35 | 36 | /** 37 | * finds value on given key. 38 | * [T] is the type of value 39 | * @param defaultValue optional default value - will take null for strings, false for bool and -1 for numeric values if [defaultValue] is not specified 40 | */ 41 | inline operator fun SharedPreferences.get( 42 | key: String, 43 | defaultValue: T? = null 44 | ): T? { 45 | return when (T::class) { 46 | String::class -> getString(key, defaultValue as? String) as T? 47 | Int::class -> getInt(key, defaultValue as? Int ?: -1) as T? 48 | Boolean::class -> getBoolean(key, defaultValue as? Boolean ?: false) as T? 49 | Float::class -> getFloat(key, defaultValue as? Float ?: -1f) as T? 50 | Long::class -> getLong(key, defaultValue as? Long ?: -1) as T? 51 | else -> throw UnsupportedOperationException("Not yet implemented") 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /app/src/main/java/com/me/booksdemoandroid/shared/repositories/RetrofitFactory.kt: -------------------------------------------------------------------------------- 1 | package com.me.booksdemoandroid.shared.repositories 2 | 3 | import com.jakewharton.retrofit2.adapter.kotlin.coroutines.CoroutineCallAdapterFactory 4 | import com.me.booksdemoandroid.BuildConfig 5 | import com.me.booksdemoandroid.shared.pref.Pref 6 | 7 | import okhttp3.Interceptor 8 | import okhttp3.OkHttpClient 9 | import okhttp3.logging.HttpLoggingInterceptor 10 | import retrofit2.Retrofit 11 | import retrofit2.converter.moshi.MoshiConverterFactory 12 | 13 | 14 | object RetrofitFactory { 15 | 16 | private val authInterceptor = Interceptor { chain -> 17 | 18 | val accessToken = Pref.token 19 | 20 | val newUrl = chain.request().url 21 | .newBuilder() 22 | //.addQueryParameter("api_key", AppConstants.tmdbApiKey) 23 | .build() 24 | 25 | val newRequest = chain.request() 26 | .newBuilder() 27 | .url(newUrl) 28 | .addHeader("Authorization", "Bearer $accessToken") 29 | .build() 30 | 31 | chain.proceed(newRequest) 32 | } 33 | 34 | 35 | private val loggingInterceptor = HttpLoggingInterceptor().apply { 36 | level = HttpLoggingInterceptor.Level.BODY 37 | } 38 | 39 | private val headersInterceptor = Interceptor { chain -> 40 | chain.proceed( 41 | chain.request().newBuilder() 42 | .addHeader("Accept", "application/json") 43 | .addHeader("Content-Type", "application/json") 44 | .build() 45 | ) 46 | } 47 | 48 | //Not logging the authkey if not debug 49 | private val client = 50 | if (BuildConfig.DEBUG) { 51 | OkHttpClient().newBuilder() 52 | .addInterceptor(authInterceptor) 53 | .addInterceptor(loggingInterceptor) 54 | .addInterceptor(headersInterceptor) 55 | .build() 56 | } else { 57 | OkHttpClient().newBuilder() 58 | .addInterceptor(loggingInterceptor) 59 | .addInterceptor(authInterceptor) 60 | .addInterceptor(headersInterceptor) 61 | .build() 62 | } 63 | 64 | 65 | fun retrofit(baseUrl: String): Retrofit = Retrofit.Builder() 66 | .client(client) 67 | .baseUrl(baseUrl) 68 | .addConverterFactory(MoshiConverterFactory.create()) 69 | .addCallAdapterFactory(CoroutineCallAdapterFactory()) 70 | .build() 71 | 72 | } 73 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /app/src/main/java/com/me/booksdemoandroid/feature/home/adapter/BookAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.me.booksdemoandroid.feature.home.adapter 2 | 3 | import android.content.Context 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import androidx.recyclerview.widget.RecyclerView 8 | import com.me.booksdemoandroid.R 9 | import com.me.booksdemoandroid.feature.home.model.Book 10 | import com.squareup.picasso.Picasso 11 | import kotlinx.android.synthetic.main.row_book.view.* 12 | 13 | 14 | class BookAdapter( 15 | private val mContext: Context, 16 | private val clickListener: (Book) -> Unit 17 | ) : 18 | androidx.recyclerview.widget.RecyclerView.Adapter() { 19 | 20 | private var data: ArrayList = ArrayList() 21 | 22 | fun setData(newData: ArrayList) { 23 | data = newData 24 | notifyDataSetChanged() 25 | } 26 | 27 | fun addData(newData: ArrayList) { 28 | data.addAll(newData) 29 | notifyDataSetChanged() 30 | } 31 | 32 | fun clear() { 33 | data.clear() 34 | notifyDataSetChanged() 35 | } 36 | 37 | class ItemViewHolder(v: View) : RecyclerView.ViewHolder(v) { 38 | 39 | val view: View = v 40 | 41 | init { 42 | //view.setOnClickListener() 43 | } 44 | 45 | fun bind( 46 | position: Int, book: Book, clickListener: (Book) -> Unit 47 | ) { 48 | itemView.setOnClickListener { clickListener(book) } 49 | itemView.txtViewTitle.text = book.title 50 | Picasso.get().load(book.preview).into(itemView.imageViewBook) 51 | 52 | } 53 | } 54 | 55 | override fun onCreateViewHolder( 56 | parent: ViewGroup, 57 | viewType: Int 58 | ): ItemViewHolder { 59 | val view = LayoutInflater.from(mContext).inflate(R.layout.row_book, parent, false) 60 | return BookAdapter.ItemViewHolder(view) 61 | } 62 | 63 | override fun onBindViewHolder( 64 | rawHolder: androidx.recyclerview.widget.RecyclerView.ViewHolder, 65 | position: Int 66 | ) { 67 | val holder = rawHolder as ItemViewHolder 68 | holder.bind(position, data[position], clickListener) 69 | holder.view.tag = position 70 | } 71 | 72 | override fun getItemCount(): Int { 73 | return data.size 74 | } 75 | 76 | override fun getItemId(position: Int): Long { 77 | return position.toLong() 78 | } 79 | 80 | override fun getItemViewType(position: Int): Int { 81 | return position 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /app/src/main/java/com/me/booksdemoandroid/feature/home/activity/HomeActivity.kt: -------------------------------------------------------------------------------- 1 | package com.me.booksdemoandroid.feature.home.activity 2 | 3 | import android.os.Bundle 4 | import androidx.appcompat.app.AppCompatActivity 5 | import androidx.lifecycle.Observer 6 | import com.google.android.material.snackbar.Snackbar 7 | import com.me.booksdemoandroid.R 8 | import com.me.booksdemoandroid.feature.home.adapter.BookAdapter 9 | import com.me.booksdemoandroid.feature.home.helper.NavHome 10 | import com.me.booksdemoandroid.feature.home.model.Book 11 | import com.me.booksdemoandroid.feature.home.vm.HomeViewModel 12 | import com.me.booksdemoandroid.shared.extension.getViewModel 13 | import com.me.booksdemoandroid.shared.extension.showSnackBar 14 | import com.me.booksdemoandroid.shared.repositories.Status 15 | import kotlinx.android.synthetic.main.activity_home.* 16 | import kotlinx.android.synthetic.main.fragment_register.* 17 | 18 | class HomeActivity : AppCompatActivity() { 19 | 20 | private lateinit var bookAdapter: BookAdapter 21 | 22 | private val vm: HomeViewModel by lazy { 23 | getViewModel { HomeViewModel() } 24 | } 25 | 26 | override fun onCreate(savedInstanceState: Bundle?) { 27 | super.onCreate(savedInstanceState) 28 | setContentView(R.layout.activity_home) 29 | setUI() 30 | setVM() 31 | } 32 | 33 | private fun setVM() { 34 | 35 | // vm.loading.observe(this, Observer { 36 | // if (it) { 37 | // 38 | // } else { 39 | // 40 | // } 41 | // }) 42 | // 43 | // vm.result.observe(this, Observer { 44 | // if (it.first) { 45 | // 46 | // } 47 | // }) 48 | 49 | vm.books.observe(this, Observer { 50 | 51 | when (it?.status) { 52 | Status.LOADING -> { 53 | 54 | } 55 | Status.SUCCESS -> { 56 | it.data?.let { it -> 57 | bookAdapter.setData(it) 58 | } 59 | } 60 | Status.ERROR -> { 61 | it.message?.let { message -> 62 | container.showSnackBar(message, Snackbar.LENGTH_SHORT) 63 | } 64 | 65 | } 66 | } 67 | }) 68 | vm.fetchBooks() 69 | } 70 | 71 | private fun setUI() { 72 | recyclerView.setHasFixedSize(true) 73 | recyclerView.layoutManager = androidx.recyclerview.widget.LinearLayoutManager( 74 | this, 75 | androidx.recyclerview.widget.RecyclerView.VERTICAL, 76 | false 77 | ) 78 | bookAdapter = BookAdapter(this) { book: Book -> 79 | NavHome.showBookDetailActivity(this,book) 80 | } 81 | recyclerView.adapter = bookAdapter 82 | } 83 | 84 | 85 | } 86 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | apply plugin: 'kotlin-android' 4 | 5 | apply plugin: 'kotlin-android-extensions' 6 | apply plugin: 'kotlin-kapt' 7 | 8 | android { 9 | compileSdkVersion 28 10 | defaultConfig { 11 | applicationId "com.me.booksdemoandroid" 12 | minSdkVersion 18 13 | targetSdkVersion 28 14 | versionCode 1 15 | versionName "1.0" 16 | vectorDrawables.useSupportLibrary = true 17 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 18 | } 19 | 20 | buildTypes { 21 | debug { 22 | minifyEnabled false 23 | useProguard false 24 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 25 | } 26 | release { 27 | minifyEnabled true 28 | useProguard true 29 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 30 | testProguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguardTest-rules.pro' 31 | } 32 | } 33 | flavorDimensions "default" 34 | productFlavors { 35 | mock { 36 | applicationIdSuffix = ".mock" 37 | dimension "default" 38 | } 39 | prod { 40 | dimension "default" 41 | } 42 | } 43 | testOptions.unitTests.all { 44 | testLogging { 45 | events 'passed', 'skipped', 'failed', 'standardOut', 'standardError' 46 | } 47 | } 48 | androidExtensions { 49 | experimental = true 50 | } 51 | } 52 | 53 | dependencies { 54 | 55 | def lifecycle_version = "2.0.0" 56 | 57 | def moshiVersion = "1.8.0" 58 | def retrofit2_version = "2.6.0" 59 | def okhttp3_version = "4.0.0-RC1" 60 | def kotlinCoroutineVersion = "1.3.0-M1" 61 | def picassoVersion = "2.71828" 62 | 63 | def junitVersion = "4.12" 64 | def hamcrestVersion = "1.3" 65 | def mockitoVersion = "2.8.47" 66 | def roboelectricVersion = "4.2" 67 | def espressoVersion = "3.0.2" 68 | 69 | 70 | implementation fileTree(dir: 'libs', include: ['*.jar']) 71 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 72 | implementation 'androidx.appcompat:appcompat:1.1.0-beta01' 73 | implementation 'androidx.core:core-ktx:1.2.0-alpha01' 74 | implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta1' 75 | implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version" 76 | implementation 'androidx.vectordrawable:vectordrawable:1.0.1' 77 | implementation 'com.google.android.material:material:1.1.0-alpha07' 78 | implementation "com.wajahatkarim3.easyvalidation:easyvalidation-core:1.0.1" 79 | 80 | //Moshi 81 | implementation "com.squareup.moshi:moshi-kotlin:$moshiVersion" 82 | kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshiVersion" 83 | 84 | //Retrofit2 85 | implementation "com.squareup.retrofit2:retrofit:$retrofit2_version" 86 | implementation "com.squareup.retrofit2:converter-moshi:$retrofit2_version" 87 | implementation "com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2" 88 | 89 | //Okhttp3 90 | implementation "com.squareup.okhttp3:okhttp:$okhttp3_version" 91 | implementation 'com.squareup.okhttp3:logging-interceptor:3.12.1' 92 | 93 | //Picasso for Image Loading 94 | implementation("com.squareup.picasso:picasso:$picassoVersion") { 95 | exclude group: "com.android.support" 96 | } 97 | 98 | //Kotlin Coroutines 99 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlinCoroutineVersion" 100 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinCoroutineVersion" 101 | testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$kotlinCoroutineVersion" 102 | 103 | 104 | annotationProcessor "android.arch.lifecycle:compiler:1.1.1" 105 | androidTestImplementation 'androidx.test:runner:1.2.0' 106 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' 107 | 108 | // Dependencies for local unit tests 109 | testImplementation "junit:junit:$junitVersion" 110 | testImplementation "org.hamcrest:hamcrest-all:$hamcrestVersion" 111 | testImplementation "org.mockito:mockito-core:$mockitoVersion" 112 | testImplementation "org.robolectric:robolectric:$roboelectricVersion" 113 | 114 | // Dependencies for Android unit tests 115 | androidTestImplementation "junit:junit:$junitVersion" 116 | androidTestImplementation "org.mockito:mockito-android:$mockitoVersion" 117 | 118 | // Espresso UI Testing 119 | androidTestImplementation "com.android.support.test.espresso:espresso-core:$espressoVersion" 120 | androidTestImplementation "com.android.support.test.espresso:espresso-contrib:$espressoVersion" 121 | androidTestImplementation "com.android.support.test.espresso:espresso-intents:$espressoVersion" 122 | androidTestImplementation 'com.jakewharton.espresso:okhttp3-idling-resource:1.0.0' 123 | androidTestImplementation "com.android.support.test.espresso.idling:idling-concurrent:$espressoVersion" 124 | 125 | 126 | } 127 | -------------------------------------------------------------------------------- /app/src/main/java/com/me/booksdemoandroid/feature/auth/frag/RegisterFragment.kt: -------------------------------------------------------------------------------- 1 | package com.me.booksdemoandroid.feature.auth.frag 2 | 3 | 4 | import android.content.Context 5 | import android.os.Bundle 6 | import android.view.LayoutInflater 7 | import android.view.View 8 | import android.view.ViewGroup 9 | import androidx.lifecycle.Observer 10 | import com.google.android.material.snackbar.Snackbar 11 | import com.me.booksdemoandroid.R 12 | import com.me.booksdemoandroid.feature.auth.vm.AuthViewModel 13 | import com.me.booksdemoandroid.shared.extension.getViewModel 14 | import com.me.booksdemoandroid.shared.extension.showSnackBar 15 | import com.me.booksdemoandroid.shared.fragment.BaseFragment 16 | import com.me.booksdemoandroid.shared.listner.OnBackPressedListener 17 | import com.me.booksdemoandroid.shared.repositories.Status 18 | import com.wajahatkarim3.easyvalidation.core.collection_ktx.minLengthList 19 | import com.wajahatkarim3.easyvalidation.core.collection_ktx.nonEmptyList 20 | import com.wajahatkarim3.easyvalidation.core.collection_ktx.validEmailList 21 | import kotlinx.android.synthetic.main.fragment_register.* 22 | 23 | class RegisterFragment : BaseFragment() { 24 | 25 | private var onBackPressedListener: OnBackPressedListener? = null 26 | 27 | 28 | private val vm: AuthViewModel by lazy { 29 | getViewModel { AuthViewModel() } 30 | } 31 | 32 | override fun onCreate(savedInstanceState: Bundle?) { 33 | super.onCreate(savedInstanceState) 34 | arguments?.let { 35 | 36 | } 37 | setUI() 38 | setVM() 39 | } 40 | 41 | override fun onCreateView( 42 | inflater: LayoutInflater, container: ViewGroup?, 43 | savedInstanceState: Bundle? 44 | ): View? { 45 | // Inflate the layout for this fragment 46 | return inflater.inflate(R.layout.fragment_register, container, false) 47 | } 48 | 49 | override fun onAttach(context: Context) { 50 | super.onAttach(context) 51 | if (context is OnBackPressedListener) { 52 | onBackPressedListener = context 53 | } else { 54 | throw RuntimeException("$context must implement OnFragmentInteractionListener") 55 | } 56 | } 57 | 58 | override fun onDetach() { 59 | super.onDetach() 60 | onBackPressedListener = null 61 | } 62 | 63 | 64 | override fun onActivityCreated(savedInstanceState: Bundle?) { 65 | super.onActivityCreated(savedInstanceState) 66 | 67 | setUI() 68 | } 69 | 70 | private fun setUI() { 71 | 72 | btnRegister.setOnClickListener { 73 | register() 74 | } 75 | 76 | btnLogin.setOnClickListener { 77 | onBackPressedListener?.doBackWithStart() 78 | } 79 | } 80 | 81 | private fun setVM() { 82 | // vm.isLoading.observe(this, Observer { 83 | // if (it) { 84 | // 85 | // } else { 86 | // 87 | // } 88 | // }) 89 | 90 | vm.result.observe(this, Observer { 91 | 92 | when (it?.status) { 93 | Status.LOADING -> { 94 | 95 | } 96 | Status.SUCCESS -> { 97 | onBackPressedListener?.doBack() 98 | } 99 | Status.ERROR -> { 100 | it.message?.let { message -> 101 | container.showSnackBar(message, Snackbar.LENGTH_SHORT) 102 | } 103 | 104 | } 105 | } 106 | 107 | // if (it.first) { 108 | // onBackPressedListener?.doBack() 109 | // } 110 | }) 111 | } 112 | 113 | 114 | private fun register() { 115 | 116 | var isValid = true 117 | 118 | 119 | nonEmptyList(editTextUserName, editTextMail, editTextPassword) { view, msg -> 120 | view.error = msg 121 | isValid = false 122 | 123 | } 124 | 125 | minLengthList( 126 | 5, 127 | editTextUserName, 128 | editTextMail, 129 | editTextPassword 130 | ) { view, msg -> 131 | view.error = msg 132 | isValid = false 133 | } 134 | 135 | 136 | minLengthList( 137 | 6, 138 | editTextPassword 139 | ) { view, msg -> 140 | view.error = msg 141 | isValid = false 142 | } 143 | 144 | 145 | validEmailList(editTextMail) { view, msg -> 146 | view.error = msg 147 | isValid = false 148 | } 149 | 150 | // numberEqualToList(11, editTextICNumber) { view, msg -> 151 | // view.error = msg 152 | // isValid = false 153 | // } 154 | if (!isValid) { 155 | return 156 | } 157 | 158 | vm.signUp( 159 | editTextUserName.text.trim().toString(), 160 | editTextMail.text.trim().toString(), 161 | editTextPassword.text.trim().toString() 162 | ) 163 | } 164 | 165 | 166 | companion object { 167 | 168 | fun newInstance(instance: Int): RegisterFragment { 169 | val args = Bundle() 170 | args.putInt(BaseFragment.ARGS_INSTANCE, instance) 171 | val fragment = RegisterFragment() 172 | fragment.arguments = args 173 | return fragment 174 | } 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /app/src/main/java/com/me/booksdemoandroid/feature/auth/frag/LoginFragment.kt: -------------------------------------------------------------------------------- 1 | package com.me.booksdemoandroid.feature.auth.frag 2 | 3 | import android.content.Context 4 | import android.os.Bundle 5 | import android.view.LayoutInflater 6 | import android.view.View 7 | import android.view.ViewGroup 8 | import android.view.WindowManager 9 | import androidx.lifecycle.Observer 10 | import com.google.android.material.snackbar.Snackbar 11 | import com.me.booksdemoandroid.R 12 | import com.me.booksdemoandroid.feature.auth.vm.AuthViewModel 13 | import com.me.booksdemoandroid.feature.home.helper.NavHome 14 | import com.me.booksdemoandroid.shared.extension.getViewModel 15 | import com.me.booksdemoandroid.shared.extension.showSnackBar 16 | import com.me.booksdemoandroid.shared.fragment.BaseFragment 17 | import com.me.booksdemoandroid.shared.helper.PreferenceHelper 18 | import com.me.booksdemoandroid.shared.helper.PreferenceHelper.set 19 | import com.me.booksdemoandroid.shared.k.KEnum 20 | import com.me.booksdemoandroid.shared.listner.OnBackPressedListener 21 | import com.me.booksdemoandroid.shared.pref.Pref 22 | import com.me.booksdemoandroid.shared.repositories.Status 23 | import com.wajahatkarim3.easyvalidation.core.collection_ktx.minLengthList 24 | import com.wajahatkarim3.easyvalidation.core.collection_ktx.nonEmptyList 25 | import kotlinx.android.synthetic.main.fragment_login.* 26 | 27 | 28 | class LoginFragment : BaseFragment() { 29 | 30 | private var onBackPressedListener: OnBackPressedListener? = null 31 | 32 | private val vm: AuthViewModel by lazy { 33 | getViewModel { AuthViewModel() } 34 | } 35 | 36 | override fun onCreate(savedInstanceState: Bundle?) { 37 | super.onCreate(savedInstanceState) 38 | activity!!.window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN) 39 | arguments?.let { 40 | 41 | } 42 | setUI() 43 | setVM() 44 | } 45 | 46 | override fun onCreateView( 47 | inflater: LayoutInflater, container: ViewGroup?, 48 | savedInstanceState: Bundle? 49 | ): View? { 50 | // Inflate the layout for this fragment 51 | return inflater.inflate(R.layout.fragment_login, container, false) 52 | } 53 | 54 | override fun onAttach(context: Context) { 55 | super.onAttach(context) 56 | if (context is OnBackPressedListener) { 57 | onBackPressedListener = context 58 | } else { 59 | throw RuntimeException("$context must implement OnFragmentInteractionListener") 60 | } 61 | } 62 | 63 | override fun onDetach() { 64 | super.onDetach() 65 | onBackPressedListener = null 66 | } 67 | 68 | override fun onActivityCreated(savedInstanceState: Bundle?) { 69 | super.onActivityCreated(savedInstanceState) 70 | setUI() 71 | } 72 | 73 | private fun setUI() { 74 | 75 | 76 | btnRegister.setOnClickListener { 77 | onBackPressedListener?.doBackWithStart() 78 | } 79 | 80 | login.setOnClickListener { 81 | login() 82 | } 83 | 84 | } 85 | 86 | private fun setVM() { 87 | 88 | // vm.isLoading.observe(this, Observer { 89 | // if (it) { 90 | // 91 | // } else { 92 | // 93 | // } 94 | // }) 95 | 96 | vm.result.observe(this, Observer { 97 | 98 | when (it?.status) { 99 | Status.LOADING -> { 100 | 101 | } 102 | Status.SUCCESS -> { 103 | it.data?.let { it -> 104 | Pref.token = it 105 | val prefs = PreferenceHelper.defaultPrefs(context!!) 106 | prefs[KEnum.Companion.SharedPref.Token.name] = it 107 | context!!.startActivity(NavHome.showHomeActivity(context!!)) 108 | activity!!.finish() 109 | } 110 | } 111 | Status.ERROR -> { 112 | it.message?.let { message -> 113 | container.showSnackBar(message, Snackbar.LENGTH_SHORT) 114 | } 115 | 116 | } 117 | } 118 | 119 | }) 120 | } 121 | 122 | private fun login() { 123 | 124 | var isValid = true 125 | 126 | nonEmptyList(editTextUserName, editTextPassword) { view, msg -> 127 | view.error = msg 128 | isValid = false 129 | } 130 | 131 | minLengthList(6, editTextUserName) { view, msg -> 132 | view.error = msg 133 | isValid = false 134 | } 135 | 136 | minLengthList(6, editTextPassword) { view, msg -> 137 | view.error = msg 138 | isValid = false 139 | 140 | } 141 | // numberEqualToList(11, editTextICNumber) { view, msg -> 142 | // view.error = msg 143 | // isValid = false 144 | // } 145 | 146 | if (!isValid) { 147 | return 148 | } 149 | vm.login(editTextUserName.text.trim().toString(), editTextPassword.text.trim().toString()) 150 | } 151 | 152 | 153 | companion object { 154 | @JvmStatic 155 | fun newInstance(instance: Int): LoginFragment { 156 | val args = Bundle() 157 | args.putInt(ARGS_INSTANCE, instance) 158 | val fragment = LoginFragment() 159 | fragment.arguments = args 160 | return fragment 161 | } 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_login.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 25 | 26 | 40 | 41 | 47 | 48 | 53 | 54 | 60 | 61 | 68 | 69 |