├── .gitignore ├── .idea ├── .gitignore ├── compiler.xml ├── gradle.xml ├── misc.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── azamovhudstc │ │ └── bookappwithcache │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── azamovhudstc │ │ │ └── bookappwithcache │ │ │ ├── app │ │ │ └── App.kt │ │ │ ├── data │ │ │ ├── local │ │ │ │ ├── database │ │ │ │ │ ├── appDatabase │ │ │ │ │ │ └── AppDatabase.kt │ │ │ │ │ ├── dao │ │ │ │ │ │ └── BookDao.kt │ │ │ │ │ └── entites │ │ │ │ │ │ └── BookEntities.kt │ │ │ │ └── sharedpref │ │ │ │ │ └── AppReference.kt │ │ │ └── remote │ │ │ │ └── retrofit2 │ │ │ │ ├── cilent │ │ │ │ └── ApiClient.kt │ │ │ │ ├── request │ │ │ │ ├── auth │ │ │ │ │ ├── AuthRequest.kt │ │ │ │ │ └── VerifyRequest.kt │ │ │ │ └── book │ │ │ │ │ ├── AddBookRequest.kt │ │ │ │ │ ├── DeleteBookRequest.kt │ │ │ │ │ ├── EditBookRequest.kt │ │ │ │ │ ├── FavouriteBookRequest.kt │ │ │ │ │ ├── GetUserBooksResponse.kt │ │ │ │ │ ├── GetUserBooksResponseItem.kt │ │ │ │ │ └── RateBookRequest.kt │ │ │ │ ├── response │ │ │ │ ├── auth │ │ │ │ │ ├── AuthResponse.kt │ │ │ │ │ ├── ErrorResponse.kt │ │ │ │ │ └── VerifyResponse.kt │ │ │ │ └── book │ │ │ │ │ ├── AddBookResponse.kt │ │ │ │ │ ├── BooksResponse.kt │ │ │ │ │ ├── BooksResponseItem.kt │ │ │ │ │ ├── GetSocialUserResponse.kt │ │ │ │ │ ├── GetSocialUserResponseItem.kt │ │ │ │ │ ├── GetUserBookItem.kt │ │ │ │ │ └── GetUserBookResponse.kt │ │ │ │ └── service │ │ │ │ ├── auth │ │ │ │ └── AuthService.kt │ │ │ │ └── book │ │ │ │ └── BookService.kt │ │ │ ├── repo │ │ │ ├── AuthRepository.kt │ │ │ ├── BookRepository.kt │ │ │ └── impl │ │ │ │ ├── AuthRepositoryImpl.kt │ │ │ │ └── BookRepositoryImpl.kt │ │ │ ├── ui │ │ │ ├── activity │ │ │ │ └── MainActivity.kt │ │ │ ├── adapter │ │ │ │ └── BooksAdapter.kt │ │ │ └── screens │ │ │ │ ├── auth │ │ │ │ ├── LoginFragment.kt │ │ │ │ ├── RegisterFragment.kt │ │ │ │ ├── SplashFragment.kt │ │ │ │ └── VerifyFragment.kt │ │ │ │ └── book │ │ │ │ ├── AddBookScreen.kt │ │ │ │ ├── BookScreen.kt │ │ │ │ ├── EditBookScreen.kt │ │ │ │ └── ShowBookScreen.kt │ │ │ ├── usecase │ │ │ ├── auth │ │ │ │ ├── LoginUseCase.kt │ │ │ │ ├── RegisterUseCase.kt │ │ │ │ ├── VerifyUseCase.kt │ │ │ │ └── impl │ │ │ │ │ ├── LoginUseCaseImpl.kt │ │ │ │ │ ├── RegisterUseCaseImpl.kt │ │ │ │ │ └── VerifyUseCaseImpl.kt │ │ │ └── book │ │ │ │ ├── BookScreenUseCase.kt │ │ │ │ └── imp │ │ │ │ └── BookScreenUseCaseImp.kt │ │ │ ├── utils │ │ │ ├── Cases.kt │ │ │ ├── ConnetctionUtil.kt │ │ │ ├── MediatorLiveDataExtension.kt │ │ │ └── MyUtils.kt │ │ │ └── viewmodel │ │ │ ├── AddScreenViewModel.kt │ │ │ ├── EditScreenViewModel.kt │ │ │ ├── HomeScreenViewModel.kt │ │ │ ├── LoginViewModel.kt │ │ │ ├── VerifyScreenViewModel.kt │ │ │ └── imp │ │ │ ├── AddScreenViewModelImp.kt │ │ │ ├── EditScreenViewModelImpl.kt │ │ │ ├── HomeScreenViewModelImp.kt │ │ │ ├── LoginViewModelImp.kt │ │ │ ├── RegisterViewModelImpl.kt │ │ │ └── VerifyScreenViewModelImp.kt │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ ├── backs.xml │ │ ├── bg_button.xml │ │ ├── bg_circle.xml │ │ ├── bg_dialog.xml │ │ ├── bg_ed.xml │ │ ├── bg_edit.xml │ │ ├── bg_edit_text.xml │ │ ├── bg_like.png │ │ ├── bg_pin.xml │ │ ├── books.png │ │ ├── edit_phone.xml │ │ ├── ic_back.xml │ │ ├── ic_backspace.xml │ │ ├── ic_baseline_access_time_24.xml │ │ ├── ic_baseline_add_24.xml │ │ ├── ic_baseline_arrow_back_ios_24.xml │ │ ├── ic_baseline_edit_24.xml │ │ ├── ic_baseline_favorite_24.xml │ │ ├── ic_baseline_favorite_border_24.xml │ │ ├── ic_baseline_home_24.xml │ │ ├── ic_baseline_keyboard_voice_24.xml │ │ ├── ic_baseline_refresh_24.xml │ │ ├── ic_baseline_thumb_down_24.xml │ │ ├── ic_baseline_thumb_up_24.xml │ │ ├── ic_check.xml │ │ ├── ic_clear.xml │ │ ├── ic_close.xml │ │ ├── ic_delete.xml │ │ ├── ic_edit.xml │ │ ├── ic_home.xml │ │ ├── ic_launcher_background.xml │ │ ├── ic_search.xml │ │ ├── img.png │ │ ├── like.xml │ │ ├── like_un.xml │ │ ├── logout.png │ │ ├── rounded_dialog.xml │ │ ├── unlike.png │ │ ├── viewbg.xml │ │ └── viewpager_background.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── books_item.xml │ │ ├── fragment_add_book_screen.xml │ │ ├── fragment_edit.xml │ │ ├── fragment_home.xml │ │ ├── fragment_login.xml │ │ ├── fragment_register.xml │ │ ├── fragment_show_book.xml │ │ ├── fragment_splash.xml │ │ └── fragment_verify.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── navigation │ │ └── app_graph.xml │ │ ├── raw │ │ ├── book.json │ │ ├── coin.json │ │ ├── coin1.json │ │ ├── lottie_delivery_boy_bumpy_ride.json │ │ ├── lottie_developer.json │ │ ├── lottie_girl_with_a_notebook.json │ │ ├── optvertification.json │ │ ├── privacy.json │ │ ├── splash.json │ │ ├── transfer.json │ │ └── welcome.json │ │ ├── values-night │ │ └── themes.xml │ │ ├── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── themes.xml │ │ └── xml │ │ ├── backup_rules.xml │ │ └── data_extraction_rules.xml │ └── test │ └── java │ └── com │ └── azamovhudstc │ └── bookappwithcache │ └── ExampleUnitTest.kt ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 18 | 19 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 21 | 22 | 23 | 24 | 25 | 27 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BookAppWithCache-Clean-Architecture 2 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'org.jetbrains.kotlin.android' 4 | id 'kotlin-kapt' 5 | id 'kotlin-android-extensions' 6 | 7 | } 8 | 9 | android { 10 | compileSdk 32 11 | 12 | defaultConfig { 13 | applicationId "com.azamovhudstc.bookappwithcache" 14 | minSdk 24 15 | targetSdk 32 16 | versionCode 1 17 | versionName "1.0" 18 | 19 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 20 | } 21 | 22 | buildTypes { 23 | release { 24 | minifyEnabled false 25 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 26 | } 27 | } 28 | compileOptions { 29 | sourceCompatibility JavaVersion.VERSION_1_8 30 | targetCompatibility JavaVersion.VERSION_1_8 31 | } 32 | kotlinOptions { 33 | jvmTarget = '1.8' 34 | } 35 | } 36 | 37 | dependencies { 38 | def lottieVersion = "3.4.0"//lottieVersion 39 | def room_version = "2.4.3"//roomVersion 40 | //noinspection GradleDependency 41 | implementation 'androidx.core:core-ktx:1.7.0' 42 | implementation 'androidx.appcompat:appcompat:1.5.1' 43 | implementation 'com.google.android.material:material:1.7.0' 44 | implementation 'androidx.constraintlayout:constraintlayout:2.1.4' 45 | testImplementation 'junit:junit:4.13.2' 46 | androidTestImplementation 'androidx.test.ext:junit:1.1.4' 47 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0' 48 | implementation 'com.squareup.retrofit2:retrofit:2.9.0'//Retrofit2 49 | implementation 'com.squareup.retrofit2:converter-gson:2.9.0'//Retrofit2 50 | implementation 'de.hdodenhof:circleimageview:3.1.0'//CircleImageView 51 | implementation 'io.github.vicmikhailau:MaskedEditText:4.0.7'//Mask Edittext 52 | implementation 'androidx.navigation:navigation-fragment:2.5.3'//FragmentNavigation 53 | implementation("com.squareup.okhttp3:okhttp:5.0.0-alpha.3")//Okhttp3 54 | implementation 'de.hdodenhof:circleimageview:3.1.0'//CircleImageView 55 | implementation 'com.github.ismaeldivita:chip-navigation-bar:1.4.0'//ChipNavigationBar 56 | //noinspection GradleDependency 57 | implementation "com.airbnb.android:lottie:$lottieVersion"//LottieAnimation 58 | implementation 'com.facebook.shimmer:shimmer:0.5.0'//FacebookShimmer 59 | implementation("com.tbuonomo:dotsindicator:4.3")//DotsIndicator 60 | implementation 'com.github.scottyab:showhidepasswordedittext:0.8'//PasswordEditText 61 | implementation 'com.github.pdrozz:pinview:1.0.2'//PinView 62 | debugImplementation 'com.readystatesoftware.chuck:library:1.1.0' 63 | releaseImplementation 'com.readystatesoftware.chuck:library-no-op:1.1.0' 64 | implementation 'com.github.st235:expandablebottombar:1.5.1' 65 | implementation 'com.github.ibrahimsn98:SmoothBottomBar:1.7.9' 66 | //room 67 | implementation "androidx.room:room-runtime:$room_version" 68 | kapt "androidx.room:room-compiler:$room_version" 69 | implementation "androidx.room:room-ktx:$room_version" 70 | 71 | //Glide 72 | implementation 'com.github.bumptech.glide:glide:4.14.2' 73 | annotationProcessor 'com.github.bumptech.glide:compiler:4.14.2' 74 | 75 | 76 | } -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /app/src/androidTest/java/com/azamovhudstc/bookappwithcache/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.azamovhudstc.bookappwithcache", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 20 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/app/App.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.app 2 | 3 | import android.app.Application 4 | import com.azamovhudstc.bookappwithcache.data.local.database.appDatabase.AppDatabase 5 | import com.azamovhudstc.bookappwithcache.data.local.sharedpref.AppReference 6 | 7 | class App : Application() { 8 | companion object { 9 | lateinit var instance: App 10 | } 11 | 12 | override fun onCreate() { 13 | super.onCreate() 14 | instance = this 15 | AppReference.init(this) 16 | AppDatabase.init(this) 17 | } 18 | 19 | } -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/data/local/database/appDatabase/AppDatabase.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.data.local.database.appDatabase 2 | 3 | import android.content.Context 4 | import androidx.room.Database 5 | import androidx.room.Room 6 | import androidx.room.RoomDatabase 7 | import com.azamovhudstc.bookappwithcache.data.local.database.dao.BookDao 8 | import com.azamovhudstc.bookappwithcache.data.local.database.entites.BookEntities 9 | 10 | @Database(entities = [BookEntities::class], version = 1, exportSchema = false) 11 | abstract class AppDatabase: RoomDatabase() { 12 | abstract fun bookDao():BookDao 13 | 14 | companion object { 15 | private var instance:AppDatabase? = null 16 | fun init(context: Context) { 17 | instance = Room.databaseBuilder(context,AppDatabase::class.java,"books_app.db") 18 | .build() 19 | } 20 | fun getInstance() = instance!! 21 | } 22 | } -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/data/local/database/dao/BookDao.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.data.local.database.dao 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.room.* 5 | import com.azamovhudstc.bookappwithcache.data.local.database.entites.BookEntities 6 | 7 | @Dao 8 | interface BookDao { 9 | 10 | @Query("SELECT * FROM BookEntities WHERE state=0 or state = 1 OR state = 2") 11 | fun getBooks(): LiveData> 12 | 13 | @Query("SELECT * FROM BookEntities") 14 | suspend fun getAllBooks(): List 15 | 16 | @Query("DELETE FROM BookEntities") 17 | suspend fun clear() 18 | 19 | @Query("SELECT * FROM BookEntities WHERE localId = :id") 20 | suspend fun getBook(id: Int): BookEntities 21 | 22 | @Delete 23 | suspend fun delete(localData: BookEntities) 24 | 25 | @Insert 26 | suspend fun insert(localData: BookEntities) 27 | 28 | @Insert 29 | suspend fun insert(list: List) 30 | 31 | @Update 32 | suspend fun update(localData: BookEntities) 33 | 34 | @Transaction 35 | suspend fun updateWholeDB(newList: List) { 36 | clear() 37 | insert(newList) 38 | } 39 | } -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/data/local/database/entites/BookEntities.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.data.local.database.entites 2 | 3 | import androidx.room.Entity 4 | import androidx.room.PrimaryKey 5 | import com.azamovhudstc.bookappwithcache.data.remote.retrofit2.request.book.AddBookRequest 6 | import com.azamovhudstc.bookappwithcache.data.remote.retrofit2.request.book.EditBookRequest 7 | import com.azamovhudstc.bookappwithcache.data.remote.retrofit2.response.book.BooksResponseItem 8 | import java.io.Serializable 9 | 10 | @Entity 11 | data class BookEntities( 12 | val id: Int, 13 | var author: String, 14 | var description: String, 15 | var fav: Boolean, 16 | var pageCount: Int, 17 | var title: String, 18 | @PrimaryKey(autoGenerate = true) 19 | val localId: Int, 20 | var state: Int 21 | ) : Serializable { 22 | fun toUpdate(): EditBookRequest = EditBookRequest(author, description, id, pageCount, title) 23 | fun toAdd(): AddBookRequest = AddBookRequest(author, description, pageCount, title) 24 | fun toDelete(): BooksResponseItem = BooksResponseItem(author, description, fav, id, pageCount, title,) 25 | } -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/data/local/sharedpref/AppReference.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.data.local.sharedpref 2 | 3 | 4 | import android.content.Context 5 | import android.content.Context.MODE_PRIVATE 6 | import android.content.SharedPreferences 7 | 8 | class AppReference private constructor() { 9 | 10 | companion object { 11 | private lateinit var sharedPref: SharedPreferences 12 | private lateinit var editor: SharedPreferences.Editor 13 | private var shp: AppReference? = null 14 | 15 | fun init(context: Context) { 16 | shp = AppReference() 17 | sharedPref = context.getSharedPreferences("auth", MODE_PRIVATE) 18 | editor = sharedPref.edit() 19 | } 20 | 21 | fun getInstance() = shp!! 22 | } 23 | 24 | fun setToken(token: String) { 25 | editor.putString("TOKEN", token) 26 | editor.apply() 27 | } 28 | 29 | fun getToken(): String? { 30 | return sharedPref.getString("TOKEN", "") 31 | } 32 | 33 | var startScreen: String 34 | set(value) = sharedPref.edit().putString("INTRO", value).apply() 35 | get() = sharedPref.getString("INTRO", "INTRO")!! 36 | 37 | var userName: String 38 | set(value) = sharedPref.edit().putString("USERNAME", value).apply() 39 | get() = sharedPref.getString("USERNAME", "USERNAME")!! 40 | 41 | var verifyToken: String 42 | set(value) = sharedPref.edit().putString("VERIFY_TOKEN", value).apply() 43 | get() = sharedPref.getString("VERIFY_TOKEN", "")!! 44 | 45 | fun clear() { 46 | editor.clear() 47 | } 48 | } -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/data/remote/retrofit2/cilent/ApiClient.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.data.remote.retrofit2.cilent 2 | 3 | import com.azamovhudstc.bookappwithcache.app.App 4 | import com.azamovhudstc.bookappwithcache.data.remote.retrofit2.service.auth.AuthService 5 | import com.azamovhudstc.bookappwithcache.data.remote.retrofit2.service.book.BookService 6 | import com.readystatesoftware.chuck.ChuckInterceptor 7 | import okhttp3.OkHttpClient 8 | import retrofit2.Retrofit 9 | import retrofit2.converter.gson.GsonConverterFactory 10 | 11 | object ApiClient { 12 | 13 | private val myClient = OkHttpClient.Builder() 14 | .addInterceptor(ChuckInterceptor(App.instance)) 15 | .build() 16 | 17 | private val retrofit: Retrofit = Retrofit.Builder() 18 | .baseUrl("http://143.198.48.222:82") 19 | .client(myClient) 20 | .addConverterFactory(GsonConverterFactory.create()) 21 | .build() 22 | 23 | fun getAuthApi(): AuthService = retrofit.create(AuthService::class.java) 24 | fun getBookApi(): BookService = retrofit.create(BookService::class.java) 25 | 26 | 27 | } 28 | 29 | -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/data/remote/retrofit2/request/auth/AuthRequest.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.data.remote.request.auth 2 | 3 | import com.google.gson.annotations.SerializedName 4 | import java.io.Serializable 5 | 6 | sealed class AuthRequest { 7 | data class RegisterRequest( 8 | @SerializedName("firstName") 9 | val firstName: String, 10 | @SerializedName("lastName") 11 | val lastName: String, 12 | @SerializedName("password") 13 | val password: String, 14 | @SerializedName("phone") 15 | val phone: String 16 | ):Serializable 17 | 18 | data class LoginRequest( 19 | @SerializedName("password") 20 | val password: String, 21 | @SerializedName("phone") 22 | val phone: String 23 | ):Serializable 24 | 25 | 26 | } 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/data/remote/retrofit2/request/auth/VerifyRequest.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.data.remote.request.auth 2 | 3 | 4 | import com.google.gson.annotations.SerializedName 5 | 6 | data class VerifyRequest( 7 | @SerializedName("code") 8 | val code: String 9 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/data/remote/retrofit2/request/book/AddBookRequest.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.data.remote.retrofit2.request.book 2 | 3 | 4 | import com.azamovhudstc.bookappwithcache.data.local.database.entites.BookEntities 5 | import com.google.gson.annotations.SerializedName 6 | 7 | data class AddBookRequest( 8 | @SerializedName("author") 9 | val author: String, 10 | @SerializedName("description") 11 | val description: String, 12 | @SerializedName("pageCount") 13 | val pageCount: Int, 14 | @SerializedName("title") 15 | val title: String 16 | ){ 17 | fun toAddRoom():BookEntities= 18 | BookEntities(id = 0,author,description,fav = false,pageCount,title,0,1) 19 | } -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/data/remote/retrofit2/request/book/DeleteBookRequest.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.data.remote.retrofit2.request.book 2 | 3 | 4 | import com.google.gson.annotations.SerializedName 5 | 6 | data class DeleteBookRequest( 7 | @SerializedName("bookId") 8 | val bookId: Int 9 | ) 10 | -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/data/remote/retrofit2/request/book/EditBookRequest.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.data.remote.retrofit2.request.book 2 | 3 | 4 | import com.azamovhudstc.bookappwithcache.data.local.database.entites.BookEntities 5 | import com.google.gson.annotations.SerializedName 6 | 7 | data class EditBookRequest( 8 | @SerializedName("author") 9 | val author: String, 10 | @SerializedName("description") 11 | val description: String, 12 | @SerializedName("id") 13 | val id: Int, 14 | @SerializedName("pageCount") 15 | val pageCount: Int, 16 | @SerializedName("title") 17 | val title: String 18 | ) { 19 | fun toEntity(): BookEntities = 20 | BookEntities(id, author, description, false, pageCount, title, 0, 2) 21 | } -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/data/remote/retrofit2/request/book/FavouriteBookRequest.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.data.remote.request.book 2 | 3 | 4 | import com.google.gson.annotations.SerializedName 5 | 6 | data class FavouriteBookRequest( 7 | @SerializedName("bookId") 8 | val bookId: Int 9 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/data/remote/retrofit2/request/book/GetUserBooksResponse.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.data.remote.request.book 2 | 3 | 4 | class GetUserBooksResponse : ArrayList() -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/data/remote/retrofit2/request/book/GetUserBooksResponseItem.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.data.remote.request.book 2 | 3 | 4 | import java.io.Serializable 5 | 6 | data class GetUserBooksResponseItem( 7 | val author: String, 8 | val description: String, 9 | val disLikeCount: Int, 10 | val fav: Boolean, 11 | val id: Int, 12 | val isLike: Boolean, 13 | val likeCount: Int, 14 | val pageCount: Int, 15 | val title: String 16 | ) : Serializable { 17 | override fun equals(other: Any?): Boolean { 18 | if (this === other) return true 19 | if (javaClass != other?.javaClass) return false 20 | 21 | other as GetUserBooksResponseItem 22 | 23 | if (author != other.author) return false 24 | if (description != other.description) return false 25 | if (disLikeCount != other.disLikeCount) return false 26 | if (fav != other.fav) return false 27 | if (id != other.id) return false 28 | if (isLike != other.isLike) return false 29 | if (likeCount != other.likeCount) return false 30 | if (pageCount != other.pageCount) return false 31 | if (title != other.title) return false 32 | 33 | return true 34 | } 35 | 36 | override fun hashCode(): Int { 37 | var result = author.hashCode() 38 | result = 31 * result + description.hashCode() 39 | result = 31 * result + disLikeCount 40 | result = 31 * result + fav.hashCode() 41 | result = 31 * result + id 42 | result = 31 * result + isLike.hashCode() 43 | result = 31 * result + likeCount 44 | result = 31 * result + pageCount 45 | result = 31 * result + title.hashCode() 46 | return result 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/data/remote/retrofit2/request/book/RateBookRequest.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.data.remote.request.book 2 | 3 | 4 | import com.google.gson.annotations.SerializedName 5 | 6 | data class RateBookRequest( 7 | @SerializedName("bookId") 8 | val bookId: Int, 9 | @SerializedName("isLike") 10 | val isLike: Boolean 11 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/data/remote/retrofit2/response/auth/AuthResponse.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.data.remote.retrofit2.response.auth 2 | 3 | 4 | import com.google.gson.annotations.SerializedName 5 | import java.io.Serializable 6 | 7 | sealed class AuthResponse{ 8 | data class RegisterResponse( 9 | @SerializedName("token") 10 | val token: String 11 | ) 12 | 13 | data class LoginResponse( 14 | @SerializedName("token") 15 | val token: String 16 | ):Serializable 17 | 18 | } -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/data/remote/retrofit2/response/auth/ErrorResponse.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.data.remote.retrofit2.response.auth 2 | 3 | import com.google.gson.annotations.SerializedName 4 | import java.io.Serializable 5 | 6 | data class ErrorResponse( 7 | val message: String 8 | ): Serializable -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/data/remote/retrofit2/response/auth/VerifyResponse.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.data.remote.response.auth 2 | 3 | 4 | import com.google.gson.annotations.SerializedName 5 | 6 | data class VerifyResponse( 7 | @SerializedName("token") 8 | val token: String 9 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/data/remote/retrofit2/response/book/AddBookResponse.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.data.remote.response.book 2 | 3 | 4 | import com.google.gson.annotations.SerializedName 5 | 6 | data class AddBookResponse( 7 | @SerializedName("author") 8 | val author: String, 9 | @SerializedName("description") 10 | val description: String, 11 | @SerializedName("fav") 12 | val fav: Boolean, 13 | @SerializedName("id") 14 | val id: Int, 15 | @SerializedName("pageCount") 16 | val pageCount: Int, 17 | @SerializedName("title") 18 | val title: String 19 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/data/remote/retrofit2/response/book/BooksResponse.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.data.remote.retrofit2.response.book 2 | 3 | 4 | class BooksResponse : ArrayList() -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/data/remote/retrofit2/response/book/BooksResponseItem.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.data.remote.retrofit2.response.book 2 | 3 | 4 | import com.azamovhudstc.bookappwithcache.data.local.database.entites.BookEntities 5 | import com.azamovhudstc.bookappwithcache.data.remote.retrofit2.request.book.DeleteBookRequest 6 | import com.google.gson.annotations.SerializedName 7 | import java.io.Serializable 8 | 9 | data class BooksResponseItem( 10 | @SerializedName("author") 11 | val author: String, 12 | @SerializedName("description") 13 | val description: String, 14 | @SerializedName("fav") 15 | var fav: Boolean, 16 | @SerializedName("id") 17 | val id: Int, 18 | @SerializedName("pageCount") 19 | val pageCount: Int, 20 | @SerializedName("title") 21 | val title: String 22 | ) : Serializable { 23 | fun toDeleteRequest(): DeleteBookRequest = DeleteBookRequest(id) 24 | fun toBookEntity(): BookEntities = 25 | BookEntities(id, author, description, fav, pageCount, title, 0, 0) 26 | 27 | fun toBookDelete():BookEntities= BookEntities(id, author, description, fav, pageCount, title,state=-1, localId = 0) 28 | } -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/data/remote/retrofit2/response/book/GetSocialUserResponse.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.data.remote.response.book 2 | 3 | 4 | class GetSocialUserResponse : ArrayList() -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/data/remote/retrofit2/response/book/GetSocialUserResponseItem.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.data.remote.response.book 2 | 3 | 4 | import com.google.gson.annotations.SerializedName 5 | 6 | data class GetSocialUserResponseItem( 7 | @SerializedName("firstName") 8 | val firstName: String, 9 | @SerializedName("id") 10 | val id: Int, 11 | @SerializedName("lastName") 12 | val lastName: String 13 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/data/remote/retrofit2/response/book/GetUserBookItem.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.data.remote.response.book 2 | 3 | 4 | import com.google.gson.annotations.SerializedName 5 | 6 | data class GetUserBookItem( 7 | @SerializedName("author") 8 | val author: String, 9 | @SerializedName("description") 10 | val description: String, 11 | @SerializedName("disLikeCount") 12 | val disLikeCount: Int, 13 | @SerializedName("fav") 14 | val fav: Boolean, 15 | @SerializedName("id") 16 | val id: Int, 17 | @SerializedName("isLike") 18 | val isLike: Boolean?, 19 | @SerializedName("likeCount") 20 | val likeCount: Int, 21 | @SerializedName("pageCount") 22 | val pageCount: Int, 23 | @SerializedName("title") 24 | val title: String 25 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/data/remote/retrofit2/response/book/GetUserBookResponse.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.data.remote.response.book 2 | 3 | 4 | class GetUserBookResponse : ArrayList() -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/data/remote/retrofit2/service/auth/AuthService.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.data.remote.retrofit2.service.auth 2 | 3 | import com.azamovhudstc.bookappwithcache.data.remote.request.auth.AuthRequest 4 | import com.azamovhudstc.bookappwithcache.data.remote.request.auth.VerifyRequest 5 | import com.azamovhudstc.bookappwithcache.data.remote.retrofit2.response.auth.AuthResponse 6 | import com.azamovhudstc.bookappwithcache.data.remote.response.auth.VerifyResponse 7 | import retrofit2.Call 8 | import retrofit2.Response 9 | import retrofit2.http.Body 10 | import retrofit2.http.Header 11 | import retrofit2.http.POST 12 | 13 | interface AuthService { 14 | 15 | @POST("/auth/sign-up") 16 | suspend fun register(@Body data: AuthRequest.RegisterRequest): Response 17 | 18 | @POST("/auth/sign-in") 19 | suspend fun login(@Body data: AuthRequest.LoginRequest): Response 20 | 21 | @POST("/auth/sign-up/verify") 22 | suspend fun verify( 23 | @Header("Authorization") authorization: String, 24 | @Body code: VerifyRequest 25 | ): Response 26 | 27 | @POST("/auth/sign-in/verify") 28 | suspend fun verifySign( 29 | @Header("Authorization") authorization: String, 30 | @Body code: VerifyRequest 31 | ): Response 32 | 33 | 34 | } -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/data/remote/retrofit2/service/book/BookService.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.data.remote.retrofit2.service.book 2 | 3 | import com.azamovhudstc.bookappwithcache.data.remote.retrofit2.request.book.AddBookRequest 4 | import com.azamovhudstc.bookappwithcache.data.remote.retrofit2.request.book.DeleteBookRequest 5 | import com.azamovhudstc.bookappwithcache.data.remote.retrofit2.request.book.EditBookRequest 6 | import com.azamovhudstc.bookappwithcache.data.remote.request.book.FavouriteBookRequest 7 | import com.azamovhudstc.bookappwithcache.data.remote.retrofit2.response.auth.ErrorResponse 8 | import com.azamovhudstc.bookappwithcache.data.remote.response.book.AddBookResponse 9 | import com.azamovhudstc.bookappwithcache.data.remote.retrofit2.response.book.BooksResponse 10 | import com.azamovhudstc.bookappwithcache.data.remote.retrofit2.response.book.BooksResponseItem 11 | import retrofit2.Response 12 | import retrofit2.http.* 13 | 14 | interface BookService { 15 | 16 | @GET("/books") 17 | suspend fun getAllBooks(@Header("Authorization") token: String): Response 18 | 19 | @POST("/book") 20 | suspend fun addBook( 21 | @Header("Authorization") token: String, 22 | @Body addBookRequest: AddBookRequest 23 | ): Response 24 | 25 | 26 | @PUT("/book") 27 | suspend fun editBook( 28 | @Header("Authorization") token: String, 29 | @Body data: EditBookRequest 30 | ): Response 31 | 32 | @HTTP(method = "DELETE", path = "/book", hasBody = true) 33 | suspend fun deleteBook( 34 | @Header("Authorization") token: String, 35 | @Body body: DeleteBookRequest 36 | ): Response 37 | 38 | @POST("book/change-fav") 39 | suspend fun addFavouriteBook( 40 | @Header("Authorization") token: String, 41 | @Body favouriteBookRequest: FavouriteBookRequest 42 | ): Response 43 | 44 | 45 | } -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/repo/AuthRepository.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.repo 2 | 3 | import androidx.lifecycle.LiveData 4 | import com.azamovhudstc.bookappwithcache.data.remote.retrofit2.response.auth.AuthResponse 5 | import kotlinx.coroutines.flow.Flow 6 | 7 | interface AuthRepository { 8 | fun login(password: String, phone: String): Flow> 9 | fun register(name: String, password: String, phone: String, lastName: String): Flow 10 | fun verify(token: String, phone: String): Flow 11 | fun verifySign(token: String, phone: String): Flow 12 | 13 | } -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/repo/BookRepository.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.repo 2 | 3 | import androidx.lifecycle.LiveData 4 | import com.azamovhudstc.bookappwithcache.data.local.database.entites.BookEntities 5 | import com.azamovhudstc.bookappwithcache.data.remote.retrofit2.request.book.AddBookRequest 6 | import com.azamovhudstc.bookappwithcache.data.remote.retrofit2.request.book.EditBookRequest 7 | import com.azamovhudstc.bookappwithcache.data.remote.retrofit2.response.book.BooksResponseItem 8 | import kotlinx.coroutines.flow.Flow 9 | 10 | interface BookRepository { 11 | fun getAllBook(token: String): Flow>> 12 | suspend fun deleteBook(token: String, booksResponseItem: Int, id:Int) 13 | suspend fun addBook(token: String, bookRequest: AddBookRequest) 14 | suspend fun editBook(token: String, id: Int, requestData: EditBookRequest, localId: Int) 15 | 16 | suspend fun reLoadLocalData():Boolean 17 | suspend fun internetState(): Flow 18 | 19 | } -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/repo/impl/AuthRepositoryImpl.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.repo.impl 2 | 3 | import com.azamovhudstc.bookappwithcache.data.local.database.appDatabase.AppDatabase 4 | import com.azamovhudstc.bookappwithcache.data.local.sharedpref.AppReference 5 | import com.azamovhudstc.bookappwithcache.data.remote.request.auth.AuthRequest 6 | import com.azamovhudstc.bookappwithcache.data.remote.request.auth.VerifyRequest 7 | import com.azamovhudstc.bookappwithcache.data.remote.retrofit2.cilent.ApiClient 8 | import com.azamovhudstc.bookappwithcache.repo.AuthRepository 9 | import kotlinx.coroutines.Dispatchers 10 | import kotlinx.coroutines.flow.Flow 11 | import kotlinx.coroutines.flow.catch 12 | import kotlinx.coroutines.flow.flow 13 | import kotlinx.coroutines.flow.flowOn 14 | 15 | class AuthRepositoryImpl : AuthRepository { 16 | private val authApi = ApiClient.getAuthApi() 17 | 18 | 19 | override fun login( 20 | password: String, 21 | phone: String 22 | ): Flow> = flow> { 23 | val response = authApi.login(AuthRequest.LoginRequest(password, phone)) 24 | if (response.isSuccessful) { 25 | response.body()?.let { 26 | val token: String = response.body()!!.token 27 | AppReference.getInstance().verifyToken = token!! 28 | emit(Result.success(Unit)) 29 | } 30 | } else { 31 | emit(Result.failure(Exception(response.errorBody()?.toString()))) 32 | } 33 | }.catch { 34 | emit(Result.failure(Exception(it.message))) 35 | }.flowOn(Dispatchers.IO) 36 | 37 | override fun register( 38 | name: String, 39 | password: String, 40 | phone: String, 41 | lastName: String 42 | ) = flow { 43 | var response = 44 | authApi.register(AuthRequest.RegisterRequest(name, lastName, password, phone)) 45 | if (response.isSuccessful) { 46 | if (response.body() != null) { 47 | val token: String = response.body()!!.token 48 | AppReference.getInstance().verifyToken = token!! 49 | emit(true) 50 | } 51 | emit(false) 52 | } 53 | emit(false) 54 | 55 | }.flowOn(Dispatchers.IO) 56 | 57 | override fun verify(token: String, phone: String) = flow { 58 | var response = authApi.verify("Bearer $token", VerifyRequest(phone)) 59 | if (response.isSuccessful && response.body() != null) { 60 | val responseToken: String = response.body()!!.token 61 | AppReference.getInstance().setToken(responseToken) 62 | emit(true) 63 | } 64 | 65 | emit(false) 66 | }.flowOn(Dispatchers.IO) 67 | 68 | override fun verifySign(token: String, phone: String): Flow = flow { 69 | var response = authApi.verifySign("Bearer $token", VerifyRequest(phone)) 70 | if (response.isSuccessful && response.body() != null) { 71 | val token: String = response.body()!!.token 72 | AppReference.getInstance().setToken(token) 73 | emit(true) 74 | 75 | } 76 | 77 | emit(false) 78 | }.flowOn(Dispatchers.IO) 79 | 80 | } -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/repo/impl/BookRepositoryImpl.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.repo.impl 2 | 3 | import android.util.Log 4 | import androidx.lifecycle.LiveData 5 | import androidx.lifecycle.MediatorLiveData 6 | import com.azamovhudstc.bookappwithcache.data.local.database.appDatabase.AppDatabase 7 | import com.azamovhudstc.bookappwithcache.data.local.database.entites.BookEntities 8 | import com.azamovhudstc.bookappwithcache.data.local.sharedpref.AppReference 9 | import com.azamovhudstc.bookappwithcache.data.remote.retrofit2.request.book.DeleteBookRequest 10 | import com.azamovhudstc.bookappwithcache.data.remote.retrofit2.request.book.AddBookRequest 11 | import com.azamovhudstc.bookappwithcache.data.remote.retrofit2.request.book.EditBookRequest 12 | import com.azamovhudstc.bookappwithcache.data.remote.retrofit2.response.book.BooksResponseItem 13 | import com.azamovhudstc.bookappwithcache.data.remote.retrofit2.cilent.ApiClient 14 | import com.azamovhudstc.bookappwithcache.repo.BookRepository 15 | import com.azamovhudstc.bookappwithcache.utils.hasConnection 16 | import kotlinx.coroutines.Dispatchers 17 | import kotlinx.coroutines.flow.Flow 18 | import kotlinx.coroutines.flow.flow 19 | import kotlinx.coroutines.flow.flowOn 20 | import kotlinx.coroutines.flow.launchIn 21 | 22 | class BookRepositoryImpl : BookRepository { 23 | var database = AppDatabase.getInstance() 24 | private val bookApi = ApiClient.getBookApi() 25 | private val mediatorLiveData = MediatorLiveData() 26 | 27 | init { 28 | mediatorLiveData.observeForever { 29 | } 30 | } 31 | 32 | override fun getAllBook(token: String): Flow>> = flow { 33 | if (hasConnection()) { 34 | reLoadLocalData() 35 | } 36 | emit(database.bookDao().getBooks()) 37 | }.flowOn(Dispatchers.IO) 38 | 39 | override suspend fun deleteBook(token: String, booksResponseItem: Int, id: Int) { 40 | if (hasConnection()) { 41 | bookApi.deleteBook("Bearer " + token, DeleteBookRequest(id)) 42 | loadData(token) 43 | 44 | } else { 45 | var book = database.bookDao().getBook(booksResponseItem) 46 | Log.d("TTT", "deleteBook: $book") 47 | val data = BookEntities( 48 | book.id, 49 | book.author, 50 | book.description, 51 | false, 52 | book.pageCount, 53 | book.title, 54 | book.localId, -1 55 | ) 56 | 57 | database.bookDao().update(data) 58 | 59 | } 60 | } 61 | 62 | 63 | override suspend fun addBook(token: String, bookRequest: AddBookRequest) { 64 | if (hasConnection()) { 65 | bookApi.addBook("Bearer $token", bookRequest) 66 | loadData(token) 67 | } else { 68 | 69 | database.bookDao().insert(bookRequest.toAddRoom()) 70 | 71 | } 72 | 73 | } 74 | 75 | override suspend fun editBook( 76 | token: String, 77 | id: Int, 78 | bookRequest: EditBookRequest, 79 | localId: Int 80 | ) { 81 | if (hasConnection()) { 82 | bookApi.editBook("Bearer $token", bookRequest) 83 | loadData(token) 84 | } else { 85 | var data = database.bookDao().getBook(localId) 86 | var entity = BookEntities( 87 | data.id, 88 | bookRequest.author, 89 | bookRequest.description, 90 | false, 91 | bookRequest.pageCount, 92 | bookRequest.title, 93 | data.localId, 94 | 2 95 | ) 96 | database.bookDao().update(entity) 97 | } 98 | 99 | 100 | } 101 | 102 | 103 | override suspend fun reLoadLocalData():Boolean { 104 | if (hasConnection()) { 105 | loadData(token = AppReference.getInstance().getToken()!!) 106 | return true 107 | } 108 | return false 109 | 110 | } 111 | 112 | 113 | override suspend fun internetState(): Flow = flow { 114 | emit(hasConnection()) 115 | }.flowOn(Dispatchers.IO) 116 | 117 | private suspend fun loadData(token: String) { 118 | //Tekshirish 119 | var list = database.bookDao().getAllBooks() as ArrayList 120 | list.filter { 121 | it.state == 1 122 | }.forEach { add -> 123 | bookApi.addBook("Bearer $token", add.toAdd()) 124 | } 125 | list.filter { 126 | it.state == 2 127 | }.forEach { update -> 128 | bookApi.editBook("Bearer $token", update.toUpdate()) 129 | } 130 | list.filter { 131 | it.state == -1 132 | }.forEach { delete -> 133 | bookApi.deleteBook("Bearer $token", delete.toDelete().toDeleteRequest()) 134 | } 135 | 136 | val getAllBook = bookApi.getAllBooks("Bearer $token") 137 | if (getAllBook.isSuccessful) { 138 | val body = getAllBook.body() 139 | database.bookDao().updateWholeDB(body?.map { it.toBookEntity() }!!) 140 | 141 | } 142 | } 143 | 144 | } -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/ui/activity/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.ui.activity 2 | 3 | import androidx.appcompat.app.AppCompatActivity 4 | import android.os.Bundle 5 | import com.azamovhudstc.bookappwithcache.R 6 | 7 | class MainActivity : AppCompatActivity() { 8 | override fun onCreate(savedInstanceState: Bundle?) { 9 | super.onCreate(savedInstanceState) 10 | setContentView(R.layout.activity_main) 11 | } 12 | } -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/ui/adapter/BooksAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.ui.adapter 2 | 3 | import android.annotation.SuppressLint 4 | import android.content.Context 5 | import android.util.Log 6 | import android.view.LayoutInflater 7 | import android.view.View 8 | import android.view.ViewGroup 9 | import androidx.recyclerview.widget.DiffUtil 10 | import androidx.recyclerview.widget.ListAdapter 11 | import androidx.recyclerview.widget.RecyclerView 12 | import com.azamovhudstc.bookappwithcache.R 13 | import com.azamovhudstc.bookappwithcache.data.local.database.entites.BookEntities 14 | import kotlinx.android.synthetic.main.books_item.view.* 15 | 16 | class BooksAdapter(var setLongClickListener: ContactItemCallBack.SetLongClickListener) : 17 | ListAdapter(ContactItemCallBack) { 18 | inner class Wh(view: View) : RecyclerView.ViewHolder(view) { 19 | @SuppressLint("NewApi", "SetTextI18n") 20 | fun onBind(contact: BookEntities, position: Int) { 21 | itemView.name.text = contact.title 22 | itemView.author.text = contact.author 23 | itemView.delete.setOnClickListener { 24 | setLongClickListener.deleteClick(contact) 25 | } 26 | itemView.edit.setOnClickListener { 27 | setLongClickListener.editItemClick(contact) 28 | } 29 | itemView.setOnClickListener { 30 | setLongClickListener.showClick(contact) 31 | } 32 | 33 | } 34 | } 35 | 36 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Wh { 37 | return Wh(LayoutInflater.from(parent.context).inflate(R.layout.books_item, parent, false)) 38 | } 39 | 40 | override fun onBindViewHolder(holder: Wh, position: Int) { 41 | holder.onBind(getItem(position), position) 42 | } 43 | 44 | object ContactItemCallBack : DiffUtil.ItemCallback() { 45 | override fun areItemsTheSame( 46 | oldItem: BookEntities, 47 | newItem: BookEntities 48 | ): Boolean { 49 | return oldItem.id == newItem.id 50 | } 51 | 52 | override fun areContentsTheSame( 53 | oldItem: BookEntities, 54 | newItem: BookEntities 55 | ): Boolean { 56 | return oldItem == newItem 57 | } 58 | 59 | 60 | interface SetLongClickListener { 61 | fun deleteClick(contact: BookEntities) 62 | fun showClick(contact: BookEntities) 63 | fun editItemClick(contact: BookEntities) 64 | fun likedClick(contact: BookEntities) 65 | } 66 | 67 | } 68 | 69 | } -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/ui/screens/auth/LoginFragment.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.ui.screens.auth 2 | 3 | import android.os.Bundle 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import androidx.fragment.app.Fragment 8 | import androidx.fragment.app.viewModels 9 | import androidx.lifecycle.Observer 10 | import androidx.navigation.NavOptions 11 | import androidx.navigation.fragment.findNavController 12 | import com.azamovhudstc.bookappwithcache.R 13 | import com.azamovhudstc.bookappwithcache.data.remote.request.auth.AuthRequest 14 | import com.azamovhudstc.bookappwithcache.utils.showToast 15 | import com.azamovhudstc.bookappwithcache.utils.state 16 | import com.azamovhudstc.bookappwithcache.viewmodel.LoginViewModel 17 | import com.azamovhudstc.bookappwithcache.viewmodel.imp.LoginViewModelImp 18 | import com.azamovhudstc.bookappwithcache.viewmodel.imp.RegisterViewModelImpl 19 | import kotlinx.android.synthetic.main.fragment_login.* 20 | import kotlinx.android.synthetic.main.fragment_register.* 21 | 22 | class LoginFragment : Fragment(R.layout.fragment_login) { 23 | private val viewModel: LoginViewModel by viewModels() 24 | private lateinit var bundle: Bundle 25 | override fun onCreate(savedInstanceState: Bundle?) { 26 | super.onCreate(savedInstanceState) 27 | bundle = Bundle() 28 | viewModel.openVerifyScreenLiveData.observe(this) { 29 | findNavController().navigate( 30 | R.id.verifyFragment, 31 | bundle, 32 | NavOptions.Builder().setPopUpTo(R.id.loginFragment, true).build() 33 | ) 34 | } 35 | viewModel.progressLiveData.observe(this, progressObserver) 36 | viewModel.errorLiveData.observe(this) { 37 | showToast(it) 38 | } 39 | viewModel.notConnectionLiveData.observe(this) { showToast("No Connection !") } 40 | } 41 | 42 | private val progressObserver = Observer { 43 | if (it) progressLogin.show() else progressLogin.hide() 44 | } 45 | 46 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 47 | super.onViewCreated(view, savedInstanceState) 48 | startRegister.setOnClickListener { 49 | findNavController().navigate(R.id.registerFragment) 50 | } 51 | viewModel.changeButtonStatusLiveData.value = 52 | !(phone.unMaskedText.toString().isEmpty() || passwordLogin.text.trim().toString() 53 | .isEmpty()) 54 | 55 | buttonLogin.setOnClickListener { 56 | if (phone.unMaskedText.toString().isEmpty() || passwordLogin.text.trim().toString() 57 | .isEmpty() 58 | ) { 59 | viewModel.changeButtonStatusLiveData.value = false 60 | showToast("Maydon Bo`sh") 61 | } else { 62 | 63 | viewModel.changeButtonStatusLiveData.value = true 64 | bundle.putString("login", "login") 65 | 66 | viewModel.login( 67 | password = passwordLogin.text.toString(), 68 | phone = "+998" + phone.unMaskedText.toString() 69 | ) 70 | bundle.putString("login", "login") 71 | bundle.putSerializable( 72 | "data", 73 | AuthRequest.LoginRequest( 74 | passwordLogin.text.toString(), "+998${phone.unMaskedText.toString()}" 75 | ) 76 | ) 77 | } 78 | } 79 | } 80 | } -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/ui/screens/auth/RegisterFragment.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.ui.screens.auth 2 | 3 | import android.os.Bundle 4 | import androidx.fragment.app.Fragment 5 | import android.view.LayoutInflater 6 | import android.view.View 7 | import android.view.ViewGroup 8 | import androidx.fragment.app.viewModels 9 | import androidx.lifecycle.Observer 10 | import androidx.navigation.NavOptions 11 | import androidx.navigation.fragment.findNavController 12 | import com.azamovhudstc.bookappwithcache.R 13 | import com.azamovhudstc.bookappwithcache.data.remote.request.auth.AuthRequest 14 | import com.azamovhudstc.bookappwithcache.utils.showToast 15 | import com.azamovhudstc.bookappwithcache.utils.state 16 | import com.azamovhudstc.bookappwithcache.viewmodel.imp.RegisterViewModelImpl 17 | import kotlinx.android.synthetic.main.fragment_login.* 18 | import kotlinx.android.synthetic.main.fragment_register.* 19 | import kotlinx.android.synthetic.main.fragment_register.buttonRegister 20 | 21 | class RegisterFragment : Fragment(R.layout.fragment_register) { 22 | private val viewModel by viewModels() 23 | lateinit var bundle: Bundle 24 | 25 | override fun onCreate(savedInstanceState: Bundle?) { 26 | super.onCreate(savedInstanceState) 27 | bundle = Bundle() 28 | viewModel.messageLIveData.observe(this) { 29 | showToast(it) 30 | } 31 | viewModel.notConnectionLiveData.observe(this) { 32 | showToast("No internet Connected") 33 | 34 | } 35 | viewModel.backLoginLiveData.observe(this) { 36 | findNavController().popBackStack() 37 | } 38 | viewModel.changeButtonStatusLiveData.observe(this) { 39 | buttonRegister.isEnabled = it 40 | } 41 | 42 | viewModel.progressLiveData.observe(this,progressObserver) 43 | viewModel.openVerifyScreenLiveData.observe(this) { 44 | findNavController().navigate( 45 | R.id.verifyFragment, 46 | bundle, 47 | NavOptions.Builder().setPopUpTo(R.id.registerFragment, true).build() 48 | ) 49 | 50 | } 51 | } 52 | private val progressObserver = Observer { if(it) progress.show() else progress.hide() 53 | } 54 | 55 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 56 | super.onViewCreated(view, savedInstanceState) 57 | backBtn.setOnClickListener { 58 | viewModel.backLogin() 59 | } 60 | buttonRegister.setOnClickListener { 61 | if (inputFirstName.text.toString().isEmpty() || lastName.text?.trim().toString() 62 | .isEmpty() || lastName.text?.trim().toString().isEmpty() 63 | ) { 64 | showToast("Maydonlarni To`ldiring") 65 | } else { 66 | bundle.putString("login", "register") 67 | viewModel.register( 68 | inputFirstName.text.toString(), 69 | password.text.toString(), 70 | "+998" + inputPhone.unMaskedText.toString(), 71 | lastName.text.toString() 72 | ) 73 | bundle.putSerializable( 74 | "data", 75 | AuthRequest.RegisterRequest( 76 | inputFirstName.text.toString(), 77 | lastName.text.toString(), 78 | password.text.toString(), 79 | "+998${inputPhone.unMaskedText.toString()}" 80 | ) 81 | ) 82 | 83 | } 84 | } 85 | } 86 | } -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/ui/screens/auth/SplashFragment.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.ui.screens.auth 2 | 3 | import android.os.Bundle 4 | import android.os.Handler 5 | import androidx.fragment.app.Fragment 6 | import android.view.LayoutInflater 7 | import android.view.View 8 | import android.view.ViewGroup 9 | import androidx.lifecycle.lifecycleScope 10 | import androidx.navigation.NavOptions 11 | import androidx.navigation.fragment.findNavController 12 | import com.azamovhudstc.bookappwithcache.R 13 | import com.azamovhudstc.bookappwithcache.data.local.sharedpref.AppReference 14 | import kotlinx.coroutines.Dispatchers 15 | import kotlinx.coroutines.coroutineScope 16 | import kotlinx.coroutines.delay 17 | import kotlinx.coroutines.launch 18 | 19 | 20 | class SplashFragment : Fragment() { 21 | 22 | override fun onCreateView( 23 | inflater: LayoutInflater, container: ViewGroup?, 24 | savedInstanceState: Bundle? 25 | ): View? { 26 | // Inflate the layout for this fragment 27 | return inflater.inflate(R.layout.fragment_splash, container, false) 28 | } 29 | 30 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 31 | super.onViewCreated(view, savedInstanceState) 32 | 33 | lifecycleScope.launch { 34 | delay(1500) 35 | if (AppReference.getInstance().getToken().toString().isEmpty()) { 36 | findNavController().navigate( 37 | R.id.loginFragment, 38 | null, 39 | NavOptions.Builder().setPopUpTo(R.id.splashFragment, true).build() 40 | ) 41 | 42 | } 43 | else { 44 | findNavController().navigate( 45 | R.id.homeFragment, 46 | null, 47 | NavOptions.Builder().setPopUpTo(R.id.splashFragment, true).build() 48 | ) 49 | } 50 | } 51 | 52 | 53 | 54 | } 55 | } -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/ui/screens/auth/VerifyFragment.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.ui.screens.auth 2 | 3 | import android.annotation.SuppressLint 4 | import android.os.Bundle 5 | import android.os.CountDownTimer 6 | import androidx.fragment.app.Fragment 7 | import android.view.LayoutInflater 8 | import android.view.View 9 | import android.view.ViewGroup 10 | import androidx.fragment.app.viewModels 11 | import androidx.lifecycle.Observer 12 | import androidx.navigation.NavOptions 13 | import androidx.navigation.fragment.findNavController 14 | import com.azamovhudstc.bookappwithcache.R 15 | import com.azamovhudstc.bookappwithcache.data.local.sharedpref.AppReference 16 | import com.azamovhudstc.bookappwithcache.data.remote.request.auth.AuthRequest 17 | import com.azamovhudstc.bookappwithcache.utils.showToast 18 | import com.azamovhudstc.bookappwithcache.utils.state 19 | import com.azamovhudstc.bookappwithcache.viewmodel.VerifyScreenViewModel 20 | import com.azamovhudstc.bookappwithcache.viewmodel.imp.RegisterViewModelImpl 21 | import com.azamovhudstc.bookappwithcache.viewmodel.imp.VerifyScreenViewModelImp 22 | import kotlinx.android.synthetic.main.fragment_login.* 23 | import kotlinx.android.synthetic.main.fragment_register.* 24 | import kotlinx.android.synthetic.main.fragment_verify.* 25 | import java.io.Serializable 26 | import java.text.DecimalFormat 27 | 28 | 29 | class VerifyFragment : Fragment(R.layout.fragment_verify) { 30 | lateinit var data: Serializable 31 | lateinit var timer: CountDownTimer 32 | private val viewModel: VerifyScreenViewModel by viewModels() 33 | 34 | override fun onCreate(savedInstanceState: Bundle?) { 35 | super.onCreate(savedInstanceState) 36 | viewModel.openHomeScreenLiveData.observe(this) { 37 | findNavController().navigate( 38 | R.id.homeFragment, 39 | null, 40 | NavOptions.Builder().setPopUpTo(R.id.verifyFragment, true).build() 41 | ) 42 | } 43 | viewModel.notConnectionLiveData.observe(this) { 44 | showToast("No connection !") 45 | } 46 | viewModel.errorMessageLiveData.observe(this) { 47 | showToast(it) 48 | } 49 | viewModel.progressLiveData.observe(this, progressObserver) 50 | } 51 | 52 | private val progressObserver = Observer { if(it) verfied_progress.show() else verfied_progress.hide() 53 | } 54 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 55 | super.onViewCreated(view, savedInstanceState) 56 | val isChecker = arguments?.getString("login") 57 | button_send.setOnClickListener { 58 | if (isChecker == "login") { 59 | if (getOtp.text.toString().isEmpty() || getOtp.text?.length!! > 6) { 60 | showToast("Iltimos kod 6 bo`lishi kerak ") 61 | } else { 62 | 63 | viewModel.verifySign( 64 | getOtp.text.toString(), 65 | ) 66 | resent_code.visibility = View.INVISIBLE 67 | 68 | } 69 | } else { 70 | if (getOtp.text.toString().isEmpty() || getOtp.text?.length!! > 6) { 71 | showToast("Iltimos kod 6 bo`lishi kerak ") 72 | } else { 73 | viewModel.verifySignUp(getOtp.text.toString()) 74 | 75 | } 76 | } 77 | } 78 | 79 | timer = object : CountDownTimer(60000, 1000) { 80 | @SuppressLint("SetTextI18n") 81 | override fun onTick(millisUntilFinished: Long) { 82 | var f = DecimalFormat("00"); 83 | val hour = millisUntilFinished / 3600000 % 24 84 | val min = millisUntilFinished / 60000 % 60 85 | val sec = millisUntilFinished / 1000 % 60 86 | countDown.text = f.format(hour) + ":" + f.format(min) + ":" + f.format(sec) 87 | 88 | } 89 | 90 | override fun onFinish() { 91 | 92 | resent_code.visibility = View.VISIBLE 93 | countDown.text = "00:00:00" 94 | 95 | 96 | } 97 | } 98 | timer.start() 99 | 100 | if (isChecker == "login") { 101 | data = arguments?.getSerializable("data") as AuthRequest.LoginRequest 102 | var phone = (data as AuthRequest.LoginRequest).phone 103 | val phoneNumber = phone!!.substring(phone.length - 4, phone.length - 2) 104 | val phoneNumberTwo = phone.substring(phone.length - 2, phone.length) 105 | getPhones.text = 106 | "+998(##)-###-$phoneNumber-$phoneNumberTwo telefon raqamga Tasdiqlash Kodi Yubordik" 107 | 108 | } else { 109 | data = arguments?.getSerializable("data") as AuthRequest.RegisterRequest 110 | 111 | var phone = (data as AuthRequest.RegisterRequest).phone 112 | val phoneNumber = phone!!.substring(phone.length - 4, phone.length - 2) 113 | val phoneNumberTwo = phone.substring(phone.length - 2, phone.length) 114 | getPhones.text = 115 | "+998(##)-###-$phoneNumber-$phoneNumberTwo telefon raqamga Tasdiqlash Kodi Yubordik" 116 | 117 | } 118 | resent_code.setOnClickListener { 119 | if (isChecker == "login") { 120 | viewModel.login( 121 | (data as AuthRequest.LoginRequest).password, 122 | (data as AuthRequest.LoginRequest).phone 123 | ) 124 | timer.start() 125 | resent_code.visibility = View.INVISIBLE 126 | 127 | } else { 128 | viewModel.register( 129 | (data as AuthRequest.RegisterRequest).firstName, 130 | (data as AuthRequest.RegisterRequest).password, 131 | (data as AuthRequest.RegisterRequest).phone, 132 | (data as AuthRequest.RegisterRequest).lastName, 133 | ) 134 | timer.start() 135 | resent_code.visibility = View.INVISIBLE 136 | 137 | 138 | } 139 | 140 | } 141 | 142 | resent_code.visibility = View.GONE 143 | } 144 | 145 | override fun onDestroyView() { 146 | timer.cancel() 147 | super.onDestroyView() 148 | 149 | } 150 | } -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/ui/screens/book/AddBookScreen.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.ui.screens.book 2 | 3 | import android.os.Bundle 4 | import androidx.fragment.app.Fragment 5 | import android.view.LayoutInflater 6 | import android.view.View 7 | import android.view.ViewGroup 8 | import android.widget.Toast 9 | import androidx.fragment.app.viewModels 10 | import androidx.navigation.fragment.findNavController 11 | import com.azamovhudstc.bookappwithcache.R 12 | import com.azamovhudstc.bookappwithcache.data.remote.retrofit2.request.book.AddBookRequest 13 | import com.azamovhudstc.bookappwithcache.utils.showSnack 14 | import com.azamovhudstc.bookappwithcache.utils.showToast 15 | import com.azamovhudstc.bookappwithcache.viewmodel.AddScreenViewModel 16 | import com.azamovhudstc.bookappwithcache.viewmodel.HomeScreenViewModel 17 | import com.azamovhudstc.bookappwithcache.viewmodel.imp.AddScreenViewModelImp 18 | import com.azamovhudstc.bookappwithcache.viewmodel.imp.HomeScreenViewModelImp 19 | import kotlinx.android.synthetic.main.fragment_add_book_screen.* 20 | import kotlinx.android.synthetic.main.fragment_home.* 21 | 22 | class AddBookScreen : Fragment(R.layout.fragment_add_book_screen) { 23 | private val viewModel: AddScreenViewModel by viewModels() 24 | 25 | override fun onCreate(savedInstanceState: Bundle?) { 26 | super.onCreate(savedInstanceState) 27 | viewModel.successBackLiveData.observe(this) { 28 | findNavController().popBackStack() 29 | showSnack("Muffaqyatli qo`shildi !") 30 | } 31 | viewModel.progressStatusLiveData.observe(this) { 32 | if (it) addBookProgress.show() else addBookProgress.hide() 33 | } 34 | viewModel.errorLiveData.observe(this) { 35 | showToast(it) 36 | } 37 | } 38 | 39 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 40 | super.onViewCreated(view, savedInstanceState) 41 | toolbar2_.setNavigationOnClickListener { 42 | findNavController().popBackStack() 43 | 44 | } 45 | addBook.setOnClickListener { 46 | if (addBookAuthor.text.trim().toString().isEmpty() || addBookDes.text.toString().trim() 47 | .isEmpty() || addBookName.text.trim().toString().isEmpty() || 48 | addBookPage.text.toString().trim().isEmpty() 49 | ) { 50 | Toast.makeText(requireContext(), "Maydonlar bo`sh", Toast.LENGTH_SHORT).show() 51 | } else { 52 | viewModel.addBook( 53 | AddBookRequest( 54 | author = addBookAuthor.text.toString(), 55 | addBookDes.text.toString(), 56 | pageCount = addBookPage.text.toString().toInt(), 57 | addBookName.text.toString() 58 | ) 59 | ) 60 | } 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/ui/screens/book/BookScreen.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.ui.screens.book 2 | 3 | import android.os.Bundle 4 | import android.util.Log 5 | import androidx.fragment.app.Fragment 6 | import android.view.LayoutInflater 7 | import android.view.View 8 | import android.view.ViewGroup 9 | import androidx.fragment.app.viewModels 10 | import androidx.lifecycle.Observer 11 | import androidx.navigation.fragment.findNavController 12 | import com.azamovhudstc.bookappwithcache.R 13 | import com.azamovhudstc.bookappwithcache.data.local.database.entites.BookEntities 14 | import com.azamovhudstc.bookappwithcache.ui.adapter.BooksAdapter 15 | import com.azamovhudstc.bookappwithcache.utils.showSnack 16 | import com.azamovhudstc.bookappwithcache.viewmodel.HomeScreenViewModel 17 | import com.azamovhudstc.bookappwithcache.viewmodel.LoginViewModel 18 | import com.azamovhudstc.bookappwithcache.viewmodel.imp.HomeScreenViewModelImp 19 | import com.azamovhudstc.bookappwithcache.viewmodel.imp.LoginViewModelImp 20 | import kotlinx.android.synthetic.main.fragment_home.* 21 | import kotlinx.android.synthetic.main.fragment_login.* 22 | 23 | 24 | class BookScreen : Fragment(R.layout.fragment_home), 25 | BooksAdapter.ContactItemCallBack.SetLongClickListener { 26 | private val adapter by lazy { BooksAdapter(this) } 27 | private var size = 0 28 | private val viewModel: HomeScreenViewModel by viewModels() 29 | override fun onCreate(savedInstanceState: Bundle?) { 30 | super.onCreate(savedInstanceState) 31 | viewModel.statusLiveData.observe(this) { 32 | toolbar.title = it 33 | } 34 | viewModel.messageLiveData.observe(this) { 35 | showSnack(it) 36 | } 37 | viewModel.editBookLiveData.observe(this) { 38 | 39 | } 40 | viewModel.getAllBooksLiveData.observe(this, getAllBookObserver) 41 | viewModel.addBookLiveData.observe(this) { 42 | findNavController().navigate(R.id.addBookScreen) 43 | } 44 | viewModel.progressLiveData.observe(this, progressObserver) 45 | } 46 | 47 | private val progressObserver = Observer { 48 | if (it) progressHome.show() else progressHome.hide() 49 | } 50 | private val getAllBookObserver = Observer> { 51 | adapter.submitList(it) 52 | size = it.size 53 | } 54 | 55 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 56 | super.onViewCreated(view, savedInstanceState) 57 | rv_book.adapter = adapter 58 | 59 | btnRefresh.setOnClickListener { 60 | viewModel.reloadData() 61 | } 62 | viewModel.status() 63 | viewModel.getAllBooks() 64 | buttonAdd.setOnClickListener { 65 | viewModel.addBook() 66 | } 67 | } 68 | 69 | override fun deleteClick(contact: BookEntities) { 70 | viewModel.delete(contact) 71 | } 72 | 73 | override fun showClick(contact: BookEntities) { 74 | var bundle = Bundle() 75 | bundle.putSerializable("data", contact) 76 | findNavController().navigate(R.id.showBookScreen,bundle) 77 | 78 | } 79 | 80 | override fun editItemClick(contact: BookEntities) { 81 | var bundle = Bundle() 82 | bundle.putSerializable("data", contact) 83 | findNavController().navigate(R.id.editBookScreen,bundle) 84 | } 85 | 86 | override fun likedClick(contact: BookEntities) { 87 | 88 | } 89 | 90 | 91 | } -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/ui/screens/book/EditBookScreen.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.ui.screens.book 2 | 3 | import android.os.Bundle 4 | import android.view.View 5 | import android.widget.Toast 6 | import androidx.fragment.app.Fragment 7 | import androidx.fragment.app.viewModels 8 | import androidx.navigation.fragment.findNavController 9 | import com.azamovhudstc.bookappwithcache.R 10 | import com.azamovhudstc.bookappwithcache.data.local.database.entites.BookEntities 11 | import com.azamovhudstc.bookappwithcache.data.remote.retrofit2.request.book.AddBookRequest 12 | import com.azamovhudstc.bookappwithcache.utils.showSnack 13 | import com.azamovhudstc.bookappwithcache.utils.showToast 14 | import com.azamovhudstc.bookappwithcache.viewmodel.EditScreenViewModel 15 | import com.azamovhudstc.bookappwithcache.viewmodel.imp.EditScreenViewModelImpl 16 | import kotlinx.android.synthetic.main.fragment_add_book_screen.* 17 | import kotlinx.android.synthetic.main.fragment_edit.* 18 | 19 | class EditBookScreen : Fragment(R.layout.fragment_edit) { 20 | private val viewModel: EditScreenViewModel by viewModels() 21 | 22 | override fun onCreate(savedInstanceState: Bundle?) { 23 | super.onCreate(savedInstanceState) 24 | viewModel.successBackLiveData.observe(this) { 25 | findNavController().popBackStack() 26 | showSnack("Muffaqyatli qo`shildi !") 27 | } 28 | viewModel.progressStatusLiveData.observe(this) { 29 | if (it) editBookProgress.show() else editBookProgress.hide() 30 | } 31 | viewModel.errorLiveData.observe(this) { 32 | showToast(it) 33 | } 34 | } 35 | 36 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 37 | super.onViewCreated(view, savedInstanceState) 38 | val serializable = arguments?.getSerializable("data") as BookEntities 39 | editBookAuthor.setText(serializable.author) 40 | editBookDes.setText(serializable.description) 41 | editBookName.setText(serializable.title) 42 | editBookPage.setText(serializable.pageCount.toString()) 43 | 44 | toolbar2_edit.setNavigationOnClickListener { 45 | findNavController().popBackStack() 46 | } 47 | edit.setOnClickListener { 48 | if (editBookAuthor.text.trim().toString().isEmpty() || editBookDes.text.toString() 49 | .trim() 50 | .isEmpty() || editBookName.text.trim().toString().isEmpty() || 51 | editBookPage.text.toString().trim().isEmpty() 52 | ) { 53 | Toast.makeText(requireContext(), "Maydonlar bo`sh", Toast.LENGTH_SHORT).show() 54 | } else { 55 | serializable.description = editBookDes.text.toString() 56 | serializable.author = editBookAuthor.text.toString() 57 | serializable.pageCount = editBookPage.text.toString().toInt() 58 | serializable.title = editBookName.text.toString() 59 | serializable.state = 2 60 | viewModel.editBook( 61 | serializable 62 | ) 63 | } 64 | } 65 | 66 | } 67 | } -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/ui/screens/book/ShowBookScreen.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.ui.screens.book 2 | 3 | import android.os.Bundle 4 | import android.view.View 5 | import androidx.fragment.app.Fragment 6 | import com.azamovhudstc.bookappwithcache.R 7 | import com.azamovhudstc.bookappwithcache.data.local.database.entites.BookEntities 8 | import kotlinx.android.synthetic.main.fragment_edit.* 9 | import kotlinx.android.synthetic.main.fragment_show_book.* 10 | 11 | class ShowBookScreen : Fragment(R.layout.fragment_show_book) { 12 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 13 | super.onViewCreated(view, savedInstanceState) 14 | val serializable = arguments?.getSerializable("data") as BookEntities 15 | show_title.title = serializable.title 16 | show_author.text = "Muallif: ${serializable.author}" 17 | show_des.text = serializable.description 18 | show_page.text = "Hajmi: ${serializable.pageCount} Bet" 19 | 20 | } 21 | } -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/usecase/auth/LoginUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.usecase.auth 2 | 3 | import kotlinx.coroutines.flow.Flow 4 | 5 | 6 | interface LoginUseCase { 7 | fun login( password:String, phone:String): Flow> 8 | } -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/usecase/auth/RegisterUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.usecase.auth 2 | 3 | interface RegisterUseCase { 4 | fun check(name: String, password: String): Boolean 5 | fun register(name: String, password: String, phone: String, lastName: String):Boolean 6 | } -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/usecase/auth/VerifyUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.usecase.auth 2 | 3 | import kotlinx.coroutines.flow.Flow 4 | 5 | interface VerifyUseCase { 6 | fun login(phone: String, password: String):Flow> 7 | fun verifySign(code: String):Flow 8 | fun verifySignUp(code: String):Flow 9 | fun register(name: String, password: String, phone: String, lastName: String):Flow 10 | } -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/usecase/auth/impl/LoginUseCaseImpl.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.usecase.auth.impl 2 | 3 | import androidx.lifecycle.ViewModel 4 | import androidx.lifecycle.viewModelScope 5 | import com.azamovhudstc.bookappwithcache.repo.impl.AuthRepositoryImpl 6 | import com.azamovhudstc.bookappwithcache.usecase.auth.LoginUseCase 7 | import kotlinx.coroutines.flow.Flow 8 | import kotlinx.coroutines.flow.launchIn 9 | import kotlinx.coroutines.flow.onEach 10 | 11 | class LoginUseCaseImpl(var authRepositoryImpl: AuthRepositoryImpl) : 12 | LoginUseCase { 13 | override fun login(password: String, phone: String): Flow> { 14 | return authRepositoryImpl.login(password, phone) 15 | } 16 | } -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/usecase/auth/impl/RegisterUseCaseImpl.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.usecase.auth.impl 2 | 3 | import androidx.lifecycle.ViewModel 4 | import androidx.lifecycle.viewModelScope 5 | import com.azamovhudstc.bookappwithcache.repo.impl.AuthRepositoryImpl 6 | import com.azamovhudstc.bookappwithcache.usecase.auth.RegisterUseCase 7 | import kotlinx.coroutines.Dispatchers 8 | import kotlinx.coroutines.flow.first 9 | import kotlinx.coroutines.launch 10 | 11 | class RegisterUseCaseImpl(var authRepositoryImpl: AuthRepositoryImpl, var viewModel: ViewModel) : 12 | RegisterUseCase { 13 | override fun register( 14 | name: String, 15 | password: String, 16 | phone: String, 17 | lastName: String 18 | ): Boolean { 19 | var boolean = false 20 | if (check(name, password)) { 21 | boolean = true 22 | viewModel.viewModelScope.launch(Dispatchers.IO) { 23 | boolean = authRepositoryImpl.register(name, password, phone, lastName).first() 24 | } 25 | } 26 | return boolean 27 | } 28 | 29 | override fun check(name: String, password: String): Boolean = 30 | (name.length > 3 && password.length > 3) 31 | 32 | 33 | } -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/usecase/auth/impl/VerifyUseCaseImpl.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.usecase.auth.impl 2 | 3 | import androidx.lifecycle.ViewModel 4 | import androidx.lifecycle.viewModelScope 5 | import com.azamovhudstc.bookappwithcache.data.local.sharedpref.AppReference 6 | import com.azamovhudstc.bookappwithcache.repo.impl.AuthRepositoryImpl 7 | import com.azamovhudstc.bookappwithcache.usecase.auth.VerifyUseCase 8 | import com.azamovhudstc.bookappwithcache.viewmodel.VerifyScreenViewModel 9 | import kotlinx.coroutines.Dispatchers 10 | import kotlinx.coroutines.flow.Flow 11 | import kotlinx.coroutines.flow.launchIn 12 | import kotlinx.coroutines.flow.onEach 13 | 14 | class VerifyUseCaseImpl(var repo: AuthRepositoryImpl) : VerifyUseCase { 15 | override fun login(phone: String, password: String): Flow> { 16 | return repo.login(password, phone) 17 | } 18 | 19 | override fun verifySign(code: String): Flow { 20 | return repo.verifySign(AppReference.getInstance().verifyToken, code) 21 | } 22 | 23 | override fun verifySignUp(code: String): Flow { 24 | return repo.verify(AppReference.getInstance().verifyToken, code) 25 | } 26 | 27 | override fun register( 28 | name: String, 29 | password: String, 30 | phone: String, 31 | lastName: String 32 | ): Flow { 33 | return repo.register(name, password, phone, lastName) 34 | } 35 | 36 | } -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/usecase/book/BookScreenUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.usecase.book 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MediatorLiveData 5 | import androidx.lifecycle.MutableLiveData 6 | import com.azamovhudstc.bookappwithcache.data.local.database.entites.BookEntities 7 | import com.azamovhudstc.bookappwithcache.data.remote.retrofit2.request.book.AddBookRequest 8 | import com.azamovhudstc.bookappwithcache.utils.Cases 9 | import kotlinx.coroutines.flow.Flow 10 | 11 | interface BookScreenUseCase { 12 | 13 | fun editBook(bookEntities: BookEntities) 14 | fun delete(deleteBookRequest: BookEntities) 15 | fun addBook(bookRequest: AddBookRequest) 16 | fun reloadData(): Boolean 17 | fun getAllBooks(): Flow>> 18 | } -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/usecase/book/imp/BookScreenUseCaseImp.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.usecase.book.imp 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.ViewModel 5 | import androidx.lifecycle.viewModelScope 6 | import com.azamovhudstc.bookappwithcache.data.local.database.entites.BookEntities 7 | import com.azamovhudstc.bookappwithcache.data.local.sharedpref.AppReference 8 | import com.azamovhudstc.bookappwithcache.data.remote.retrofit2.request.book.AddBookRequest 9 | import com.azamovhudstc.bookappwithcache.repo.impl.AuthRepositoryImpl 10 | import com.azamovhudstc.bookappwithcache.repo.impl.BookRepositoryImpl 11 | import com.azamovhudstc.bookappwithcache.usecase.book.BookScreenUseCase 12 | import com.azamovhudstc.bookappwithcache.utils.Cases 13 | import kotlinx.coroutines.Dispatchers 14 | import kotlinx.coroutines.flow.Flow 15 | import kotlinx.coroutines.flow.first 16 | import kotlinx.coroutines.launch 17 | 18 | class BookScreenUseCaseImp(var bookRepositoryImpl: BookRepositoryImpl, var viewModel: ViewModel) : 19 | BookScreenUseCase { 20 | override fun editBook(bookEntities: BookEntities) { 21 | var unit = Unit 22 | viewModel.viewModelScope.launch { 23 | unit = bookRepositoryImpl.editBook( 24 | AppReference.getInstance().getToken()!!, 25 | bookEntities.id, 26 | bookEntities.toUpdate(), 27 | bookEntities.localId 28 | ) 29 | } 30 | return unit 31 | 32 | } 33 | 34 | 35 | override fun delete(deleteBookRequest: BookEntities) { 36 | var unit = Unit 37 | viewModel.viewModelScope.launch { 38 | 39 | unit = bookRepositoryImpl.deleteBook( 40 | AppReference.getInstance().getToken()!!, 41 | deleteBookRequest.localId, 42 | deleteBookRequest.id 43 | ) 44 | } 45 | return unit 46 | } 47 | 48 | override fun addBook(bookRequest: AddBookRequest) { 49 | var unit = Unit 50 | 51 | viewModel.viewModelScope.launch { 52 | unit = bookRepositoryImpl.addBook(AppReference.getInstance().getToken()!!, bookRequest) 53 | } 54 | return unit 55 | } 56 | 57 | override fun reloadData(): Boolean { 58 | var boolean = false 59 | viewModel.viewModelScope.launch { 60 | boolean = bookRepositoryImpl.reLoadLocalData() 61 | } 62 | return boolean 63 | 64 | } 65 | 66 | override fun getAllBooks(): Flow>> { 67 | return bookRepositoryImpl.getAllBook(AppReference.getInstance().getToken()!!) 68 | } 69 | 70 | 71 | } -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/utils/Cases.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.utils 2 | 3 | enum class Cases { 4 | SUCCESS, 5 | ERROR, 6 | CONNECTION, 7 | DISCONNECTION, 8 | ADD, 9 | DELETE, 10 | EDIT, 11 | 12 | } -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/utils/ConnetctionUtil.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.utils 2 | 3 | import android.annotation.SuppressLint 4 | import android.content.Context 5 | import android.net.ConnectivityManager 6 | import android.net.NetworkCapabilities 7 | import android.os.Build 8 | import com.azamovhudstc.bookappwithcache.app.App 9 | 10 | fun hasConnection(): Boolean = App.instance.isAvailableNetwork() 11 | 12 | @SuppressLint("MissingPermission") 13 | private fun Context.isAvailableNetwork(): Boolean { 14 | var result = false 15 | val connectivityManager = 16 | getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager 17 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 18 | val networkCapabilities = connectivityManager.activeNetwork ?: return false 19 | val actNw = 20 | connectivityManager.getNetworkCapabilities(networkCapabilities) ?: return false 21 | result = when { 22 | actNw.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> true 23 | actNw.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> true 24 | actNw.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> true 25 | else -> false 26 | } 27 | } else { 28 | connectivityManager.run { 29 | connectivityManager.activeNetworkInfo?.run { 30 | result = when (type) { 31 | ConnectivityManager.TYPE_WIFI -> true 32 | ConnectivityManager.TYPE_MOBILE -> true 33 | ConnectivityManager.TYPE_ETHERNET -> true 34 | else -> false 35 | } 36 | 37 | } 38 | } 39 | } 40 | return result 41 | } -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/utils/MediatorLiveDataExtension.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.utils 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MediatorLiveData 5 | import androidx.lifecycle.Observer 6 | 7 | fun MediatorLiveData<*>.addDisposable(liveData: LiveData, observer: Observer){ 8 | addSource(liveData) { 9 | observer.onChanged(it) 10 | removeSource(liveData) 11 | } 12 | } -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/utils/MyUtils.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.utils 2 | 3 | import android.util.Log 4 | import android.widget.Toast 5 | import androidx.appcompat.widget.AppCompatEditText 6 | import androidx.core.widget.ContentLoadingProgressBar 7 | import androidx.core.widget.addTextChangedListener 8 | import androidx.fragment.app.Fragment 9 | import com.azamovhudstc.bookappwithcache.data.remote.retrofit2.response.auth.ErrorResponse 10 | import com.google.android.material.snackbar.Snackbar 11 | import com.google.gson.Gson 12 | import com.google.gson.JsonSyntaxException 13 | import retrofit2.Callback 14 | import retrofit2.Response 15 | 16 | fun T.myApply(block: T.() -> Unit) { 17 | block(this) 18 | } 19 | 20 | fun Fragment.showToast(message: String, duration: Int = Toast.LENGTH_SHORT) { 21 | Toast.makeText(requireContext(), message, duration).show() 22 | } 23 | 24 | fun Fragment.showSnack(message: String, duration: Int = Toast.LENGTH_SHORT) { 25 | Snackbar.make(requireView(), message, duration).show() 26 | } 27 | 28 | 29 | fun AppCompatEditText.myAddTextChangedListener(block: (String) -> Unit) { 30 | this.addTextChangedListener { 31 | it?.let { 32 | block.invoke(it.toString()) 33 | } 34 | } 35 | } 36 | 37 | fun ContentLoadingProgressBar.state(bool: Boolean) { 38 | if (bool) this.show() 39 | else this.hide() 40 | } 41 | 42 | 43 | fun AppCompatEditText.amount(): String = this.text.toString() 44 | 45 | fun Callback.showApiError(response: Response): String { 46 | val gson = Gson() 47 | var message = "" 48 | response.errorBody()?.let { 49 | try { 50 | val error = gson.fromJson(it.string(), ErrorResponse::class.java) 51 | Log.d("TTT", error.message) 52 | message = error.message 53 | } catch (e: JsonSyntaxException) { 54 | return "server o'chirilgan" 55 | } 56 | } 57 | return message 58 | } 59 | 60 | fun AppCompatEditText.clear() { 61 | setText("") 62 | } -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/viewmodel/AddScreenViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.viewmodel 2 | 3 | import androidx.lifecycle.MutableLiveData 4 | import com.azamovhudstc.bookappwithcache.data.remote.retrofit2.request.book.AddBookRequest 5 | 6 | interface AddScreenViewModel { 7 | val successBackLiveData: MutableLiveData 8 | val errorLiveData: MutableLiveData 9 | val progressStatusLiveData: MutableLiveData 10 | fun addBook(bookRequest: AddBookRequest) 11 | 12 | } -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/viewmodel/EditScreenViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.viewmodel 2 | 3 | import androidx.lifecycle.MutableLiveData 4 | import com.azamovhudstc.bookappwithcache.data.local.database.entites.BookEntities 5 | import com.azamovhudstc.bookappwithcache.data.remote.retrofit2.request.book.AddBookRequest 6 | 7 | interface EditScreenViewModel { 8 | val successBackLiveData: MutableLiveData 9 | val errorLiveData: MutableLiveData 10 | val progressStatusLiveData: MutableLiveData 11 | fun editBook(bookRequest: BookEntities) 12 | 13 | } -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/viewmodel/HomeScreenViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.viewmodel 2 | 3 | import androidx.lifecycle.MediatorLiveData 4 | import androidx.lifecycle.MutableLiveData 5 | import com.azamovhudstc.bookappwithcache.data.local.database.entites.BookEntities 6 | import com.azamovhudstc.bookappwithcache.data.remote.retrofit2.request.book.DeleteBookRequest 7 | 8 | interface HomeScreenViewModel { 9 | val addBookLiveData: MediatorLiveData 10 | val messageLiveData: MediatorLiveData 11 | val statusLiveData:MutableLiveData 12 | val getAllBooksLiveData: MutableLiveData> 13 | val editBookLiveData: MediatorLiveData 14 | val progressLiveData: MutableLiveData 15 | fun editBook(bookEntities: BookEntities) 16 | fun status() 17 | fun delete(deleteBookRequest:BookEntities) 18 | fun addBook() 19 | fun reloadData() 20 | fun getAllBooks() 21 | 22 | } -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/viewmodel/LoginViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.viewmodel 2 | 3 | import androidx.lifecycle.MutableLiveData 4 | 5 | interface LoginViewModel { 6 | val errorLiveData: MutableLiveData 7 | fun login(phone: String, password: String) 8 | val changeButtonStatusLiveData :MutableLiveData 9 | val progressLiveData:MutableLiveData 10 | val notConnectionLiveData: MutableLiveData 11 | val openVerifyScreenLiveData: MutableLiveData 12 | 13 | } -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/viewmodel/VerifyScreenViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.viewmodel 2 | 3 | import androidx.lifecycle.MutableLiveData 4 | 5 | interface VerifyScreenViewModel { 6 | val openHomeScreenLiveData:MutableLiveData 7 | val errorMessageLiveData:MutableLiveData 8 | val notConnectionLiveData: MutableLiveData 9 | val changeButtonStatusLiveData : MutableLiveData 10 | val progressLiveData : MutableLiveData 11 | fun login(phone:String,password:String) 12 | fun verifySign(code:String) 13 | fun verifySignUp(code: String) 14 | fun register(name: String, password: String, phone: String, lastName: String) 15 | 16 | } -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/viewmodel/imp/AddScreenViewModelImp.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.viewmodel.imp 2 | 3 | import androidx.lifecycle.MutableLiveData 4 | import androidx.lifecycle.ViewModel 5 | import androidx.lifecycle.viewModelScope 6 | import com.azamovhudstc.bookappwithcache.data.local.sharedpref.AppReference 7 | import com.azamovhudstc.bookappwithcache.data.remote.retrofit2.request.book.AddBookRequest 8 | import com.azamovhudstc.bookappwithcache.repo.impl.BookRepositoryImpl 9 | import com.azamovhudstc.bookappwithcache.usecase.book.imp.BookScreenUseCaseImp 10 | import com.azamovhudstc.bookappwithcache.viewmodel.AddScreenViewModel 11 | import kotlinx.coroutines.Dispatchers 12 | import kotlinx.coroutines.launch 13 | 14 | class AddScreenViewModelImp : AddScreenViewModel, ViewModel() { 15 | override val successBackLiveData: MutableLiveData = MutableLiveData() 16 | override val errorLiveData: MutableLiveData = MutableLiveData() 17 | override val progressStatusLiveData: MutableLiveData = MutableLiveData() 18 | private val bookScreenUseCase = BookRepositoryImpl() 19 | 20 | 21 | override fun addBook(bookRequest: AddBookRequest) { 22 | progressStatusLiveData.value = true 23 | var token = AppReference.getInstance().getToken() 24 | if (checker(bookRequest)) { 25 | progressStatusLiveData.value = false 26 | viewModelScope.launch(Dispatchers.IO) { 27 | bookScreenUseCase.addBook(AppReference.getInstance().getToken()!!, bookRequest) 28 | .let { 29 | 30 | } 31 | successBackLiveData.postValue(Unit) 32 | } 33 | } else { 34 | progressStatusLiveData.value = false 35 | 36 | errorLiveData.value = "Maydonlar Hato to`ldirilgan" 37 | } 38 | } 39 | 40 | private fun checker(bookRequest: AddBookRequest): Boolean { 41 | return bookRequest.author.length > 10 && bookRequest.description.length > 30 42 | 43 | } 44 | } -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/viewmodel/imp/EditScreenViewModelImpl.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.viewmodel.imp 2 | 3 | import androidx.lifecycle.MutableLiveData 4 | import androidx.lifecycle.ViewModel 5 | import androidx.lifecycle.viewModelScope 6 | import com.azamovhudstc.bookappwithcache.data.local.database.entites.BookEntities 7 | import com.azamovhudstc.bookappwithcache.data.local.sharedpref.AppReference 8 | import com.azamovhudstc.bookappwithcache.data.remote.retrofit2.request.book.EditBookRequest 9 | import com.azamovhudstc.bookappwithcache.repo.impl.BookRepositoryImpl 10 | import com.azamovhudstc.bookappwithcache.viewmodel.EditScreenViewModel 11 | import kotlinx.coroutines.Dispatchers 12 | import kotlinx.coroutines.launch 13 | 14 | class EditScreenViewModelImpl : EditScreenViewModel, ViewModel() { 15 | override val successBackLiveData: MutableLiveData = MutableLiveData() 16 | override val errorLiveData: MutableLiveData = MutableLiveData() 17 | override val progressStatusLiveData: MutableLiveData = MutableLiveData() 18 | private val bookScreenUseCaseImp = BookRepositoryImpl() 19 | 20 | override fun editBook(bookRequest: BookEntities) { 21 | progressStatusLiveData.value = true 22 | var token = AppReference.getInstance().getToken() 23 | 24 | 25 | if (checker(bookRequest.toUpdate())) { 26 | progressStatusLiveData.value = false 27 | viewModelScope.launch(Dispatchers.IO) { 28 | bookScreenUseCaseImp.editBook(token!!, bookRequest.id, bookRequest.toUpdate(), bookRequest.localId) 29 | successBackLiveData.postValue(Unit) 30 | } 31 | } else { 32 | progressStatusLiveData.value = false 33 | 34 | errorLiveData.value = "Maydonlar Hato to`ldirilgan" 35 | } 36 | } 37 | 38 | private fun checker(bookRequest: EditBookRequest): Boolean { 39 | return bookRequest.author.length > 10 && bookRequest.description.length > 30 40 | 41 | } 42 | } -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/viewmodel/imp/HomeScreenViewModelImp.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.viewmodel.imp 2 | 3 | import android.util.Log 4 | import androidx.lifecycle.* 5 | import com.azamovhudstc.bookappwithcache.data.local.database.entites.BookEntities 6 | import com.azamovhudstc.bookappwithcache.repo.impl.BookRepositoryImpl 7 | import com.azamovhudstc.bookappwithcache.usecase.book.imp.BookScreenUseCaseImp 8 | import com.azamovhudstc.bookappwithcache.utils.hasConnection 9 | import com.azamovhudstc.bookappwithcache.viewmodel.HomeScreenViewModel 10 | import kotlinx.coroutines.Dispatchers 11 | import kotlinx.coroutines.flow.launchIn 12 | import kotlinx.coroutines.flow.onEach 13 | import kotlinx.coroutines.launch 14 | import java.net.SocketTimeoutException 15 | 16 | class HomeScreenViewModelImp : HomeScreenViewModel, ViewModel() { 17 | override val addBookLiveData: MediatorLiveData = MediatorLiveData() 18 | override val messageLiveData: MediatorLiveData = MediatorLiveData() 19 | override val statusLiveData: MutableLiveData = MediatorLiveData() 20 | private val bookScreenUseCaseImp = BookScreenUseCaseImp(BookRepositoryImpl(),this) 21 | override var getAllBooksLiveData: MutableLiveData> = MutableLiveData() 22 | override val editBookLiveData: MediatorLiveData = MediatorLiveData() 23 | override val progressLiveData: MutableLiveData = MutableLiveData() 24 | private var viewLiveData: LiveData> = MutableLiveData() 25 | 26 | init { 27 | getAllBooks() 28 | } 29 | 30 | override fun editBook(bookEntities: BookEntities) { 31 | } 32 | 33 | override fun status() { 34 | viewModelScope.launch { 35 | if (hasConnection()) statusLiveData.value = "Online" 36 | else statusLiveData.value = 37 | "Offline" 38 | } 39 | } 40 | 41 | override fun delete(deleteBookRequest: BookEntities) { 42 | viewModelScope.launch(Dispatchers.IO) { 43 | bookScreenUseCaseImp.delete(deleteBookRequest) 44 | 45 | } 46 | } 47 | 48 | override fun addBook() { 49 | addBookLiveData.value = Unit 50 | } 51 | 52 | override fun reloadData() { 53 | try { 54 | if (hasConnection()) { 55 | viewModelScope.launch(Dispatchers.IO) { 56 | bookScreenUseCaseImp.reloadData() 57 | } 58 | } else { 59 | messageLiveData.value = "Iltimos Internetni yoqing !" 60 | } 61 | status() 62 | } catch (e: SocketTimeoutException) { 63 | Log.d("!@#", "reloadData: ${e.message}") 64 | } 65 | 66 | } 67 | 68 | override fun getAllBooks() { 69 | progressLiveData.value = true 70 | 71 | viewModelScope.launch(Dispatchers.Main) { 72 | bookScreenUseCaseImp.getAllBooks().onEach { it -> 73 | editBookLiveData.addSource(it) { 74 | getAllBooksLiveData.value = it 75 | 76 | } 77 | }.launchIn(viewModelScope) 78 | progressLiveData.value = false 79 | 80 | } 81 | } 82 | } -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/viewmodel/imp/LoginViewModelImp.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.viewmodel.imp 2 | 3 | import androidx.lifecycle.MutableLiveData 4 | import androidx.lifecycle.ViewModel 5 | import androidx.lifecycle.viewModelScope 6 | import com.azamovhudstc.bookappwithcache.repo.AuthRepository 7 | import com.azamovhudstc.bookappwithcache.repo.impl.AuthRepositoryImpl 8 | import com.azamovhudstc.bookappwithcache.usecase.auth.LoginUseCase 9 | import com.azamovhudstc.bookappwithcache.usecase.auth.impl.LoginUseCaseImpl 10 | import com.azamovhudstc.bookappwithcache.utils.hasConnection 11 | import com.azamovhudstc.bookappwithcache.viewmodel.LoginViewModel 12 | import kotlinx.coroutines.Dispatchers 13 | import kotlinx.coroutines.flow.* 14 | import kotlinx.coroutines.launch 15 | 16 | class LoginViewModelImp : LoginViewModel, ViewModel() { 17 | override val errorLiveData: MutableLiveData = MutableLiveData() 18 | override val changeButtonStatusLiveData: MutableLiveData = MutableLiveData() 19 | private val repo: LoginUseCase = LoginUseCaseImpl(AuthRepositoryImpl()) 20 | override val progressLiveData: MutableLiveData = MutableLiveData() 21 | override val notConnectionLiveData: MutableLiveData = MutableLiveData() 22 | 23 | override val openVerifyScreenLiveData: MutableLiveData = MutableLiveData() 24 | override fun login(phone: String, password: String) { 25 | progressLiveData.value = true 26 | if (!hasConnection()) { 27 | progressLiveData.value = false 28 | notConnectionLiveData.value = Unit 29 | return 30 | } 31 | 32 | 33 | repo.login(password, phone).onEach { 34 | it.onSuccess { 35 | progressLiveData.value = false 36 | openVerifyScreenLiveData.value = Unit 37 | } 38 | it.onFailure { 39 | progressLiveData.value = false 40 | errorLiveData.value = it.message 41 | } 42 | }.launchIn(viewModelScope) 43 | 44 | 45 | // viewModelScope.launch { 46 | // repo.login(password,phone).flowOn(Dispatchers.Main).collect{ 47 | // it.onSuccess { 48 | // progressLiveData.value=false 49 | // openVerifyScreenLiveData.value=Unit 50 | // } 51 | // it.onFailure { 52 | // progressLiveData.value=false 53 | // errorLiveData.value=it.message 54 | // } 55 | // } 56 | // } 57 | } 58 | 59 | 60 | } -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/viewmodel/imp/RegisterViewModelImpl.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.viewmodel.imp 2 | 3 | import androidx.lifecycle.MutableLiveData 4 | import androidx.lifecycle.ViewModel 5 | import com.azamovhudstc.bookappwithcache.data.local.sharedpref.AppReference 6 | import com.azamovhudstc.bookappwithcache.repo.impl.AuthRepositoryImpl 7 | import com.azamovhudstc.bookappwithcache.usecase.auth.RegisterUseCase 8 | import com.azamovhudstc.bookappwithcache.usecase.auth.impl.RegisterUseCaseImpl 9 | import com.azamovhudstc.bookappwithcache.utils.hasConnection 10 | 11 | class RegisterViewModelImpl() : ViewModel() { 12 | val openVerifyScreenLiveData = MutableLiveData() 13 | val notConnectionLiveData = MutableLiveData() 14 | val messageLIveData = MutableLiveData() 15 | val backLoginLiveData = MutableLiveData() 16 | val changeButtonStatusLiveData = MutableLiveData() 17 | val progressLiveData = MutableLiveData() 18 | 19 | private val registerUseCase: RegisterUseCase = RegisterUseCaseImpl(AuthRepositoryImpl(), this) 20 | private val shP: AppReference = AppReference.getInstance() 21 | fun register(name: String, password: String, phone: String, lastName: String) { 22 | changeButtonStatusLiveData.value = false 23 | progressLiveData.value = true 24 | if (!hasConnection()) { 25 | notConnectionLiveData.value = Unit 26 | return 27 | } else { 28 | if (registerUseCase.check(name, password)) { 29 | if (registerUseCase.register(name, password, phone, lastName)) { 30 | changeButtonStatusLiveData.value = true 31 | shP.userName = name 32 | messageLIveData.value = "registered in successfully" 33 | progressLiveData.value = false 34 | openVerifyScreenLiveData.value = Unit 35 | 36 | } else { 37 | progressLiveData.value = false 38 | changeButtonStatusLiveData.value = true 39 | messageLIveData.value = "Maydonlarda xatolik mavjud" 40 | } 41 | } else { 42 | progressLiveData.value = false 43 | 44 | messageLIveData.value = "Maydonlarda xatolik mavjud" 45 | 46 | } 47 | 48 | } 49 | } 50 | 51 | 52 | fun backLogin() { 53 | backLoginLiveData.value = Unit 54 | } 55 | 56 | 57 | } -------------------------------------------------------------------------------- /app/src/main/java/com/azamovhudstc/bookappwithcache/viewmodel/imp/VerifyScreenViewModelImp.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache.viewmodel.imp 2 | 3 | import androidx.lifecycle.MutableLiveData 4 | import androidx.lifecycle.ViewModel 5 | import androidx.lifecycle.viewModelScope 6 | import com.azamovhudstc.bookappwithcache.data.local.sharedpref.AppReference 7 | import com.azamovhudstc.bookappwithcache.repo.impl.AuthRepositoryImpl 8 | import com.azamovhudstc.bookappwithcache.usecase.auth.VerifyUseCase 9 | import com.azamovhudstc.bookappwithcache.usecase.auth.impl.VerifyUseCaseImpl 10 | import com.azamovhudstc.bookappwithcache.utils.hasConnection 11 | import com.azamovhudstc.bookappwithcache.viewmodel.VerifyScreenViewModel 12 | import kotlinx.coroutines.Dispatchers 13 | import kotlinx.coroutines.flow.first 14 | import kotlinx.coroutines.flow.flowOn 15 | import kotlinx.coroutines.launch 16 | 17 | class VerifyScreenViewModelImp : VerifyScreenViewModel, ViewModel() { 18 | private val verifyUSeCase: VerifyUseCase = VerifyUseCaseImpl(AuthRepositoryImpl()) 19 | private val shP: AppReference = AppReference.getInstance() 20 | 21 | override val openHomeScreenLiveData: MutableLiveData = MutableLiveData() 22 | override val errorMessageLiveData: MutableLiveData = MutableLiveData() 23 | override val notConnectionLiveData: MutableLiveData = MutableLiveData() 24 | override val changeButtonStatusLiveData: MutableLiveData = MutableLiveData() 25 | override val progressLiveData: MutableLiveData = MutableLiveData() 26 | override fun login(phone: String, password: String) { 27 | progressLiveData.value = true 28 | if (!hasConnection()) { 29 | progressLiveData.value = false 30 | notConnectionLiveData.value = Unit 31 | return 32 | } 33 | 34 | viewModelScope.launch { 35 | verifyUSeCase.login(password, phone).flowOn(Dispatchers.Main).collect { 36 | it.onSuccess { 37 | progressLiveData.value = false 38 | 39 | } 40 | it.onFailure { 41 | progressLiveData.value = false 42 | errorMessageLiveData.value = it.message 43 | } 44 | } 45 | } 46 | } 47 | 48 | override fun verifySign(code: String) { 49 | progressLiveData.value = true 50 | if (!hasConnection()) { 51 | progressLiveData.value = false 52 | notConnectionLiveData.value = Unit 53 | return 54 | } else { 55 | viewModelScope.launch(Dispatchers.IO) { 56 | if (verifyUSeCase.verifySign(code).first()) { 57 | viewModelScope.launch(Dispatchers.Main) { 58 | openHomeScreenLiveData.value = Unit 59 | progressLiveData.value = false 60 | 61 | } 62 | } else { 63 | viewModelScope.launch(Dispatchers.Main) { 64 | changeButtonStatusLiveData.value = true 65 | 66 | progressLiveData.value = false 67 | 68 | } 69 | } 70 | 71 | } 72 | } 73 | 74 | } 75 | 76 | override fun verifySignUp(code: String) { 77 | if (!hasConnection()) { 78 | notConnectionLiveData.value = Unit 79 | } else { 80 | val verifyToken = AppReference.getInstance().verifyToken 81 | viewModelScope.launch(Dispatchers.IO) { 82 | if (verifyUSeCase.verifySignUp(code).first()) { 83 | viewModelScope.launch(Dispatchers.Main) { 84 | openHomeScreenLiveData.value = Unit 85 | 86 | } 87 | } else { 88 | viewModelScope.launch(Dispatchers.Main) { 89 | errorMessageLiveData.value = "Kod Hato !" 90 | 91 | } 92 | } 93 | 94 | } 95 | } 96 | 97 | } 98 | 99 | override fun register(name: String, password: String, phone: String, lastName: String) { 100 | if (!hasConnection()) { 101 | notConnectionLiveData.value = Unit 102 | return 103 | } else { 104 | 105 | viewModelScope.launch(Dispatchers.IO) { 106 | if (verifyUSeCase.register(name, password, phone, lastName).first()) { 107 | viewModelScope.launch(Dispatchers.Main) { 108 | changeButtonStatusLiveData.value = true 109 | shP.userName = name 110 | progressLiveData.value = false 111 | 112 | } 113 | } 114 | } 115 | } 116 | 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/backs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_button.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_circle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_dialog.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_ed.xml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 15 | 16 | 20 | 21 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_edit.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 12 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_edit_text.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_like.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/professorDeveloper/BookAppWithCache-Clean-Architecture/ee7e2aa18c75111df4d613397680f8f6b97c7e8f/app/src/main/res/drawable/bg_like.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_pin.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/books.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/professorDeveloper/BookAppWithCache-Clean-Architecture/ee7e2aa18c75111df4d613397680f8f6b97c7e8f/app/src/main/res/drawable/books.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/edit_phone.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_back.xml: -------------------------------------------------------------------------------- 1 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_backspace.xml: -------------------------------------------------------------------------------- 1 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_access_time_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_add_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_arrow_back_ios_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_edit_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_favorite_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_favorite_border_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_home_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_keyboard_voice_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_refresh_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_thumb_down_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_thumb_up_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_check.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_clear.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_close.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_delete.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_edit.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_home.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_search.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/professorDeveloper/BookAppWithCache-Clean-Architecture/ee7e2aa18c75111df4d613397680f8f6b97c7e8f/app/src/main/res/drawable/img.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/like.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/like_un.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/logout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/professorDeveloper/BookAppWithCache-Clean-Architecture/ee7e2aa18c75111df4d613397680f8f6b97c7e8f/app/src/main/res/drawable/logout.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/rounded_dialog.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/unlike.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/professorDeveloper/BookAppWithCache-Clean-Architecture/ee7e2aa18c75111df4d613397680f8f6b97c7e8f/app/src/main/res/drawable/unlike.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/viewbg.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 13 | 16 | 17 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/viewpager_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/layout/books_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 15 | 18 | 19 | 27 | 28 | 29 | 30 | 31 | 35 | 36 | 51 | 52 | 67 | 68 | 78 | 79 | 87 | 88 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_add_book_screen.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 12 | 13 | 27 | 28 | 29 | 45 | 46 | 65 | 66 | 83 | 84 | 103 | 115 | 116 | 127 | 128 | 129 | 140 | 141 | 154 | 155 | 156 | 157 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_edit.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 12 | 13 | 27 | 28 | 29 | 30 | 45 | 46 | 63 | 64 | 80 | 81 | 98 | 110 | 111 | 122 | 123 | 124 | 136 | 137 | 150 | 151 | 152 | 153 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_home.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 20 | 21 | 34 | 35 | 47 | 48 | 61 | 62 | 75 | 76 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_login.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 25 | 26 | 40 | 41 | 50 | 51 | 59 | 60 | 72 | 73 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_register.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 14 | 15 | 29 | 30 | 41 | 42 | 54 | 55 | 69 | 70 | 82 | 83 | 84 | 93 | 94 | 106 | 107 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_show_book.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 15 | 16 | 27 | 28 | 34 | 35 | 36 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 53 | 54 | 57 | 58 | 59 | 73 | 74 | 85 | 86 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_splash.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 24 | 25 | 26 | 40 | 41 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/professorDeveloper/BookAppWithCache-Clean-Architecture/ee7e2aa18c75111df4d613397680f8f6b97c7e8f/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/professorDeveloper/BookAppWithCache-Clean-Architecture/ee7e2aa18c75111df4d613397680f8f6b97c7e8f/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/professorDeveloper/BookAppWithCache-Clean-Architecture/ee7e2aa18c75111df4d613397680f8f6b97c7e8f/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/professorDeveloper/BookAppWithCache-Clean-Architecture/ee7e2aa18c75111df4d613397680f8f6b97c7e8f/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/professorDeveloper/BookAppWithCache-Clean-Architecture/ee7e2aa18c75111df4d613397680f8f6b97c7e8f/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/professorDeveloper/BookAppWithCache-Clean-Architecture/ee7e2aa18c75111df4d613397680f8f6b97c7e8f/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/professorDeveloper/BookAppWithCache-Clean-Architecture/ee7e2aa18c75111df4d613397680f8f6b97c7e8f/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/professorDeveloper/BookAppWithCache-Clean-Architecture/ee7e2aa18c75111df4d613397680f8f6b97c7e8f/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/professorDeveloper/BookAppWithCache-Clean-Architecture/ee7e2aa18c75111df4d613397680f8f6b97c7e8f/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/professorDeveloper/BookAppWithCache-Clean-Architecture/ee7e2aa18c75111df4d613397680f8f6b97c7e8f/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/navigation/app_graph.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 16 | 19 | 20 | 25 | 28 | 31 | 34 | 35 | 40 | 43 | 44 | 49 | 52 | 53 | 58 | 61 | 64 | 65 | 70 | 74 | 78 | -------------------------------------------------------------------------------- /app/src/main/res/raw/splash.json: -------------------------------------------------------------------------------- 1 | {"v":"5.5.7","meta":{"g":"LottieFiles AE 0.1.21","a":"","k":"","d":"","tc":""},"fr":60,"ip":0,"op":360,"w":400,"h":400,"nm":"Loader","ddd":0,"assets":[{"id":"comp_0","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Left Eye","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.555,"y":1},"o":{"x":1,"y":0},"t":60,"s":[800,627.5,0],"to":[-4.5,2.083,0],"ti":[4.5,-2.083,0]},{"i":{"x":0.335,"y":0.335},"o":{"x":0.167,"y":0.167},"t":120,"s":[773,640,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.763,"y":1},"o":{"x":1,"y":0},"t":257,"s":[773,640,0],"to":[4.5,-2.083,0],"ti":[-4.5,2.083,0]},{"t":310,"s":[800,627.5,0]}],"ix":2},"a":{"a":0,"k":[20,-36,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0,0,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":10,"s":[0,0,100]},{"i":{"x":[0.555,0.555,0.667],"y":[1,1,1]},"o":{"x":[1,1,0.333],"y":[0,0,0]},"t":60,"s":[140,140,100]},{"i":{"x":[0.497,0.497,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":120,"s":[100,100,100]},{"i":{"x":[0,0,0.667],"y":[1,1,1]},"o":{"x":[1,1,0.167],"y":[0,0,0]},"t":257,"s":[100,100,100]},{"i":{"x":[0.117,0.117,0.833],"y":[1,1,1]},"o":{"x":[0.14,0.14,0.333],"y":[0,0,0]},"t":310,"s":[140,140,100]},{"t":359,"s":[0,0,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[35,35],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":124,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.094117647059,0.250980392157,0.160784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[20,-36],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Right Eye","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.555,"y":1},"o":{"x":1,"y":0},"t":60,"s":[800,627.5,0],"to":[4.667,2.083,0],"ti":[-4.667,-2.083,0]},{"i":{"x":0.76,"y":0.76},"o":{"x":0.167,"y":0.167},"t":120,"s":[828,640,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.769,"y":1},"o":{"x":1,"y":0},"t":257,"s":[828,640,0],"to":[-4.667,-2.083,0],"ti":[4.667,2.083,0]},{"t":310,"s":[800,627.5,0]}],"ix":2},"a":{"a":0,"k":[20,-36,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0,0,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":10,"s":[0,0,100]},{"i":{"x":[0.555,0.555,0.667],"y":[1,1,1]},"o":{"x":[1,1,0.333],"y":[0,0,0]},"t":60,"s":[140,140,100]},{"i":{"x":[0.497,0.497,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":120,"s":[100,100,100]},{"i":{"x":[0,0,0.667],"y":[1,1,1]},"o":{"x":[1,1,0.167],"y":[0,0,0]},"t":257,"s":[100,100,100]},{"i":{"x":[0.112,0.112,0.833],"y":[1,1,1]},"o":{"x":[0.141,0.141,0.333],"y":[0,0,0]},"t":310,"s":[140,140,100]},{"t":359,"s":[0,0,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0,0.457],"y":[1,1]},"o":{"x":[0.936,0.455],"y":[0,0]},"t":150,"s":[35,35]},{"i":{"x":[0.748,0.667],"y":[1,1]},"o":{"x":[0.862,0.333],"y":[0,0]},"t":170,"s":[35,12]},{"i":{"x":[0,0.457],"y":[1,1]},"o":{"x":[0.392,0.855],"y":[0,0]},"t":182,"s":[38,10]},{"i":{"x":[0.107,0.667],"y":[1,1]},"o":{"x":[0.862,0.333],"y":[0,0]},"t":193,"s":[35,12]},{"t":213,"s":[35,35]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":124,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.094117647059,0.250980392157,0.160784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[20,-36],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Smile Lip","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.239],"y":[1]},"o":{"x":[0.373],"y":[0]},"t":60,"s":[-180]},{"i":{"x":[0.452],"y":[1]},"o":{"x":[0.877],"y":[0]},"t":77,"s":[-210]},{"i":{"x":[0.381],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":120,"s":[0]},{"i":{"x":[0],"y":[1]},"o":{"x":[0.015],"y":[0]},"t":230,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.836],"y":[0]},"t":247,"s":[-20]},{"t":310,"s":[180]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.761,"y":0.761},"o":{"x":0.167,"y":0.167},"t":120,"s":[800,640,0],"to":[0,0,0],"ti":[0,0,0]},{"t":230,"s":[800,640,0]}],"ix":2},"a":{"a":0,"k":[-136.248,-44.248,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.761,0.761,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":120,"s":[100.678,100,100]},{"t":230,"s":[100.678,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[147.503,147.503],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":124,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.094117647059,0.250980392157,0.160784313725,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":16,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-136.248,-44.248],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":10,"s":[11]},{"i":{"x":[0],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":60,"s":[0]},{"i":{"x":[0],"y":[1]},"o":{"x":[0.134],"y":[0]},"t":230,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":310,"s":[0]},{"t":359,"s":[11]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":10,"s":[11]},{"i":{"x":[0],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":60,"s":[23]},{"i":{"x":[0],"y":[1]},"o":{"x":[0.134],"y":[0]},"t":230,"s":[23]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":310,"s":[23]},{"t":359,"s":[11]}],"ix":2},"o":{"a":0,"k":48,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":600,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"Smile_lottie","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[200,144,0],"ix":2},"a":{"a":0,"k":[800,600,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"w":1600,"h":1200,"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"BG","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[200,200,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[202.25,204,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[800,600],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.9215686274509803,0.23529411764705882,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0}],"markers":[]} -------------------------------------------------------------------------------- /app/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #166BFA 10 | #35373A 11 | #2196F3 12 | #35373A 13 | #67686A 14 | #63D0DB 15 | #9D9FA1 16 | #FFFFFFFF 17 | #66d7ef 18 | #F44336 19 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | BookAppWithCache 3 | 4 | Hello blank fragment 5 | Welcome to Onboarding App ! 6 | Here is an open-source project which shows sweet & original onboarding ViewPagers that follows Material Design Guidelines 7 | 8 | Try AirBnB Lottie 9 | We use Lottie Animations on our Intro to make it interactive & pleasant. Did you already tried it ? If no, let\'s start now. It\'s just awesome 10 | 11 | Code beautiful apps 12 | Our mission is to make useful & subtle apps. We love help people to save time with open-source stuffs, but we also make life easier by creating new things. Good Coding! 13 | 14 | By continuing on this application you agree to our Terms of Use and Privacy Policy 15 | Bu Ilova insonlarga qulaylik uchun yaratilgan ! 16 | Siz ro`yxatdan o`tish orqali bizning maxfiylik siyosatimizga rozi bo`lasiz. biz sizning malumotlaringizn albatta ximoya qilamiz ! 17 | Siz bizning ishonchli va xavfsiz transferlarimizdan foydalanishingiz mumkin.Obuna bo`ling va bizning sifatli xizmatimizdan foydalaning! 18 | Qulay dizayn. 19 | 20 | start 21 | Skip 22 | finish 23 | next 24 | back 25 | +998-##-###-##-## 26 | OTP kod yuborildi optni kiriting. 27 | O’zbekiston xalq yozuvchisi, bolalarning sevimli adibi Xudoyberdi To’xtaboyevning asalari ‎nafaqat respublikamizda, balki chetellarda ham ma’lum va mashhurdir. Qo’lingizdagi “Sariq ‎devni minib” sarguzasht romani bolalar hayotidan olib yozilgan bo’lib, ularning sevimli ‎kitoblaridandir. Bu asarda orzu-havasga eltadigan chinakam yo’l halol mehnat, yaxshi xulq-odob ‎va qunt bilan o’qishda ekanligi ta’kidlanadi.‎ 28 | 29 | "Material is the metaphor.\n\n" 30 | 31 | "A material metaphor is the unifying theory of a rationalized space and a system of motion." 32 | "The material is grounded in tactile reality, inspired by the study of paper and ink, yet " 33 | "technologically advanced and open to imagination and magic.\n" 34 | "Surfaces and edges of the material provide visual cues that are grounded in reality. The " 35 | "use of familiar tactile attributes helps users quickly understand affordances. Yet the " 36 | "flexibility of the material creates new affordances that supercede those in the physical " 37 | "world, without breaking the rules of physics.\n" 38 | "The fundamentals of light, surface, and movement are key to conveying how objects move, " 39 | "interact, and exist in space and in relation to each other. Realistic lighting shows " 40 | "seams, divides space, and indicates moving parts.\n\n" 41 | 42 | "Bold, graphic, intentional.\n\n" 43 | 44 | "The foundational elements of print based design typography, grids, space, scale, color, " 45 | "and use of imagery guide visual treatments. These elements do far more than please the " 46 | "eye. They create hierarchy, meaning, and focus. Deliberate color choices, edge to edge " 47 | "imagery, large scale typography, and intentional white space create a bold and graphic " 48 | "interface that immerse the user in the experience.\n" 49 | "An emphasis on user actions makes core functionality immediately apparent and provides " 50 | "waypoints for the user.\n\n" 51 | 52 | "Motion provides meaning.\n\n" 53 | 54 | "Motion respects and reinforces the user as the prime mover. Primary user actions are " 55 | "inflection points that initiate motion, transforming the whole design.\n" 56 | "All action takes place in a single environment. Objects are presented to the user without " 57 | "breaking the continuity of experience even as they transform and reorganize.\n" 58 | "Motion is meaningful and appropriate, serving to focus attention and maintain continuity. " 59 | "Feedback is subtle yet clear. Transitions are efficient yet coherent.\n\n" 60 | 61 | "3D world.\n\n" 62 | 63 | "The material environment is a 3D space, which means all objects have x, y, and z " 64 | "dimensions. The z-axis is perpendicularly aligned to the plane of the display, with the " 65 | "positive z-axis extending towards the viewer. Every sheet of material occupies a single " 66 | "position along the z-axis and has a standard 1dp thickness.\n" 67 | "On the web, the z-axis is used for layering and not for perspective. The 3D world is " 68 | "emulated by manipulating the y-axis.\n\n" 69 | 70 | "Light and shadow.\n\n" 71 | 72 | "Within the material environment, virtual lights illuminate the scene. Key lights create " 73 | "directional shadows, while ambient light creates soft shadows from all angles.\n" 74 | "Shadows in the material environment are cast by these two light sources. In Android " 75 | "development, shadows occur when light sources are blocked by sheets of material at " 76 | "various positions along the z-axis. On the web, shadows are depicted by manipulating the " 77 | "y-axis only. The following example shows the card with a height of 6dp.\n\n" 78 | 79 | "Resting elevation.\n\n" 80 | 81 | "All material objects, regardless of size, have a resting elevation, or default elevation " 82 | "that does not change. If an object changes elevation, it should return to its resting " 83 | "elevation as soon as possible.\n\n" 84 | 85 | "Component elevations.\n\n" 86 | 87 | "The resting elevation for a component type is consistent across apps (e.g., FAB elevation " 88 | "does not vary from 6dp in one app to 16dp in another app).\n" 89 | "Components may have different resting elevations across platforms, depending on the depth " 90 | "of the environment (e.g., TV has a greater depth than mobile or desktop).\n\n" 91 | 92 | "Responsive elevation and dynamic elevation offsets.\n\n" 93 | 94 | "Some component types have responsive elevation, meaning they change elevation in response " 95 | "to user input (e.g., normal, focused, and pressed) or system events. These elevation " 96 | "changes are consistently implemented using dynamic elevation offsets.\n" 97 | "Dynamic elevation offsets are the goal elevation that a component moves towards, relative " 98 | "to the component’s resting state. They ensure that elevation changes are consistent " 99 | "across actions and component types. For example, all components that lift on press have " 100 | "the same elevation change relative to their resting elevation.\n" 101 | "Once the input event is completed or cancelled, the component will return to its resting " 102 | "elevation.\n\n" 103 | 104 | "Avoiding elevation interference.\n\n" 105 | 106 | "Components with responsive elevations may encounter other components as they move between " 107 | "their resting elevations and dynamic elevation offsets. Because material cannot pass " 108 | "through other material, components avoid interfering with one another any number of ways, " 109 | "whether on a per component basis or using the entire app layout.\n" 110 | "On a component level, components can move or be removed before they cause interference. " 111 | "For example, a floating action button (FAB) can disappear or move off screen before a " 112 | "user picks up a card, or it can move if a snackbar appears.\n" 113 | "On the layout level, design your app layout to minimize opportunities for interference. " 114 | "For example, position the FAB to one side of stream of a cards so the FAB won’t interfere " 115 | "when a user tries to pick up one of cards.\n\n" 116 | 117 | 118 | -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | 17 | 20 | 21 | 25 | 26 | 27 | 31 | 32 | 35 | 36 | 39 | 40 | 47 | 48 | 49 | 55 | 56 | 57 | 65 | 66 | 71 | 72 | 82 | 83 | 86 | 87 | 90 | -------------------------------------------------------------------------------- /app/src/main/res/xml/backup_rules.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/xml/data_extraction_rules.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 12 | 13 | 19 | -------------------------------------------------------------------------------- /app/src/test/java/com/azamovhudstc/bookappwithcache/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.azamovhudstc.bookappwithcache 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | plugins { 3 | id 'com.android.application' version '7.2.2' apply false 4 | id 'com.android.library' version '7.2.2' apply false 5 | id 'org.jetbrains.kotlin.android' version '1.7.10' apply false 6 | } 7 | 8 | task clean(type: Delete) { 9 | delete rootProject.buildDir 10 | } -------------------------------------------------------------------------------- /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=-Xmx2048m -Dfile.encoding=UTF-8 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 | # Kotlin code style for this project: "official" or "obsolete": 19 | kotlin.code.style=official 20 | android.enableJetifier=true 21 | # Enables namespacing of each library's R class so that its R class includes only the 22 | # resources declared in the library itself and none from the library's dependencies, 23 | # thereby reducing the size of the R class for that library 24 | android.nonTransitiveRClass=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/professorDeveloper/BookAppWithCache-Clean-Architecture/ee7e2aa18c75111df4d613397680f8f6b97c7e8f/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Dec 05 23:58:30 UZT 2022 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | gradlePluginPortal() 4 | google() 5 | mavenCentral() 6 | maven { url 'https://jitpack.io' } 7 | 8 | } 9 | } 10 | dependencyResolutionManagement { 11 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 12 | repositories { 13 | google() 14 | mavenCentral() 15 | maven { url 'https://jitpack.io' } 16 | } 17 | } 18 | rootProject.name = "BookAppWithCache" 19 | include ':app' 20 | --------------------------------------------------------------------------------