├── .DS_Store ├── .gitattributes ├── .gitignore ├── .idea ├── .gitignore ├── .name ├── compiler.xml ├── jarRepositories.xml └── misc.xml ├── README.md ├── app ├── .DS_Store ├── .gitignore ├── build.gradle ├── extensions │ ├── .DS_Store │ ├── .gitignore │ ├── build.gradle │ ├── consumer-rules.pro │ ├── proguard-rules.pro │ └── src │ │ ├── .DS_Store │ │ ├── androidTest │ │ └── java │ │ │ └── com │ │ │ └── demo │ │ │ └── extensions │ │ │ └── ExampleInstrumentedTest.kt │ │ ├── main │ │ ├── .DS_Store │ │ ├── AndroidManifest.xml │ │ └── java │ │ │ ├── .DS_Store │ │ │ └── com │ │ │ ├── .DS_Store │ │ │ └── demo │ │ │ ├── .DS_Store │ │ │ └── extensions │ │ │ ├── activity │ │ │ └── ActivityExt.kt │ │ │ ├── fragment │ │ │ └── FragmentManagerExt.kt │ │ │ ├── intent │ │ │ └── IntentData.kt │ │ │ ├── snackbar │ │ │ └── SnackbarExtensions.kt │ │ │ ├── string │ │ │ └── StringExt.kt │ │ │ ├── toast │ │ │ └── ToastExt.kt │ │ │ └── view │ │ │ └── ViewExt.kt │ │ └── test │ │ └── java │ │ └── com │ │ └── demo │ │ └── extensions │ │ └── ExampleUnitTest.kt ├── proguard-rules.pro └── src │ ├── .DS_Store │ ├── androidTest │ └── java │ │ └── com │ │ └── demo │ │ └── code │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── .DS_Store │ ├── AndroidManifest.xml │ ├── assets │ │ └── movie_data.json │ ├── java │ │ └── com │ │ │ └── demo │ │ │ └── code │ │ │ ├── .DS_Store │ │ │ ├── JetPackFeatureSelectionActivity.kt │ │ │ ├── base │ │ │ ├── BaseActivity.kt │ │ │ └── BaseFragment.kt │ │ │ ├── camerax │ │ │ ├── activities │ │ │ │ └── CameraxActivity.kt │ │ │ └── placeholder │ │ │ ├── dataStore │ │ │ ├── activities │ │ │ │ └── DataStoreActivity.kt │ │ │ ├── fragments │ │ │ │ ├── SelectionDataStoreFragment.kt │ │ │ │ ├── preferenceDataStore │ │ │ │ │ └── PreferenceDataStoreFragment.kt │ │ │ │ └── protoDataStore │ │ │ │ │ └── ProtoDataStoreFragment.kt │ │ │ └── util │ │ │ │ └── DataManager.kt │ │ │ ├── lifecycle │ │ │ ├── actions │ │ │ │ └── ExoplayerAction.kt │ │ │ ├── activities │ │ │ │ └── ExoplayerActivity.kt │ │ │ └── util │ │ │ │ └── ExoplayerActivityObserver.kt │ │ │ ├── liveData │ │ │ ├── activity │ │ │ │ └── LiveDataActivity.kt │ │ │ ├── mediatorLiveData │ │ │ │ ├── fragments │ │ │ │ │ └── MediatorLiveDataFragment.kt │ │ │ │ └── vm │ │ │ │ │ └── MediatorLiveDataViewModel.kt │ │ │ ├── mutableLiveData │ │ │ │ ├── fragments │ │ │ │ │ └── MutableLiveDataFragment.kt │ │ │ │ └── vm │ │ │ │ │ └── MutableLiveDataViewModel.kt │ │ │ └── selection │ │ │ │ ├── fragments │ │ │ │ └── SelectionLiveDataFragment.kt │ │ │ │ └── vm │ │ │ │ └── SelectionLiveDataViewModel.kt │ │ │ ├── navigation │ │ │ ├── activities │ │ │ │ ├── BottomNavigationActivity.kt │ │ │ │ ├── NavigationDrawerActivity.kt │ │ │ │ ├── SelectionScreenActivity.kt │ │ │ │ └── TwoFragmentContainerActivity.kt │ │ │ └── fragments │ │ │ │ ├── FragmentA.kt │ │ │ │ ├── FragmentB.kt │ │ │ │ ├── FragmentC.kt │ │ │ │ ├── FragmentD.kt │ │ │ │ ├── FragmentE.kt │ │ │ │ └── FragmentF.kt │ │ │ ├── paging │ │ │ ├── .DS_Store │ │ │ ├── Paging3Activity.kt │ │ │ ├── usingLocalSource │ │ │ │ ├── Executors.kt │ │ │ │ ├── Extensions.kt │ │ │ │ ├── db │ │ │ │ │ ├── MovieDao.kt │ │ │ │ │ └── MovieDatabase.kt │ │ │ │ ├── model │ │ │ │ │ ├── Movie.kt │ │ │ │ │ └── MovieData.kt │ │ │ │ └── ui │ │ │ │ │ ├── MovieListAdapter.kt │ │ │ │ │ ├── MovieListViewModel.kt │ │ │ │ │ └── PagingFromLocalDbActivity.kt │ │ │ ├── usingRemoteAndLocalSource │ │ │ │ ├── database │ │ │ │ │ ├── LocalDatabase.kt │ │ │ │ │ └── dao │ │ │ │ │ │ ├── KeysDao.kt │ │ │ │ │ │ └── PostsDao.kt │ │ │ │ ├── models │ │ │ │ │ ├── FeedPost.kt │ │ │ │ │ ├── PostContainer.kt │ │ │ │ │ ├── PostsApiResponse.kt │ │ │ │ │ ├── PostsKeys.kt │ │ │ │ │ └── PostsListing.kt │ │ │ │ ├── networking │ │ │ │ │ ├── ApiClient.kt │ │ │ │ │ └── RemoteService.kt │ │ │ │ ├── repositories │ │ │ │ │ ├── DataMediator.kt │ │ │ │ │ └── Repository.kt │ │ │ │ ├── ui │ │ │ │ │ ├── LocalRemoteApiLoadingAdapter.kt │ │ │ │ │ ├── LocalRemoteApiViewModel.kt │ │ │ │ │ ├── PagingFromLocalRemoteApiActivity.kt │ │ │ │ │ └── RemoteApiAdapter.kt │ │ │ │ └── utils │ │ │ │ │ └── DiffUtilCallBack.kt │ │ │ └── usingRemoteSource │ │ │ │ ├── database │ │ │ │ ├── LocalDatabase.kt │ │ │ │ └── dao │ │ │ │ │ ├── KeysDao.kt │ │ │ │ │ └── PostsDao.kt │ │ │ │ ├── models │ │ │ │ ├── FeedPost.kt │ │ │ │ ├── PostContainer.kt │ │ │ │ ├── PostsApiResponse.kt │ │ │ │ ├── PostsKeys.kt │ │ │ │ └── PostsListing.kt │ │ │ │ ├── networking │ │ │ │ ├── ApiClient.kt │ │ │ │ └── RemoteService.kt │ │ │ │ ├── placeholder │ │ │ │ ├── repositories │ │ │ │ ├── DataMediator.kt │ │ │ │ ├── DataSource.kt │ │ │ │ └── Repository.kt │ │ │ │ ├── ui │ │ │ │ ├── PagingFromRemoteApiActivity.kt │ │ │ │ ├── RemoteApiAdapter.kt │ │ │ │ ├── RemoteApiLoadingAdapter.kt │ │ │ │ └── RemoteApiViewModel.kt │ │ │ │ └── utils │ │ │ │ └── DiffUtilCallBack.kt │ │ │ ├── utils │ │ │ ├── Constants.kt │ │ │ └── placeholder.kt │ │ │ └── workmanager │ │ │ ├── WorkManagerActivity.kt │ │ │ ├── chainingworker │ │ │ ├── ChainingWorkerActivity.kt │ │ │ └── workers │ │ │ │ ├── DownloadWorker.kt │ │ │ │ └── FileClearWorker.kt │ │ │ ├── exampleone │ │ │ ├── ImageUtils.kt │ │ │ ├── WorkManagerExampleOneActivity.kt │ │ │ └── workers │ │ │ │ ├── CleanFilesWorker.kt │ │ │ │ ├── CompressWorker.kt │ │ │ │ ├── FilterWorker.kt │ │ │ │ └── UploadWorker.kt │ │ │ └── simpleworker │ │ │ ├── SimpleWorkerActivity.kt │ │ │ └── workers │ │ │ └── SimpleDownloadWorker.kt │ └── res │ │ ├── .DS_Store │ │ ├── drawable-v24 │ │ ├── ic_camera_black_48dp.xml │ │ ├── ic_camera_front_black_24dp.xml │ │ ├── ic_camera_rear_black_48dp.xml │ │ ├── ic_flash_off_black_48dp.xml │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ ├── ic_comment.xml │ │ ├── ic_flash_on_black_48dp.xml │ │ ├── ic_home.xml │ │ ├── ic_launcher_background.xml │ │ ├── ic_launcher_foreground.xml │ │ ├── ic_menu_camera.xml │ │ ├── ic_menu_gallery.xml │ │ ├── ic_menu_slideshow.xml │ │ ├── ic_star.xml │ │ ├── ic_vertical_align_bottom_white_24px.xml │ │ ├── ic_vertical_align_top_white_24px.xml │ │ └── side_nav_bar.xml │ │ ├── layout │ │ ├── activity_bottom_navigation.xml │ │ ├── activity_camera_x.xml │ │ ├── activity_chain_worker.xml │ │ ├── activity_data_store.xml │ │ ├── activity_jetpack_selection.xml │ │ ├── activity_live_data_layout.xml │ │ ├── activity_navigation_drawer.xml │ │ ├── activity_paging.xml │ │ ├── activity_paging_from_local_db.xml │ │ ├── activity_paging_from_local_remote_api.xml │ │ ├── activity_paging_from_remote_api.xml │ │ ├── activity_player.xml │ │ ├── activity_selection.xml │ │ ├── activity_simple_worker.xml │ │ ├── activity_two_frag_containers.xml │ │ ├── activity_work_manager.xml │ │ ├── activity_work_manager_example_one.xml │ │ ├── adapter_row.xml │ │ ├── app_bar_main.xml │ │ ├── content_main.xml │ │ ├── fragment_layout_a.xml │ │ ├── fragment_layout_b.xml │ │ ├── fragment_layout_c.xml │ │ ├── fragment_layout_d.xml │ │ ├── fragment_layout_e.xml │ │ ├── fragment_layout_f.xml │ │ ├── fragment_preference_data_store.xml │ │ ├── fragment_proto_data_store.xml │ │ ├── fragment_selection_data_store.xml │ │ ├── horizontal_paging_fragment.xml │ │ ├── item_loading_state.xml │ │ ├── list_item.xml │ │ ├── list_item_movie.xml │ │ ├── mediator_live_data_fragment.xml │ │ ├── mutable_live_data_fragment.xml │ │ ├── nav_header_main.xml │ │ ├── selection_live_data_fragment.xml │ │ └── selection_paging_fragment.xml │ │ ├── menu │ │ ├── activity_main_drawer.xml │ │ ├── bottom_menu_nav.xml │ │ ├── main.xml │ │ └── menu.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── navigation │ │ ├── data_store_nav.xml │ │ ├── live_data_nav.xml │ │ ├── nav_graph_a.xml │ │ ├── nav_graph_b.xml │ │ ├── nav_graph_bottom_navigation.xml │ │ ├── nav_graph_drawer.xml │ │ └── paging_nav.xml │ │ ├── values-night │ │ └── themes.xml │ │ └── values │ │ ├── colors.xml │ │ ├── colours_material.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ ├── styles.xml │ │ └── themes.xml │ └── test │ └── java │ └── com │ └── demo │ └── code │ └── ExampleUnitTest.kt ├── build.gradle ├── buildsystem ├── dependencies.gradle └── version.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── images ├── .DS_Store ├── Logo-new.png ├── alltools.png ├── android-jetpack.png ├── android_jetpack_lifecycle.jpeg ├── assistant.png ├── camerax.png ├── data_source_types.png ├── datastore.jpeg ├── jetpacknavigation.png ├── liveData.jpeg ├── local_remote_paging.png ├── mediator_paging.png ├── mobile_server.png ├── navigationgraph.png ├── page_struct.png ├── paging.png ├── pagingBanner.jpeg ├── paging_elements.png ├── prefdatastore.jpeg ├── proposed_arch.png ├── protodatastore.jpeg ├── recyclerview.jpeg ├── supporting_caching.png └── types_of_work_manager.png └── settings.gradle /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devrath/DroidAndroidJetpack/bfa825ed41965c57a469eeb67e4b376fc8e2ab98/.DS_Store -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | *.aab 5 | 6 | # Files for the ART/Dalvik VM 7 | *.dex 8 | 9 | # Java class files 10 | *.class 11 | 12 | # Generated files 13 | bin/ 14 | gen/ 15 | out/ 16 | # Uncomment the following line in case you need and you don't have the release build type files in your app 17 | # release/ 18 | 19 | # Gradle files 20 | .gradle/ 21 | build/ 22 | 23 | # Local configuration file (sdk path, etc) 24 | local.properties 25 | 26 | # Proguard folder generated by Eclipse 27 | proguard/ 28 | 29 | # Log Files 30 | *.log 31 | 32 | # Android Studio Navigation editor temp files 33 | .navigation/ 34 | 35 | # Android Studio captures folder 36 | captures/ 37 | 38 | # IntelliJ 39 | *.iml 40 | .idea/workspace.xml 41 | .idea/tasks.xml 42 | .idea/gradle.xml 43 | .idea/assetWizardSettings.xml 44 | .idea/dictionaries 45 | .idea/libraries 46 | # Android Studio 3 in .gitignore file. 47 | .idea/caches 48 | .idea/modules.xml 49 | # Comment next line if keeping position of elements in Navigation Editor is relevant for you 50 | .idea/navEditor.xml 51 | 52 | # Keystore files 53 | # Uncomment the following lines if you do not want to check your keystore files in. 54 | #*.jks 55 | #*.keystore 56 | 57 | # External native build folder generated in Android Studio 2.2 and later 58 | .externalNativeBuild 59 | 60 | # Google Services (e.g. APIs or Firebase) 61 | # google-services.json 62 | 63 | # Freeline 64 | freeline.py 65 | freeline/ 66 | freeline_project_description.json 67 | 68 | # fastlane 69 | fastlane/report.xml 70 | fastlane/Preview.html 71 | fastlane/screenshots 72 | fastlane/test_output 73 | fastlane/readme.md 74 | 75 | # Version control 76 | vcs.xml 77 | 78 | # lint 79 | lint/intermediates/ 80 | lint/generated/ 81 | lint/outputs/ 82 | lint/tmp/ 83 | # lint/reports/ 84 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | code -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Banner](images/Logo-new.png) 2 | 3 |

4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |

14 | 15 | 16 | 17 | | Documentation links of Wiki is present below | 18 | | --- | 19 | | [Navigation](https://github.com/devrath/DroidAndroidJetpack/wiki/Jetpack-Navigation) | 20 | | [Life-cycle aware component](https://github.com/devrath/DroidAndroidJetpack/wiki/Lifecycle-Aware-Components) | 21 | | [Data Store](https://github.com/devrath/DroidAndroidJetpack/wiki/Data-Store) | 22 | | [Live Data](https://github.com/devrath/DroidAndroidJetpack/wiki/Live-Data) | 23 | | [CameraX](https://github.com/devrath/DroidAndroidJetpack/wiki/CameraX) | 24 | | [Work Manager](https://github.com/devrath/DroidAndroidJetpack/wiki/Workmanager) | 25 | | [Paging3](https://github.com/devrath/DroidAndroidJetpack/wiki/Paging-Library) | 26 | 27 | 28 |

29 | 30 |

31 | -------------------------------------------------------------------------------- /app/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devrath/DroidAndroidJetpack/bfa825ed41965c57a469eeb67e4b376fc8e2ab98/app/.DS_Store -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/extensions/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devrath/DroidAndroidJetpack/bfa825ed41965c57a469eeb67e4b376fc8e2ab98/app/extensions/.DS_Store -------------------------------------------------------------------------------- /app/extensions/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/extensions/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | id 'kotlin-android' 4 | } 5 | 6 | android { 7 | compileSdkVersion 30 8 | buildToolsVersion "30.0.3" 9 | 10 | defaultConfig { 11 | minSdkVersion 16 12 | targetSdkVersion 30 13 | versionCode 1 14 | versionName "1.0" 15 | 16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 17 | consumerProguardFiles "consumer-rules.pro" 18 | } 19 | 20 | buildTypes { 21 | release { 22 | minifyEnabled false 23 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 24 | } 25 | } 26 | compileOptions { 27 | sourceCompatibility JavaVersion.VERSION_1_8 28 | targetCompatibility JavaVersion.VERSION_1_8 29 | } 30 | kotlinOptions { 31 | jvmTarget = '1.8' 32 | } 33 | } 34 | 35 | dependencies { 36 | 37 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" 38 | implementation 'androidx.core:core-ktx:1.3.2' 39 | implementation 'androidx.appcompat:appcompat:1.2.0' 40 | implementation 'com.google.android.material:material:1.3.0' 41 | testImplementation 'junit:junit:4.+' 42 | androidTestImplementation 'androidx.test.ext:junit:1.1.2' 43 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' 44 | } -------------------------------------------------------------------------------- /app/extensions/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devrath/DroidAndroidJetpack/bfa825ed41965c57a469eeb67e4b376fc8e2ab98/app/extensions/consumer-rules.pro -------------------------------------------------------------------------------- /app/extensions/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/extensions/src/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devrath/DroidAndroidJetpack/bfa825ed41965c57a469eeb67e4b376fc8e2ab98/app/extensions/src/.DS_Store -------------------------------------------------------------------------------- /app/extensions/src/androidTest/java/com/demo/extensions/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.demo.extensions 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.demo.extensions.test", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /app/extensions/src/main/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devrath/DroidAndroidJetpack/bfa825ed41965c57a469eeb67e4b376fc8e2ab98/app/extensions/src/main/.DS_Store -------------------------------------------------------------------------------- /app/extensions/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /app/extensions/src/main/java/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devrath/DroidAndroidJetpack/bfa825ed41965c57a469eeb67e4b376fc8e2ab98/app/extensions/src/main/java/.DS_Store -------------------------------------------------------------------------------- /app/extensions/src/main/java/com/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devrath/DroidAndroidJetpack/bfa825ed41965c57a469eeb67e4b376fc8e2ab98/app/extensions/src/main/java/com/.DS_Store -------------------------------------------------------------------------------- /app/extensions/src/main/java/com/demo/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devrath/DroidAndroidJetpack/bfa825ed41965c57a469eeb67e4b376fc8e2ab98/app/extensions/src/main/java/com/demo/.DS_Store -------------------------------------------------------------------------------- /app/extensions/src/main/java/com/demo/extensions/activity/ActivityExt.kt: -------------------------------------------------------------------------------- 1 | package com.demo.extensions.activity 2 | 3 | import android.app.Activity 4 | import android.util.DisplayMetrics 5 | 6 | 7 | 8 | 9 | 10 | fun Activity.isInPortrait(): Boolean { 11 | val displayMetrics = DisplayMetrics() 12 | windowManager.defaultDisplay.getMetrics(displayMetrics) 13 | return displayMetrics.heightPixels > displayMetrics.widthPixels 14 | } -------------------------------------------------------------------------------- /app/extensions/src/main/java/com/demo/extensions/fragment/FragmentManagerExt.kt: -------------------------------------------------------------------------------- 1 | package com.demo.extensions.fragment 2 | 3 | import androidx.fragment.app.FragmentManager 4 | import androidx.fragment.app.FragmentTransaction 5 | 6 | inline fun FragmentManager.inTransactionCommit(func: FragmentTransaction.() -> Unit) { 7 | val fragmentTransaction = beginTransaction() 8 | fragmentTransaction.func() 9 | fragmentTransaction.commit() 10 | } 11 | 12 | inline fun FragmentManager.inTransactionCommitAllowingStateLoss(func: FragmentTransaction.() -> Unit) { 13 | val fragmentTransaction = beginTransaction() 14 | fragmentTransaction.func() 15 | fragmentTransaction.commitAllowingStateLoss() 16 | } -------------------------------------------------------------------------------- /app/extensions/src/main/java/com/demo/extensions/snackbar/SnackbarExtensions.kt: -------------------------------------------------------------------------------- 1 | package com.demo.extensions.snackbar 2 | 3 | import android.view.View 4 | import androidx.annotation.StringRes 5 | import androidx.core.content.ContextCompat 6 | import com.google.android.material.snackbar.Snackbar 7 | 8 | inline fun View.snack(@StringRes messageRes: Int, length: Int = Snackbar.LENGTH_LONG, f: Snackbar.() -> Unit) { 9 | snack(resources.getString(messageRes), length, f) 10 | } 11 | 12 | inline fun View.snack(message: String, length: Int = Snackbar.LENGTH_LONG, f: Snackbar.() -> Unit) { 13 | val snack = Snackbar.make(this, message, length) 14 | snack.f() 15 | snack.show() 16 | } 17 | 18 | fun Snackbar.action(@StringRes actionRes: Int, color: Int? = null, listener: (View) -> Unit) { 19 | action(view.resources.getString(actionRes), color, listener) 20 | } 21 | 22 | fun Snackbar.action(action: String, color: Int? = null, listener: (View) -> Unit) { 23 | setAction(action, listener) 24 | color?.let { setActionTextColor(ContextCompat.getColor(context, color)) } 25 | } -------------------------------------------------------------------------------- /app/extensions/src/main/java/com/demo/extensions/string/StringExt.kt: -------------------------------------------------------------------------------- 1 | package com.demo.extensions.string 2 | 3 | import java.security.MessageDigest 4 | 5 | 6 | fun String.md5() = encrypt(this, "MD5") 7 | 8 | fun String.sha1() = encrypt(this, "SHA-1") 9 | 10 | fun String.isIdcard(): Boolean { 11 | val p18 = "^[1-9]\\d{5}(18|19|([23]\\d))\\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\\d{3}[0-9Xx]\$".toRegex() 12 | val p15 = "^[1-9]\\d{5}\\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\\d{2}[0-9Xx]\$".toRegex() 13 | return matches(p18) || matches(p15) 14 | } 15 | 16 | fun String.isPhone(): Boolean { 17 | val p = "^1([34578])\\d{9}\$".toRegex() 18 | return matches(p) 19 | } 20 | 21 | fun String.isEmail(): Boolean { 22 | val p = "^(\\w)+(\\.\\w+)*@(\\w)+((\\.\\w+)+)\$".toRegex() 23 | return matches(p) 24 | } 25 | 26 | fun String.isNumeric(): Boolean { 27 | val p = "^[0-9]+$".toRegex() 28 | return matches(p) 29 | } 30 | 31 | fun String.equalsIgnoreCase(other: String) = this.toLowerCase().contentEquals(other.toLowerCase()) 32 | 33 | private fun encrypt(string: String?, type: String): String { 34 | val bytes = MessageDigest.getInstance(type).digest(string!!.toByteArray()) 35 | return bytes2Hex(bytes) 36 | } 37 | 38 | internal fun bytes2Hex(bts: ByteArray): String { 39 | var des = "" 40 | var tmp: String 41 | for (i in bts.indices) { 42 | tmp = Integer.toHexString(bts[i].toInt() and 0xFF) 43 | if (tmp.length == 1) { 44 | des += "0" 45 | } 46 | des += tmp 47 | } 48 | return des 49 | } 50 | -------------------------------------------------------------------------------- /app/extensions/src/main/java/com/demo/extensions/toast/ToastExt.kt: -------------------------------------------------------------------------------- 1 | package com.demo.extensions.toast 2 | 3 | import android.annotation.SuppressLint 4 | import android.content.Context 5 | import android.widget.Toast 6 | 7 | private var toast: Toast? = null 8 | 9 | @SuppressLint("ShowToast") 10 | fun toast(msg: Any?, isShort: Boolean = true, context : Context) { 11 | msg?.let { 12 | if (toast == null) { 13 | toast = Toast.makeText(context, msg.toString(), Toast.LENGTH_SHORT) 14 | } else { 15 | toast!!.setText(msg.toString()) 16 | } 17 | toast!!.duration = if (isShort) Toast.LENGTH_SHORT else Toast.LENGTH_LONG 18 | toast!!.show() 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/extensions/src/test/java/com/demo/extensions/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.demo.extensions 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 | } -------------------------------------------------------------------------------- /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/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devrath/DroidAndroidJetpack/bfa825ed41965c57a469eeb67e4b376fc8e2ab98/app/src/.DS_Store -------------------------------------------------------------------------------- /app/src/androidTest/java/com/demo/code/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code 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.demo.code", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devrath/DroidAndroidJetpack/bfa825ed41965c57a469eeb67e4b376fc8e2ab98/app/src/main/.DS_Store -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devrath/DroidAndroidJetpack/bfa825ed41965c57a469eeb67e4b376fc8e2ab98/app/src/main/java/com/demo/code/.DS_Store -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/JetPackFeatureSelectionActivity.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code 2 | 3 | import android.os.Bundle 4 | import com.demo.code.base.BaseActivity 5 | import com.demo.code.camerax.activities.CameraxActivity 6 | import com.demo.code.dataStore.activities.DataStoreActivity 7 | import com.demo.code.databinding.ActivityJetpackSelectionBinding 8 | import com.demo.code.lifecycle.activities.ExoplayerActivity 9 | import com.demo.code.liveData.activity.LiveDataActivity 10 | import com.demo.code.navigation.activities.SelectionScreenActivity 11 | import com.demo.code.paging.Paging3Activity 12 | import com.demo.code.workmanager.WorkManagerActivity 13 | import com.demo.extensions.intent.openActivity 14 | 15 | class JetPackFeatureSelectionActivity : BaseActivity() { 16 | 17 | private lateinit var binding: ActivityJetpackSelectionBinding 18 | 19 | override fun onCreate(savedInstanceState: Bundle?) { 20 | super.onCreate(savedInstanceState) 21 | binding = ActivityJetpackSelectionBinding.inflate(layoutInflater) 22 | setContentView(binding.root) 23 | 24 | binding.apply { 25 | this.jetpackNavigationId.setOnClickListener { 26 | openActivity(SelectionScreenActivity::class.java) 27 | } 28 | this.jetPackLifeCycleAwareId.setOnClickListener { 29 | openActivity(ExoplayerActivity::class.java) 30 | } 31 | this.jetPackPaging3Id.setOnClickListener{ 32 | openActivity(Paging3Activity::class.java) 33 | } 34 | this.jetDataStoreId.setOnClickListener{ 35 | openActivity(DataStoreActivity::class.java) 36 | } 37 | this.jetLiveDataId.setOnClickListener{ 38 | openActivity(LiveDataActivity::class.java) 39 | } 40 | this.jetPackCameraXId.setOnClickListener{ 41 | openActivity(CameraxActivity::class.java) 42 | } 43 | this.workManagerId.setOnClickListener{ 44 | openActivity(WorkManagerActivity::class.java) 45 | } 46 | } 47 | } 48 | 49 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/base/BaseActivity.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.base 2 | 3 | import androidx.appcompat.app.AppCompatActivity 4 | 5 | open class BaseActivity : AppCompatActivity() { 6 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/base/BaseFragment.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.base 2 | 3 | import androidx.fragment.app.Fragment 4 | 5 | open class BaseFragment : Fragment() { 6 | 7 | 8 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/camerax/activities/CameraxActivity.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.camerax.activities 2 | 3 | import android.os.Bundle 4 | import com.demo.code.base.BaseActivity 5 | import com.demo.code.databinding.ActivityCameraXBinding 6 | 7 | class CameraxActivity: BaseActivity() { 8 | 9 | private lateinit var binding: ActivityCameraXBinding 10 | 11 | 12 | override fun onCreate(savedInstanceState: Bundle?) { 13 | super.onCreate(savedInstanceState) 14 | binding = ActivityCameraXBinding.inflate(layoutInflater) 15 | setContentView(binding.root) 16 | } 17 | 18 | 19 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/camerax/placeholder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devrath/DroidAndroidJetpack/bfa825ed41965c57a469eeb67e4b376fc8e2ab98/app/src/main/java/com/demo/code/camerax/placeholder -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/dataStore/activities/DataStoreActivity.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.dataStore.activities 2 | 3 | import android.os.Bundle 4 | import com.demo.code.base.BaseActivity 5 | import com.demo.code.databinding.ActivityDataStoreBinding 6 | 7 | class DataStoreActivity : BaseActivity() { 8 | 9 | private lateinit var binding: ActivityDataStoreBinding 10 | 11 | override fun onCreate(savedInstanceState: Bundle?) { 12 | super.onCreate(savedInstanceState) 13 | binding = ActivityDataStoreBinding.inflate(layoutInflater) 14 | setContentView(binding.root) 15 | } 16 | 17 | 18 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/dataStore/fragments/SelectionDataStoreFragment.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.dataStore.fragments 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.navigation.fragment.findNavController 9 | import com.demo.code.R 10 | import com.demo.code.databinding.ActivityDataStoreBinding 11 | import com.demo.code.databinding.FragmentSelectionDataStoreBinding 12 | import com.demo.code.navigation.activities.NavigationDrawerActivity 13 | import com.demo.code.navigation.activities.TwoFragmentContainerActivity 14 | import com.demo.extensions.intent.openActivity 15 | 16 | 17 | class SelectionDataStoreFragment : Fragment(), View.OnClickListener { 18 | 19 | private var _binding: FragmentSelectionDataStoreBinding? = null 20 | private val binding get() = _binding!! 21 | 22 | override fun onCreateView( 23 | inflater: LayoutInflater, container: ViewGroup?, 24 | savedInstanceState: Bundle? 25 | ): View? { 26 | _binding = FragmentSelectionDataStoreBinding.inflate(inflater, container, false) 27 | return binding.root 28 | } 29 | 30 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 31 | super.onViewCreated(view, savedInstanceState) 32 | setClickListener() 33 | } 34 | 35 | private fun setClickListener() { 36 | binding.preferencesDsId.setOnClickListener(this) 37 | binding.protoDsId.setOnClickListener(this) 38 | } 39 | 40 | override fun onClick(view: View?) { 41 | when(view?.id){ 42 | R.id.preferencesDsId -> findNavController().navigate(R.id.preferenceDataStoreFragment) 43 | R.id.protoDsId -> findNavController().navigate(R.id.protoDataStoreFragment) 44 | } 45 | } 46 | 47 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/dataStore/fragments/preferenceDataStore/PreferenceDataStoreFragment.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.dataStore.fragments.preferenceDataStore 2 | 3 | import android.content.Context 4 | import android.os.Bundle 5 | import android.os.UserManager 6 | import androidx.fragment.app.Fragment 7 | import android.view.LayoutInflater 8 | import android.view.View 9 | import android.view.ViewGroup 10 | import androidx.lifecycle.asLiveData 11 | import com.demo.code.R 12 | import com.demo.code.dataStore.util.DataManager 13 | import com.demo.code.databinding.FragmentPreferenceDataStoreBinding 14 | import com.demo.code.databinding.FragmentProtoDataStoreBinding 15 | import kotlinx.coroutines.GlobalScope 16 | import kotlinx.coroutines.launch 17 | 18 | class PreferenceDataStoreFragment : Fragment() { 19 | 20 | private var _binding: FragmentPreferenceDataStoreBinding? = null 21 | private val binding get() = _binding!! 22 | 23 | lateinit var dataManager: DataManager 24 | var name = "" 25 | 26 | override fun onCreateView( 27 | inflater: LayoutInflater, container: ViewGroup?, 28 | savedInstanceState: Bundle? 29 | ): View? { 30 | _binding = FragmentPreferenceDataStoreBinding.inflate(inflater, container, false) 31 | return binding.root 32 | } 33 | 34 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 35 | super.onViewCreated(view, savedInstanceState) 36 | initDataStore() 37 | observeData() 38 | saveData() 39 | } 40 | 41 | private fun observeData() { 42 | // of UserManager class 43 | this.dataManager.dataAsFlow.asLiveData().observe(requireActivity()) { 44 | binding.displayTextId.text = it.toString() 45 | } 46 | } 47 | 48 | private fun saveData() { 49 | binding.apply { 50 | saveDataId.setOnClickListener { 51 | val dataToSave = binding.editTextTextPersonName.text.toString() 52 | GlobalScope.launch { dataManager.storeData(dataToSave) } 53 | } 54 | } 55 | } 56 | 57 | private fun initDataStore() { 58 | // Get reference to our userManager class 59 | dataManager = DataManager(requireContext()) 60 | } 61 | 62 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/dataStore/fragments/protoDataStore/ProtoDataStoreFragment.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.dataStore.fragments.protoDataStore 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 com.demo.code.R 9 | import com.demo.code.databinding.FragmentProtoDataStoreBinding 10 | import com.demo.code.databinding.FragmentSelectionDataStoreBinding 11 | 12 | class ProtoDataStoreFragment : Fragment() { 13 | 14 | private var _binding: FragmentProtoDataStoreBinding? = null 15 | private val binding get() = _binding!! 16 | 17 | override fun onCreateView( 18 | inflater: LayoutInflater, container: ViewGroup?, 19 | savedInstanceState: Bundle? 20 | ): View? { 21 | _binding = FragmentProtoDataStoreBinding.inflate(inflater, container, false) 22 | return binding.root 23 | } 24 | 25 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/dataStore/util/DataManager.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.dataStore.util 2 | 3 | import android.content.Context 4 | import androidx.datastore.core.DataStore 5 | import androidx.datastore.preferences.core.Preferences 6 | import androidx.datastore.preferences.core.edit 7 | import androidx.datastore.preferences.core.stringPreferencesKey 8 | import androidx.datastore.preferences.preferencesDataStore 9 | import kotlinx.coroutines.flow.Flow 10 | import kotlinx.coroutines.flow.map 11 | 12 | 13 | const val DATA_STORE_NAME = "user_prefs" 14 | const val DATA_KEY = "data_key" 15 | 16 | class DataManager(val context: Context) { 17 | 18 | // Create the dataStore and give it a name same as shared preferences 19 | val Context.dataStore: DataStore by preferencesDataStore(name = DATA_STORE_NAME) 20 | 21 | // Create some keys we will use them to store and retrieve the data 22 | companion object { 23 | val DATA_KEY_KEY = stringPreferencesKey(DATA_KEY) 24 | } 25 | 26 | // Store user data 27 | suspend fun storeData(data: String) { 28 | context.dataStore.edit { it[DATA_KEY_KEY] = data } 29 | } 30 | 31 | // Retrieve the data 32 | val dataAsFlow: Flow = context.dataStore.data 33 | .map { preferences -> preferences[DATA_KEY_KEY] ?: "" } 34 | 35 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/lifecycle/actions/ExoplayerAction.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.lifecycle.actions 2 | 3 | import android.net.Uri 4 | import com.google.android.exoplayer2.SimpleExoPlayer 5 | 6 | sealed class ExoplayerAction { 7 | data class BindExoplayer(val simpleExoplayer: SimpleExoPlayer) : ExoplayerAction() 8 | data class ProgressBarVisibility(val isVisible: Boolean) : ExoplayerAction() 9 | } 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/lifecycle/activities/ExoplayerActivity.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.lifecycle.activities 2 | 3 | import android.os.Bundle 4 | import android.view.View 5 | import com.demo.code.base.BaseActivity 6 | import com.demo.code.databinding.ActivityPlayerBinding 7 | import com.demo.code.lifecycle.actions.ExoplayerAction 8 | import com.demo.code.lifecycle.util.ExoplayerActivityObserver 9 | import com.google.android.exoplayer2.Player 10 | 11 | 12 | class ExoplayerActivity : BaseActivity(), Player.EventListener { 13 | 14 | private lateinit var binding: ActivityPlayerBinding 15 | 16 | private lateinit var locationListener: ExoplayerActivityObserver 17 | 18 | override fun onCreate(savedInstanceState: Bundle?) { 19 | super.onCreate(savedInstanceState) 20 | binding = ActivityPlayerBinding.inflate(layoutInflater) 21 | setContentView(binding.root) 22 | initExoplayerListener() 23 | } 24 | 25 | private fun initExoplayerListener() { 26 | locationListener = ExoplayerActivityObserver(lifecycle,this) { it -> 27 | when(it) { 28 | is ExoplayerAction.BindExoplayer -> binding.exoplayerView.player = it.simpleExoplayer 29 | is ExoplayerAction.ProgressBarVisibility -> handleProgressVisibilityOfPlayer(it.isVisible) 30 | } 31 | } 32 | } 33 | 34 | private fun handleProgressVisibilityOfPlayer(visible: Boolean) { 35 | if (visible) 36 | binding.progressBar.visibility = View.VISIBLE 37 | else 38 | binding.progressBar.visibility = View.INVISIBLE 39 | } 40 | 41 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/liveData/activity/LiveDataActivity.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.liveData.activity 2 | 3 | import android.os.Bundle 4 | import com.demo.code.base.BaseActivity 5 | import com.demo.code.databinding.ActivityLiveDataLayoutBinding 6 | 7 | class LiveDataActivity : BaseActivity() { 8 | 9 | private lateinit var binding: ActivityLiveDataLayoutBinding 10 | 11 | override fun onCreate(savedInstanceState: Bundle?) { 12 | super.onCreate(savedInstanceState) 13 | binding = ActivityLiveDataLayoutBinding.inflate(layoutInflater) 14 | setContentView(binding.root) 15 | } 16 | 17 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/liveData/mediatorLiveData/fragments/MediatorLiveDataFragment.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.liveData.mediatorLiveData.fragments 2 | 3 | import androidx.lifecycle.ViewModelProvider 4 | import android.os.Bundle 5 | import androidx.fragment.app.Fragment 6 | import android.view.LayoutInflater 7 | import android.view.View 8 | import android.view.ViewGroup 9 | import com.demo.code.databinding.MediatorLiveDataFragmentBinding 10 | import com.demo.code.liveData.mediatorLiveData.vm.MediatorLiveDataViewModel 11 | 12 | class MediatorLiveDataFragment : Fragment() { 13 | 14 | companion object { 15 | fun newInstance() = MediatorLiveDataFragment() 16 | } 17 | 18 | private var _binding: MediatorLiveDataFragmentBinding? = null 19 | private val binding get() = _binding!! 20 | 21 | private lateinit var viewModel: MediatorLiveDataViewModel 22 | 23 | override fun onCreateView( 24 | inflater: LayoutInflater, container: ViewGroup?, 25 | savedInstanceState: Bundle? 26 | ): View? { 27 | _binding = MediatorLiveDataFragmentBinding.inflate(inflater, container, false) 28 | return binding.root 29 | } 30 | 31 | override fun onActivityCreated(savedInstanceState: Bundle?) { 32 | super.onActivityCreated(savedInstanceState) 33 | viewModel = ViewModelProvider(this).get(MediatorLiveDataViewModel::class.java) 34 | observeLiveData() 35 | setClickListener() 36 | } 37 | 38 | private fun setClickListener() { 39 | binding.serverCounterId.setOnClickListener { 40 | viewModel.fetchDataFromServer() 41 | } 42 | binding.localCounterId.setOnClickListener { 43 | viewModel.fetchDataFromLocalDb() 44 | } 45 | } 46 | 47 | private fun observeLiveData() { 48 | viewModel.observeData().observe(viewLifecycleOwner,{ 49 | binding.counterValueId.text = it.toString() 50 | }) 51 | } 52 | 53 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/liveData/mediatorLiveData/vm/MediatorLiveDataViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.liveData.mediatorLiveData.vm 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MediatorLiveData 5 | import androidx.lifecycle.MutableLiveData 6 | import androidx.lifecycle.ViewModel 7 | 8 | class MediatorLiveDataViewModel : ViewModel() { 9 | 10 | private var counter : Int = 0 11 | 12 | private var counterFromServerLiveData : MutableLiveData = MutableLiveData() 13 | private var counterFromLocalDbLiveData : MutableLiveData = MutableLiveData() 14 | 15 | fun observeData(): LiveData { 16 | 17 | val dataSourceLiveData : MediatorLiveData = MediatorLiveData() 18 | 19 | dataSourceLiveData.addSource(counterFromServerLiveData) { 20 | dataSourceLiveData.value = it 21 | } 22 | dataSourceLiveData.addSource(counterFromLocalDbLiveData) { 23 | dataSourceLiveData.value = it 24 | } 25 | 26 | return dataSourceLiveData 27 | } 28 | 29 | fun fetchDataFromServer() { 30 | counterFromServerLiveData.value = counter++ 31 | } 32 | 33 | fun fetchDataFromLocalDb() { 34 | counterFromLocalDbLiveData.value = counter++ 35 | } 36 | 37 | 38 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/liveData/mutableLiveData/fragments/MutableLiveDataFragment.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.liveData.mutableLiveData.fragments 2 | 3 | import androidx.lifecycle.ViewModelProvider 4 | import android.os.Bundle 5 | import androidx.fragment.app.Fragment 6 | import android.view.LayoutInflater 7 | import android.view.View 8 | import android.view.ViewGroup 9 | import com.demo.code.databinding.MutableLiveDataFragmentBinding 10 | import com.demo.code.liveData.mutableLiveData.vm.MutableLiveDataViewModel 11 | 12 | class MutableLiveDataFragment : Fragment() { 13 | 14 | private var _binding: MutableLiveDataFragmentBinding? = null 15 | private val binding get() = _binding!! 16 | 17 | private lateinit var viewModel: MutableLiveDataViewModel 18 | 19 | override fun onCreateView( 20 | inflater: LayoutInflater, container: ViewGroup?, 21 | savedInstanceState: Bundle? 22 | ): View? { 23 | _binding = MutableLiveDataFragmentBinding.inflate(inflater, container, false) 24 | return binding.root 25 | } 26 | 27 | override fun onActivityCreated(savedInstanceState: Bundle?) { 28 | super.onActivityCreated(savedInstanceState) 29 | viewModel = ViewModelProvider(this).get(MutableLiveDataViewModel::class.java) 30 | observeLiveData() 31 | setClickListener() 32 | } 33 | 34 | private fun setClickListener() { 35 | binding.buttonId.setOnClickListener { 36 | viewModel.fetchDataFromServer() 37 | } 38 | } 39 | 40 | private fun observeLiveData() { 41 | viewModel.dataFromRepository().observe(viewLifecycleOwner,{ 42 | binding.counterValueId.text = it.toString() 43 | }) 44 | } 45 | 46 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/liveData/mutableLiveData/vm/MutableLiveDataViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.liveData.mutableLiveData.vm 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MutableLiveData 5 | import androidx.lifecycle.ViewModel 6 | 7 | /** 8 | * Live data is something that can be observed but can't be set 9 | * Mutable live data can be modified 10 | */ 11 | class MutableLiveDataViewModel : ViewModel() { 12 | 13 | private var counter : Int = 0 14 | 15 | private var counterLiveData : MutableLiveData = MutableLiveData() 16 | 17 | /** 18 | * Observe the live data from the Activity/Fragment 19 | */ 20 | fun dataFromRepository() : LiveData = counterLiveData 21 | 22 | /** 23 | * Simulation: New value obtained from the server 24 | */ 25 | fun fetchDataFromServer() { 26 | // Here we are updating the mutable live data 27 | counterLiveData.value = counter++ 28 | } 29 | 30 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/liveData/selection/fragments/SelectionLiveDataFragment.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.liveData.selection.fragments 2 | 3 | import androidx.lifecycle.ViewModelProvider 4 | import android.os.Bundle 5 | import androidx.fragment.app.Fragment 6 | import android.view.LayoutInflater 7 | import android.view.View 8 | import android.view.ViewGroup 9 | import androidx.navigation.fragment.findNavController 10 | import com.demo.code.R 11 | import com.demo.code.databinding.FragmentSelectionDataStoreBinding 12 | import com.demo.code.databinding.SelectionLiveDataFragmentBinding 13 | import com.demo.code.liveData.selection.vm.SelectionLiveDataViewModel 14 | 15 | class SelectionLiveDataFragment : Fragment(), View.OnClickListener { 16 | 17 | companion object { 18 | fun newInstance() = SelectionLiveDataFragment() 19 | } 20 | 21 | private var _binding: SelectionLiveDataFragmentBinding? = null 22 | private val binding get() = _binding!! 23 | 24 | private lateinit var viewModel: SelectionLiveDataViewModel 25 | 26 | override fun onCreateView( 27 | inflater: LayoutInflater, container: ViewGroup?, 28 | savedInstanceState: Bundle? 29 | ): View? { 30 | _binding = SelectionLiveDataFragmentBinding.inflate(inflater, container, false) 31 | return binding.root 32 | } 33 | 34 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 35 | super.onViewCreated(view, savedInstanceState) 36 | viewModel = ViewModelProvider(this).get(SelectionLiveDataViewModel::class.java) 37 | setClickListener() 38 | } 39 | 40 | private fun setClickListener() { 41 | binding.mutableLiveDataId.setOnClickListener(this) 42 | binding.mediatorLiveDataId.setOnClickListener(this) 43 | } 44 | 45 | override fun onClick(view: View?) { 46 | when(view?.id){ 47 | R.id.mutableLiveDataId -> { 48 | findNavController().navigate(R.id.mutableLiveDataFragment) 49 | } 50 | R.id.mediatorLiveDataId -> { 51 | findNavController().navigate(R.id.mediatorLiveDataFragment) 52 | } 53 | } 54 | } 55 | 56 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/liveData/selection/vm/SelectionLiveDataViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.liveData.selection.vm 2 | 3 | import androidx.lifecycle.ViewModel 4 | 5 | class SelectionLiveDataViewModel : ViewModel() { 6 | // TODO: Implement the ViewModel 7 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/navigation/activities/BottomNavigationActivity.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.navigation.activities 2 | 3 | import android.os.Bundle 4 | import androidx.navigation.fragment.NavHostFragment 5 | import androidx.navigation.ui.NavigationUI 6 | import com.demo.code.R 7 | import com.demo.code.base.BaseActivity 8 | import com.demo.code.databinding.ActivityBottomNavigationBinding 9 | 10 | class BottomNavigationActivity : BaseActivity() { 11 | 12 | private lateinit var binding: ActivityBottomNavigationBinding 13 | 14 | override fun onCreate(savedInstanceState: Bundle?) { 15 | super.onCreate(savedInstanceState) 16 | binding = ActivityBottomNavigationBinding.inflate(layoutInflater) 17 | setContentView(binding.root) 18 | setUpNavigation() 19 | } 20 | 21 | private fun setUpNavigation() { 22 | val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment? 23 | NavigationUI.setupWithNavController(binding.bttmNav, navHostFragment!!.navController) 24 | } 25 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/navigation/activities/SelectionScreenActivity.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.navigation.activities 2 | 3 | import android.os.Bundle 4 | import android.view.View 5 | import com.demo.code.R 6 | import com.demo.code.base.BaseActivity 7 | import com.demo.code.databinding.ActivitySelectionBinding 8 | import com.demo.extensions.intent.openActivity 9 | 10 | class SelectionScreenActivity : BaseActivity() , View.OnClickListener { 11 | 12 | private lateinit var binding: ActivitySelectionBinding 13 | 14 | override fun onCreate(savedInstanceState: Bundle?) { 15 | super.onCreate(savedInstanceState) 16 | binding = ActivitySelectionBinding.inflate(layoutInflater) 17 | setContentView(binding.root) 18 | setClickListener() 19 | } 20 | 21 | private fun setClickListener() { 22 | binding.navDrawerActivity.setOnClickListener(this) 23 | binding.navTwoContainerActivity.setOnClickListener(this) 24 | binding.navBottomNavigationActivity.setOnClickListener(this) 25 | } 26 | 27 | override fun onClick(view: View?) { 28 | when(view?.id){ 29 | R.id.navDrawerActivity -> openActivity(NavigationDrawerActivity::class.java) 30 | R.id.navTwoContainerActivity -> openActivity(TwoFragmentContainerActivity::class.java) 31 | R.id.navBottomNavigationActivity -> openActivity(BottomNavigationActivity::class.java) 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/navigation/activities/TwoFragmentContainerActivity.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.navigation.activities 2 | 3 | import android.os.Bundle 4 | import com.demo.code.R 5 | import com.demo.code.base.BaseActivity 6 | 7 | class TwoFragmentContainerActivity : BaseActivity() { 8 | override fun onCreate(savedInstanceState: Bundle?) { 9 | super.onCreate(savedInstanceState) 10 | setContentView(R.layout.activity_two_frag_containers) 11 | } 12 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/navigation/fragments/FragmentA.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.navigation.fragments 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.navigation.fragment.findNavController 9 | import com.demo.code.R 10 | import com.demo.code.databinding.FragmentLayoutABinding 11 | 12 | class FragmentA : Fragment() { 13 | 14 | private var _binding: FragmentLayoutABinding? = null 15 | private val binding get() = _binding!! 16 | 17 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, 18 | savedInstanceState: Bundle?): View? { 19 | _binding = FragmentLayoutABinding.inflate(inflater, container, false) 20 | return binding.root 21 | } 22 | 23 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 24 | super.onViewCreated(view, savedInstanceState) 25 | binding.apply { 26 | this.FragmentANextId.setOnClickListener{ 27 | findNavController().navigate(R.id.fragmentC) 28 | } 29 | this.FragmentABackId.setOnClickListener{ 30 | findNavController().popBackStack() 31 | } 32 | } 33 | } 34 | 35 | override fun onDestroyView() { 36 | super.onDestroyView() 37 | _binding = null 38 | } 39 | 40 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/navigation/fragments/FragmentB.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.navigation.fragments 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.navigation.fragment.findNavController 9 | import com.demo.code.R 10 | import com.demo.code.databinding.FragmentLayoutBBinding 11 | 12 | class FragmentB : Fragment() { 13 | 14 | private var _binding: FragmentLayoutBBinding? = null 15 | private val binding get() = _binding!! 16 | 17 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, 18 | savedInstanceState: Bundle?): View? { 19 | _binding = FragmentLayoutBBinding.inflate(inflater, container, false) 20 | return binding.root 21 | } 22 | 23 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 24 | super.onViewCreated(view, savedInstanceState) 25 | binding.apply { 26 | this.FragmentBNextId.setOnClickListener{ 27 | findNavController().navigate(R.id.fragmentD) 28 | } 29 | this.FragmentBBackId.setOnClickListener{ 30 | findNavController().popBackStack() 31 | } 32 | } 33 | } 34 | 35 | override fun onDestroyView() { 36 | super.onDestroyView() 37 | _binding = null 38 | } 39 | 40 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/navigation/fragments/FragmentC.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.navigation.fragments 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.navigation.fragment.findNavController 9 | import com.demo.code.databinding.FragmentLayoutCBinding 10 | import com.demo.extensions.toast.toast 11 | 12 | class FragmentC : Fragment() { 13 | 14 | private var _binding: FragmentLayoutCBinding? = null 15 | private val binding get() = _binding!! 16 | 17 | override fun onCreateView( 18 | inflater: LayoutInflater, container: ViewGroup?, 19 | savedInstanceState: Bundle? 20 | ): View? { 21 | _binding = FragmentLayoutCBinding.inflate(inflater, container, false) 22 | return binding.root 23 | } 24 | 25 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 26 | super.onViewCreated(view, savedInstanceState) 27 | binding.apply { 28 | this.FragmentCNextId.setOnClickListener{ 29 | activity?.let { 30 | toast("Last Fragment",false,it) 31 | } 32 | } 33 | this.FragmentCBackId.setOnClickListener{ 34 | findNavController().popBackStack() 35 | } 36 | } 37 | } 38 | 39 | override fun onDestroyView() { 40 | super.onDestroyView() 41 | _binding = null 42 | } 43 | 44 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/navigation/fragments/FragmentD.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.navigation.fragments 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.navigation.fragment.findNavController 9 | import com.demo.code.R 10 | import com.demo.code.databinding.FragmentLayoutDBinding 11 | 12 | class FragmentD : Fragment() { 13 | 14 | private var _binding: FragmentLayoutDBinding? = null 15 | private val binding get() = _binding!! 16 | 17 | override fun onCreateView( 18 | inflater: LayoutInflater, container: ViewGroup?, 19 | savedInstanceState: Bundle? 20 | ): View? { 21 | _binding = FragmentLayoutDBinding.inflate(inflater, container, false) 22 | return binding.root 23 | } 24 | 25 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 26 | super.onViewCreated(view, savedInstanceState) 27 | binding.apply { 28 | this.FragmentDNextId.setOnClickListener{ 29 | findNavController().navigate(R.id.fragmentE) 30 | } 31 | this.FragmentDBackId.setOnClickListener{ 32 | findNavController().popBackStack() 33 | } 34 | } 35 | } 36 | 37 | override fun onDestroyView() { 38 | super.onDestroyView() 39 | _binding = null 40 | } 41 | 42 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/navigation/fragments/FragmentE.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.navigation.fragments 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.navigation.fragment.findNavController 9 | import com.demo.code.R 10 | import com.demo.code.databinding.FragmentLayoutEBinding 11 | 12 | class FragmentE : Fragment() { 13 | 14 | private var _binding: FragmentLayoutEBinding? = null 15 | private val binding get() = _binding!! 16 | 17 | override fun onCreateView( 18 | inflater: LayoutInflater, container: ViewGroup?, 19 | savedInstanceState: Bundle? 20 | ): View? { 21 | _binding = FragmentLayoutEBinding.inflate(inflater, container, false) 22 | return binding.root 23 | } 24 | 25 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 26 | super.onViewCreated(view, savedInstanceState) 27 | binding.apply { 28 | this.FragmentDNextId.setOnClickListener{ 29 | findNavController().navigate(R.id.fragmentF) 30 | } 31 | this.FragmentDBackId.setOnClickListener{ 32 | findNavController().popBackStack() 33 | } 34 | } 35 | } 36 | 37 | override fun onDestroyView() { 38 | super.onDestroyView() 39 | _binding = null 40 | } 41 | 42 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/navigation/fragments/FragmentF.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.navigation.fragments 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.navigation.fragment.findNavController 9 | import com.demo.code.databinding.FragmentLayoutFBinding 10 | import com.demo.extensions.toast.toast 11 | 12 | class FragmentF : Fragment() { 13 | 14 | private var _binding: FragmentLayoutFBinding? = null 15 | private val binding get() = _binding!! 16 | 17 | override fun onCreateView( 18 | inflater: LayoutInflater, container: ViewGroup?, 19 | savedInstanceState: Bundle? 20 | ): View? { 21 | _binding = FragmentLayoutFBinding.inflate(inflater, container, false) 22 | return binding.root 23 | } 24 | 25 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 26 | super.onViewCreated(view, savedInstanceState) 27 | binding.apply { 28 | this.FragmentDNextId.setOnClickListener{ 29 | activity?.let { 30 | toast("Last Fragment",false,it) 31 | } 32 | } 33 | this.FragmentDBackId.setOnClickListener{ 34 | findNavController().popBackStack() 35 | } 36 | } 37 | } 38 | 39 | override fun onDestroyView() { 40 | super.onDestroyView() 41 | _binding = null 42 | } 43 | 44 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/paging/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devrath/DroidAndroidJetpack/bfa825ed41965c57a469eeb67e4b376fc8e2ab98/app/src/main/java/com/demo/code/paging/.DS_Store -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/paging/Paging3Activity.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.paging 2 | 3 | import android.os.Bundle 4 | import com.demo.code.base.BaseActivity 5 | import com.demo.code.databinding.ActivityPagingBinding 6 | import com.demo.code.paging.usingLocalSource.ui.PagingFromLocalDbActivity 7 | import com.demo.code.paging.usingRemoteAndLocalSource.ui.PagingFromLocalRemoteApiActivity 8 | import com.demo.code.paging.usingRemoteSource.ui.PagingFromRemoteApiActivity 9 | import com.demo.extensions.intent.openActivity 10 | 11 | class Paging3Activity : BaseActivity() { 12 | 13 | private lateinit var binding: ActivityPagingBinding 14 | 15 | override fun onCreate(savedInstanceState: Bundle?) { 16 | super.onCreate(savedInstanceState) 17 | binding = ActivityPagingBinding.inflate(layoutInflater) 18 | setContentView(binding.root) 19 | 20 | binding.apply { 21 | this.pagingFromLocalDbId.setOnClickListener { 22 | openActivity(PagingFromLocalDbActivity::class.java) 23 | } 24 | this.pagingFromRemoteApiId.setOnClickListener { 25 | openActivity(PagingFromRemoteApiActivity::class.java) 26 | } 27 | this.pagingFromLocalRemoteApiId.setOnClickListener { 28 | openActivity(PagingFromLocalRemoteApiActivity::class.java) 29 | } 30 | } 31 | } 32 | 33 | 34 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/paging/usingLocalSource/Executors.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.paging.usingLocalSource 2 | 3 | import java.util.concurrent.Executors 4 | 5 | private val BG_EXECUTOR = Executors.newSingleThreadExecutor() 6 | 7 | fun bgThread(f : () -> Unit) { 8 | BG_EXECUTOR.execute(f) 9 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/paging/usingLocalSource/Extensions.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.paging.usingLocalSource 2 | 3 | import android.view.LayoutInflater 4 | import android.view.View 5 | import android.view.ViewGroup 6 | import androidx.annotation.LayoutRes 7 | 8 | fun ViewGroup.inflate(@LayoutRes layoutRes: Int, attachToRoot: Boolean = false): View { 9 | return LayoutInflater.from(context).inflate(layoutRes, this, attachToRoot) 10 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/paging/usingLocalSource/db/MovieDao.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.paging.usingLocalSource.db 2 | 3 | import androidx.paging.DataSource 4 | import androidx.room.Dao 5 | import androidx.room.Delete 6 | import androidx.room.Insert 7 | import androidx.room.Query 8 | import com.demo.code.paging.usingLocalSource.model.Movie 9 | 10 | @Dao 11 | interface MovieDao { 12 | 13 | /* 14 | * 15 | * 16 | * 17 | @Query("SELECT * FROM Movie ORDER BY ranking") 18 | fun allMovies(): LiveData> 19 | * 20 | * 21 | --- Instead of Live data we shall replace with a factory that returns the positional data source 22 | * 23 | */ 24 | @Query("SELECT * FROM Movie ORDER BY ranking") 25 | fun allMovies(): DataSource.Factory 26 | 27 | @Insert 28 | fun insert(movies: List) 29 | 30 | @Insert 31 | fun insert(movie: Movie) 32 | 33 | @Delete 34 | fun delete(movie: Movie) 35 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/paging/usingLocalSource/db/MovieDatabase.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.paging.usingLocalSource.db 2 | 3 | import android.content.Context 4 | import android.util.Log 5 | import androidx.room.Database 6 | import androidx.room.Room 7 | import androidx.room.RoomDatabase 8 | import androidx.sqlite.db.SupportSQLiteDatabase 9 | import com.demo.code.paging.usingLocalSource.bgThread 10 | import com.demo.code.paging.usingLocalSource.model.Movie 11 | import com.demo.code.paging.usingLocalSource.model.MovieData 12 | import com.google.gson.Gson 13 | import java.io.IOException 14 | import java.nio.charset.StandardCharsets 15 | 16 | @Database(entities = [(Movie::class)], version = 1, exportSchema = false) 17 | abstract class MovieDatabase : RoomDatabase() { 18 | abstract fun movieDao(): MovieDao 19 | 20 | companion object { 21 | private const val TAG = "MovieDatabase" 22 | private var instance: MovieDatabase? = null 23 | 24 | @Synchronized 25 | fun get(context: Context): MovieDatabase { 26 | if (instance == null) { 27 | instance = Room.databaseBuilder(context.applicationContext, 28 | MovieDatabase::class.java, "MovieDatabase") 29 | .addCallback(object : RoomDatabase.Callback() { 30 | override fun onCreate(db: SupportSQLiteDatabase) { 31 | fillInDatabase(context.applicationContext) 32 | } 33 | }).build() 34 | } 35 | return instance!! 36 | } 37 | 38 | private fun fillInDatabase(context: Context) { 39 | bgThread { 40 | val jsonString = readJson(context) 41 | 42 | if (jsonString != null) { 43 | val movieData = Gson().fromJson(jsonString, MovieData::class.java) 44 | get(context).movieDao().insert(movieData.movies) 45 | Log.v(TAG, movieData.toString()) 46 | } 47 | } 48 | } 49 | 50 | private fun readJson(context: Context): String? { 51 | var json: String? = null 52 | 53 | try { 54 | val inputStream = context.assets.open("movie_data.json") 55 | val size = inputStream.available() 56 | val buffer = ByteArray(size) 57 | inputStream.read(buffer) 58 | inputStream.close() 59 | json = String(buffer, StandardCharsets.UTF_8) 60 | } catch (e: IOException) { 61 | Log.e(TAG, "Error reading JSON data ::: " + e.message, e) 62 | } 63 | 64 | return json 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/paging/usingLocalSource/model/Movie.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.paging.usingLocalSource.model 2 | import androidx.room.Entity 3 | import androidx.room.PrimaryKey 4 | import com.google.gson.annotations.SerializedName 5 | 6 | @Entity 7 | data class Movie( 8 | @PrimaryKey(autoGenerate = true) val id: Int, 9 | val title: String, 10 | val rating: Double, 11 | @SerializedName("release_date") val releaseDate: String, 12 | val ranking: Int) -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/paging/usingLocalSource/model/MovieData.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.paging.usingLocalSource.model 2 | 3 | 4 | data class MovieData(var movies: List) -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/paging/usingLocalSource/ui/MovieListAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.paging.usingLocalSource.ui 2 | 3 | import android.view.View 4 | import android.view.ViewGroup 5 | import androidx.paging.PagedListAdapter 6 | import androidx.recyclerview.widget.DiffUtil 7 | import androidx.recyclerview.widget.RecyclerView 8 | import com.demo.code.R 9 | import com.demo.code.paging.usingLocalSource.inflate 10 | import kotlinx.android.extensions.LayoutContainer 11 | import com.demo.code.paging.usingLocalSource.model.Movie 12 | import kotlinx.android.synthetic.main.list_item_movie.* 13 | 14 | /** 15 | * @param Movie -> Type of the data per each item row 16 | * @param ViewHolder -> View holder patter to recycle the views 17 | * @param diffCallback -> We pass the diff util callback to the page list adapter so it updates the page list 18 | */ 19 | class MovieListAdapter : PagedListAdapter(diffCallback) { 20 | 21 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = 22 | MovieViewHolder(parent.inflate(R.layout.list_item_movie)) 23 | 24 | override fun onBindViewHolder(holder: MovieViewHolder, position: Int) { 25 | holder.bind(getItem(position)) 26 | } 27 | 28 | companion object { 29 | 30 | private val diffCallback = object : DiffUtil.ItemCallback() { 31 | override fun areItemsTheSame(oldItem: Movie, newItem: Movie): Boolean = 32 | oldItem.id == newItem.id 33 | override fun areContentsTheSame(oldItem: Movie, newItem: Movie): Boolean = 34 | oldItem == newItem 35 | } 36 | } 37 | 38 | class MovieViewHolder(override val containerView: View) 39 | : RecyclerView.ViewHolder(containerView), LayoutContainer { 40 | 41 | var movie: Movie? = null 42 | 43 | fun bind(movie: Movie?) { 44 | this.movie = movie 45 | title.text = movie?.title 46 | releaseDate.text = movie?.releaseDate?.substring(0, 4) 47 | rating.text = movie?.rating.toString() 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/paging/usingLocalSource/ui/MovieListViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.paging.usingLocalSource.ui 2 | 3 | import android.app.Application 4 | import androidx.lifecycle.AndroidViewModel 5 | import androidx.paging.LivePagedListBuilder 6 | import androidx.paging.PagedList 7 | import com.demo.code.paging.usingLocalSource.bgThread 8 | import com.demo.code.paging.usingLocalSource.db.MovieDatabase 9 | import com.demo.code.paging.usingLocalSource.model.Movie 10 | 11 | class MovieListViewModel(application: Application) : AndroidViewModel(application) { 12 | private val dao = MovieDatabase.get(application).movieDao() 13 | 14 | val allMovies = LivePagedListBuilder(dao.allMovies(), PagedList.Config.Builder() 15 | .setPageSize(PAGE_SIZE) 16 | .setEnablePlaceholders(ENABLE_PLACEHOLDERS) 17 | .build()).build() 18 | 19 | fun remove(movie: Movie) = bgThread { 20 | dao.delete(movie) 21 | } 22 | 23 | companion object { 24 | private const val PAGE_SIZE = 30 25 | private const val ENABLE_PLACEHOLDERS = true 26 | } 27 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/paging/usingRemoteAndLocalSource/database/LocalDatabase.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.paging.usingRemoteAndLocalSource.database 2 | 3 | import android.content.Context 4 | import androidx.room.Database 5 | import androidx.room.Room 6 | import androidx.room.RoomDatabase 7 | import com.demo.code.paging.usingRemoteSource.database.dao.KeysDao 8 | import com.demo.code.paging.usingRemoteSource.models.PostsKeys 9 | import com.demo.code.paging.usingRemoteSource.models.FeedPost 10 | import com.demo.code.paging.usingRemoteSource.database.dao.PostsDao 11 | import com.demo.code.paging.usingRemoteSource.database.LocalDatabase 12 | 13 | @Database( 14 | entities = [FeedPost::class, PostsKeys::class], 15 | version = 1, 16 | exportSchema = false 17 | ) 18 | abstract class LocalDatabase : RoomDatabase() { 19 | 20 | companion object { 21 | 22 | private const val DATABASE_NAME = "paging_demo.db" 23 | private val LOCAL_DATABASE = LocalDatabase::class.java 24 | 25 | fun create(context: Context): LocalDatabase { 26 | val databaseBuilder = Room.databaseBuilder(context, LOCAL_DATABASE, DATABASE_NAME) 27 | return databaseBuilder.build() 28 | } 29 | } 30 | 31 | abstract fun postsDao(): PostsDao 32 | abstract fun keysDao(): KeysDao 33 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/paging/usingRemoteAndLocalSource/database/dao/KeysDao.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.paging.usingRemoteAndLocalSource.database.dao 2 | 3 | import androidx.room.Dao 4 | import androidx.room.Insert 5 | import androidx.room.OnConflictStrategy.REPLACE 6 | import androidx.room.Query 7 | import com.demo.code.paging.usingRemoteSource.models.PostsKeys 8 | 9 | @Dao 10 | interface KeysDao { 11 | 12 | @Insert(onConflict = REPLACE) 13 | suspend fun savePostsKeys(postsKey: PostsKeys) 14 | 15 | @Query("SELECT * FROM postKeys ORDER BY id DESC") 16 | suspend fun getPostsKeys(): List 17 | 18 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/paging/usingRemoteAndLocalSource/database/dao/PostsDao.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.paging.usingRemoteAndLocalSource.database.dao 2 | 3 | import androidx.paging.PagingSource 4 | import androidx.room.Dao 5 | import androidx.room.Insert 6 | import androidx.room.OnConflictStrategy.REPLACE 7 | import androidx.room.Query 8 | import com.demo.code.paging.usingRemoteSource.models.FeedPost 9 | 10 | @Dao 11 | interface PostsDao { 12 | 13 | @Insert(onConflict = REPLACE) 14 | suspend fun savePosts(feedPosts: List) 15 | 16 | @Query("SELECT * FROM feedPosts") 17 | fun getPosts(): PagingSource 18 | 19 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/paging/usingRemoteAndLocalSource/models/FeedPost.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.paging.usingRemoteAndLocalSource.models 2 | 3 | import androidx.room.Entity 4 | import androidx.room.PrimaryKey 5 | import com.google.gson.annotations.SerializedName 6 | 7 | @Entity(tableName = "feedPosts") 8 | data class FeedPost( 9 | @SerializedName("name") 10 | @PrimaryKey 11 | val key: String, 12 | @SerializedName("title") 13 | val title: String, 14 | @SerializedName("score") 15 | val score: Int, 16 | @SerializedName("author") 17 | val author: String, 18 | @SerializedName("num_comments") 19 | val commentCount: Int 20 | ) 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/paging/usingRemoteAndLocalSource/models/PostContainer.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.paging.usingRemoteAndLocalSource.models 2 | 3 | import com.demo.code.paging.usingRemoteSource.models.FeedPost 4 | 5 | class PostContainer(val data: FeedPost) -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/paging/usingRemoteAndLocalSource/models/PostsApiResponse.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.paging.usingRemoteAndLocalSource.models 2 | 3 | import com.demo.code.paging.usingRemoteSource.models.PostsListing 4 | 5 | 6 | class PostsApiResponse(val data: PostsListing) -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/paging/usingRemoteAndLocalSource/models/PostsKeys.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.paging.usingRemoteAndLocalSource.models 2 | 3 | import androidx.room.Entity 4 | import androidx.room.PrimaryKey 5 | 6 | @Entity(tableName = "postKeys") 7 | data class PostsKeys( 8 | @PrimaryKey(autoGenerate = true) 9 | val id: Int, 10 | val after: String?, 11 | val before: String? 12 | ) 13 | -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/paging/usingRemoteAndLocalSource/models/PostsListing.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.paging.usingRemoteAndLocalSource.models 2 | 3 | import com.demo.code.paging.usingRemoteSource.models.PostContainer 4 | 5 | class PostsListing(val children: List, val after: String?, val before: String?) -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/paging/usingRemoteAndLocalSource/networking/ApiClient.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.paging.usingRemoteAndLocalSource.networking 2 | 3 | import retrofit2.Retrofit 4 | import retrofit2.converter.gson.GsonConverterFactory 5 | 6 | class ApiClient { 7 | 8 | companion object { 9 | 10 | private const val BASE_URL = "https://www.reddit.com/" 11 | private var retrofit: Retrofit? = null 12 | 13 | fun getClient(): Retrofit { 14 | when (retrofit) { 15 | null -> retrofit = Retrofit.Builder() 16 | .baseUrl(BASE_URL) 17 | .addConverterFactory(GsonConverterFactory.create()) 18 | .build() 19 | } 20 | return retrofit as Retrofit 21 | } 22 | 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/paging/usingRemoteAndLocalSource/networking/RemoteService.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.paging.usingRemoteAndLocalSource.networking 2 | 3 | import com.demo.code.paging.usingRemoteSource.models.PostsApiResponse 4 | import com.demo.code.paging.usingRemoteSource.networking.RemoteService 5 | import retrofit2.Response 6 | import retrofit2.http.GET 7 | import retrofit2.http.Query 8 | 9 | interface RemoteService { 10 | 11 | companion object { 12 | val service = RemoteService::class.java 13 | } 14 | 15 | @GET("/r/aww/hot.json") 16 | suspend fun fetchPosts( 17 | @Query("limit") loadSize: Int = 0, 18 | @Query("after") after: String? = null, 19 | @Query("before") before: String? = null 20 | ): Response 21 | 22 | 23 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/paging/usingRemoteAndLocalSource/repositories/DataMediator.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.paging.usingRemoteAndLocalSource.repositories 2 | 3 | import androidx.paging.* 4 | import androidx.room.withTransaction 5 | import com.demo.code.paging.usingRemoteSource.database.LocalDatabase 6 | import com.demo.code.paging.usingRemoteSource.models.PostsKeys 7 | import com.demo.code.paging.usingRemoteSource.models.FeedPost 8 | import com.demo.code.paging.usingRemoteSource.networking.RemoteService 9 | import retrofit2.HttpException 10 | import java.io.IOException 11 | 12 | @OptIn(ExperimentalPagingApi::class) 13 | class DataMediator( 14 | private val remoteService: RemoteService, 15 | private val LocalDatabase: LocalDatabase 16 | ) : RemoteMediator() { 17 | 18 | override suspend fun load( 19 | loadType: LoadType, 20 | state: PagingState 21 | ): MediatorResult { 22 | 23 | return try { 24 | 25 | val loadKey = when(loadType){ 26 | LoadType.REFRESH -> null 27 | LoadType.PREPEND -> return MediatorResult.Success(endOfPaginationReached = true) 28 | LoadType.APPEND ->{ 29 | state.lastItemOrNull() 30 | ?: return MediatorResult.Success(endOfPaginationReached = true) 31 | getRedditKeys() 32 | } 33 | } 34 | 35 | val response = remoteService.fetchPosts( 36 | loadSize = state.config.pageSize, 37 | after = loadKey?.after, 38 | before = loadKey?.before 39 | ) 40 | 41 | val listing = response.body()?.data 42 | 43 | val feedPosts = listing?.children?.map { it.data } 44 | 45 | if (feedPosts != null) { 46 | LocalDatabase.withTransaction { 47 | LocalDatabase.keysDao() 48 | .savePostsKeys(PostsKeys(0, listing.after, listing.before)) 49 | LocalDatabase.postsDao().savePosts(feedPosts) 50 | } 51 | 52 | } 53 | MediatorResult.Success(endOfPaginationReached = listing?.after == null) 54 | 55 | } catch (exception: IOException) { 56 | MediatorResult.Error(exception) 57 | } catch (exception: HttpException) { 58 | MediatorResult.Error(exception) 59 | } 60 | 61 | } 62 | 63 | private suspend fun getRedditKeys(): PostsKeys? { 64 | return LocalDatabase.keysDao().getPostsKeys().firstOrNull() 65 | } 66 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/paging/usingRemoteAndLocalSource/repositories/Repository.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.paging.usingRemoteAndLocalSource.repositories 2 | 3 | import android.content.Context 4 | import androidx.paging.ExperimentalPagingApi 5 | import androidx.paging.Pager 6 | import androidx.paging.PagingConfig 7 | import androidx.paging.PagingData 8 | import com.demo.code.paging.usingRemoteSource.database.LocalDatabase 9 | import com.demo.code.paging.usingRemoteSource.models.FeedPost 10 | import com.demo.code.paging.usingRemoteSource.networking.ApiClient 11 | import com.demo.code.paging.usingRemoteSource.networking.RemoteService 12 | import com.demo.code.paging.usingRemoteSource.repositories.DataMediator 13 | import kotlinx.coroutines.flow.Flow 14 | 15 | class Repository(context: Context) { 16 | 17 | // Remote API reference 18 | private val apiService = ApiClient.getClient().create(RemoteService.service) 19 | // Local database reference 20 | private val localDatabase = LocalDatabase.create(context) 21 | 22 | /** 23 | * @return Flow of Paging data 24 | */ 25 | @OptIn(ExperimentalPagingApi::class) 26 | fun fetchPosts(): Flow> { 27 | 28 | return Pager( 29 | PagingConfig(pageSize = 10, enablePlaceholders = false, prefetchDistance = 1), 30 | remoteMediator = DataMediator(apiService, localDatabase), 31 | pagingSourceFactory = { localDatabase.postsDao().getPosts() } 32 | ).flow 33 | 34 | } 35 | 36 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/paging/usingRemoteAndLocalSource/ui/LocalRemoteApiLoadingAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.paging.usingRemoteAndLocalSource.ui 2 | 3 | import android.view.LayoutInflater 4 | import android.view.View 5 | import android.view.ViewGroup 6 | import android.widget.Button 7 | import android.widget.ProgressBar 8 | import android.widget.TextView 9 | import androidx.core.view.isVisible 10 | import androidx.paging.LoadState 11 | import androidx.paging.LoadStateAdapter 12 | import androidx.recyclerview.widget.RecyclerView 13 | import com.demo.code.R 14 | import kotlinx.android.synthetic.main.item_loading_state.view.* 15 | 16 | class LocalRemoteApiLoadingAdapter(private val retry: () -> Unit) : 17 | LoadStateAdapter() { 18 | 19 | override fun onBindViewHolder(holder: LoadingStateViewHolder, loadState: LoadState) { 20 | holder.bindState(loadState) 21 | } 22 | 23 | override fun onCreateViewHolder( 24 | parent: ViewGroup, 25 | loadState: LoadState 26 | ): LoadingStateViewHolder { 27 | val view = LayoutInflater.from(parent.context) 28 | .inflate(R.layout.item_loading_state, parent, false) 29 | return LoadingStateViewHolder(view, retry) 30 | } 31 | 32 | 33 | class LoadingStateViewHolder(itemView: View, retry: () -> Unit) : 34 | RecyclerView.ViewHolder(itemView) { 35 | 36 | private val tvErrorMessage: TextView = itemView.tvErrorMessage 37 | private val progressBar: ProgressBar = itemView.progress_bar 38 | private val btnRetry: Button = itemView.btnRetry 39 | 40 | init { 41 | btnRetry.setOnClickListener { 42 | retry.invoke() 43 | } 44 | } 45 | 46 | 47 | fun bindState(loadState: LoadState) { 48 | if (loadState is LoadState.Error) { 49 | tvErrorMessage.text = loadState.error.localizedMessage 50 | } 51 | progressBar.isVisible = loadState is LoadState.Loading 52 | tvErrorMessage.isVisible = loadState !is LoadState.Loading 53 | btnRetry.isVisible = loadState !is LoadState.Loading 54 | } 55 | 56 | } 57 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/paging/usingRemoteAndLocalSource/ui/LocalRemoteApiViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.paging.usingRemoteAndLocalSource.ui 2 | 3 | import android.app.Application 4 | import androidx.lifecycle.AndroidViewModel 5 | import androidx.lifecycle.viewModelScope 6 | import androidx.paging.PagingData 7 | import androidx.paging.cachedIn 8 | import com.demo.code.paging.usingRemoteSource.models.FeedPost 9 | import com.demo.code.paging.usingRemoteSource.repositories.Repository 10 | import kotlinx.coroutines.flow.Flow 11 | 12 | class LocalRemoteApiViewModel(application: Application) : AndroidViewModel(application) { 13 | 14 | companion object { 15 | val thisClass = LocalRemoteApiViewModel::class.java 16 | } 17 | 18 | private val redditRepo = Repository(application) 19 | 20 | fun fetchPosts(): Flow> { 21 | 22 | return redditRepo.fetchPosts().cachedIn(viewModelScope) 23 | 24 | } 25 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/paging/usingRemoteAndLocalSource/ui/PagingFromLocalRemoteApiActivity.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.paging.usingRemoteAndLocalSource.ui 2 | 3 | import android.os.Bundle 4 | import androidx.lifecycle.ViewModelProvider 5 | import androidx.lifecycle.lifecycleScope 6 | import com.demo.code.R 7 | import com.demo.code.base.BaseActivity 8 | import com.demo.code.databinding.ActivityPagingFromLocalRemoteApiBinding 9 | import kotlinx.coroutines.flow.collectLatest 10 | import kotlinx.coroutines.launch 11 | 12 | class PagingFromLocalRemoteApiActivity : BaseActivity() { 13 | 14 | // Binding: View references 15 | private lateinit var binding: ActivityPagingFromLocalRemoteApiBinding 16 | 17 | // Adapter: List of items 18 | private val adapter = RemoteApiAdapter() 19 | 20 | // viewModel reference 21 | private val localRemoteApiViewModel: LocalRemoteApiViewModel by lazy { 22 | ViewModelProvider(this).get(LocalRemoteApiViewModel.thisClass) 23 | } 24 | 25 | override fun onCreate(savedInstanceState: Bundle?) { 26 | setTheme(R.style.AppTheme) 27 | super.onCreate(savedInstanceState) 28 | binding = ActivityPagingFromLocalRemoteApiBinding.inflate(layoutInflater) 29 | setContentView(binding.root) 30 | setupViews() 31 | fetchPosts() 32 | } 33 | 34 | private fun fetchPosts() { 35 | lifecycleScope.launch { 36 | 37 | localRemoteApiViewModel.fetchPosts().collectLatest { pagingData -> 38 | // We get the new data from the flow - We publish the new data to adapter 39 | adapter.submitData(pagingData) 40 | } 41 | } 42 | } 43 | 44 | private fun setupViews() { 45 | binding.apply { 46 | // Adapter: List of items 47 | rvPosts.adapter = adapter 48 | // Adapters: Header and Footer item 49 | rvPosts.adapter = adapter.withLoadStateHeaderAndFooter( 50 | // Header 51 | header = LocalRemoteApiLoadingAdapter { adapter.retry() }, 52 | // Footer 53 | footer = LocalRemoteApiLoadingAdapter { adapter.retry() } 54 | ) 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/paging/usingRemoteAndLocalSource/ui/RemoteApiAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.paging.usingRemoteAndLocalSource.ui 2 | 3 | import android.view.LayoutInflater 4 | import android.view.View 5 | import android.view.ViewGroup 6 | import android.widget.TextView 7 | import androidx.paging.PagingDataAdapter 8 | import androidx.recyclerview.widget.RecyclerView 9 | import com.demo.code.R 10 | import com.demo.code.paging.usingRemoteSource.models.FeedPost 11 | import com.demo.code.paging.usingRemoteSource.utils.DiffUtilCallBack 12 | import kotlinx.android.synthetic.main.adapter_row.view.* 13 | 14 | class RemoteApiAdapter : 15 | PagingDataAdapter(DiffUtilCallBack()) { 16 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RedditViewHolder { 17 | val view = LayoutInflater.from(parent.context).inflate(R.layout.adapter_row, parent, false) 18 | return RedditViewHolder(view) 19 | } 20 | 21 | override fun onBindViewHolder(holder: RedditViewHolder, position: Int) { 22 | getItem(position)?.let { holder.bindPost(it) } 23 | } 24 | 25 | class RedditViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { 26 | private val titleText: TextView = itemView.title 27 | 28 | fun bindPost(feedPost: FeedPost) { 29 | with(feedPost) { 30 | titleText.text = title 31 | } 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/paging/usingRemoteAndLocalSource/utils/DiffUtilCallBack.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.paging.usingRemoteAndLocalSource.utils 2 | 3 | import androidx.recyclerview.widget.DiffUtil 4 | import com.demo.code.paging.usingRemoteSource.models.FeedPost 5 | 6 | class DiffUtilCallBack : DiffUtil.ItemCallback() { 7 | override fun areItemsTheSame(oldItem: FeedPost, newItem: FeedPost): Boolean { 8 | return oldItem.key == newItem.key 9 | } 10 | 11 | override fun areContentsTheSame(oldItem: FeedPost, newItem: FeedPost): Boolean { 12 | return oldItem.key == newItem.key 13 | && oldItem.score == newItem.score 14 | && oldItem.commentCount == newItem.commentCount 15 | } 16 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/paging/usingRemoteSource/database/LocalDatabase.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.paging.usingRemoteSource.database 2 | 3 | import android.content.Context 4 | import androidx.room.Database 5 | import androidx.room.Room 6 | import androidx.room.RoomDatabase 7 | import com.demo.code.paging.usingRemoteSource.database.dao.KeysDao 8 | import com.demo.code.paging.usingRemoteSource.models.PostsKeys 9 | import com.demo.code.paging.usingRemoteSource.models.FeedPost 10 | import com.demo.code.paging.usingRemoteSource.database.dao.PostsDao 11 | 12 | @Database( 13 | entities = [FeedPost::class, PostsKeys::class], 14 | version = 1, 15 | exportSchema = false 16 | ) 17 | abstract class LocalDatabase : RoomDatabase() { 18 | 19 | companion object { 20 | 21 | private const val DATABASE_NAME = "paging_demo.db" 22 | private val LOCAL_DATABASE = LocalDatabase::class.java 23 | 24 | fun create(context: Context): LocalDatabase { 25 | val databaseBuilder = Room.databaseBuilder(context, LOCAL_DATABASE, DATABASE_NAME) 26 | return databaseBuilder.build() 27 | } 28 | } 29 | 30 | abstract fun postsDao(): PostsDao 31 | abstract fun keysDao(): KeysDao 32 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/paging/usingRemoteSource/database/dao/KeysDao.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.paging.usingRemoteSource.database.dao 2 | 3 | import androidx.room.Dao 4 | import androidx.room.Insert 5 | import androidx.room.OnConflictStrategy.REPLACE 6 | import androidx.room.Query 7 | import com.demo.code.paging.usingRemoteSource.models.PostsKeys 8 | 9 | @Dao 10 | interface KeysDao { 11 | 12 | @Insert(onConflict = REPLACE) 13 | suspend fun savePostsKeys(postsKey: PostsKeys) 14 | 15 | @Query("SELECT * FROM postKeys ORDER BY id DESC") 16 | suspend fun getPostsKeys(): List 17 | 18 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/paging/usingRemoteSource/database/dao/PostsDao.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.paging.usingRemoteSource.database.dao 2 | 3 | import androidx.paging.PagingSource 4 | import androidx.room.Dao 5 | import androidx.room.Insert 6 | import androidx.room.OnConflictStrategy.REPLACE 7 | import androidx.room.Query 8 | import com.demo.code.paging.usingRemoteSource.models.FeedPost 9 | 10 | @Dao 11 | interface PostsDao { 12 | 13 | @Insert(onConflict = REPLACE) 14 | suspend fun savePosts(feedPosts: List) 15 | 16 | @Query("SELECT * FROM feedPosts") 17 | fun getPosts(): PagingSource 18 | 19 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/paging/usingRemoteSource/models/FeedPost.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.paging.usingRemoteSource.models 2 | 3 | import androidx.room.Entity 4 | import androidx.room.PrimaryKey 5 | import com.google.gson.annotations.SerializedName 6 | 7 | @Entity(tableName = "feedPosts") 8 | data class FeedPost( 9 | @SerializedName("name") 10 | @PrimaryKey 11 | val key: String, 12 | @SerializedName("title") 13 | val title: String, 14 | @SerializedName("score") 15 | val score: Int, 16 | @SerializedName("author") 17 | val author: String, 18 | @SerializedName("num_comments") 19 | val commentCount: Int 20 | ) 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/paging/usingRemoteSource/models/PostContainer.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.paging.usingRemoteSource.models 2 | 3 | class PostContainer(val data: FeedPost) -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/paging/usingRemoteSource/models/PostsApiResponse.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.paging.usingRemoteSource.models 2 | 3 | import com.demo.code.paging.usingRemoteSource.models.PostsListing 4 | 5 | 6 | class PostsApiResponse(val data: PostsListing) -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/paging/usingRemoteSource/models/PostsKeys.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.paging.usingRemoteSource.models 2 | 3 | import androidx.room.Entity 4 | import androidx.room.PrimaryKey 5 | 6 | @Entity(tableName = "postKeys") 7 | data class PostsKeys( 8 | @PrimaryKey(autoGenerate = true) 9 | val id: Int, 10 | val after: String?, 11 | val before: String? 12 | ) 13 | -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/paging/usingRemoteSource/models/PostsListing.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.paging.usingRemoteSource.models 2 | 3 | class PostsListing(val children: List, val after: String?, val before: String?) -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/paging/usingRemoteSource/networking/ApiClient.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.paging.usingRemoteSource.networking 2 | 3 | import retrofit2.Retrofit 4 | import retrofit2.converter.gson.GsonConverterFactory 5 | 6 | class ApiClient { 7 | 8 | companion object { 9 | 10 | private const val BASE_URL = "https://www.reddit.com/" 11 | private var retrofit: Retrofit? = null 12 | 13 | fun getClient(): Retrofit { 14 | when (retrofit) { 15 | null -> retrofit = Retrofit.Builder() 16 | .baseUrl(BASE_URL) 17 | .addConverterFactory(GsonConverterFactory.create()) 18 | .build() 19 | } 20 | return retrofit as Retrofit 21 | } 22 | 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/paging/usingRemoteSource/networking/RemoteService.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.paging.usingRemoteSource.networking 2 | 3 | import com.demo.code.paging.usingRemoteSource.models.PostsApiResponse 4 | import retrofit2.Response 5 | import retrofit2.http.GET 6 | import retrofit2.http.Query 7 | 8 | interface RemoteService { 9 | 10 | companion object { 11 | val service = RemoteService::class.java 12 | } 13 | 14 | @GET("/r/aww/hot.json") 15 | suspend fun fetchPosts( 16 | @Query("limit") loadSize: Int = 0, 17 | @Query("after") after: String? = null, 18 | @Query("before") before: String? = null 19 | ): Response 20 | 21 | 22 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/paging/usingRemoteSource/placeholder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devrath/DroidAndroidJetpack/bfa825ed41965c57a469eeb67e4b376fc8e2ab98/app/src/main/java/com/demo/code/paging/usingRemoteSource/placeholder -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/paging/usingRemoteSource/repositories/DataMediator.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.paging.usingRemoteSource.repositories 2 | 3 | import androidx.paging.* 4 | import androidx.room.withTransaction 5 | import com.demo.code.paging.usingRemoteSource.database.LocalDatabase 6 | import com.demo.code.paging.usingRemoteSource.models.PostsKeys 7 | import com.demo.code.paging.usingRemoteSource.models.FeedPost 8 | import com.demo.code.paging.usingRemoteSource.networking.RemoteService 9 | import retrofit2.HttpException 10 | import java.io.IOException 11 | 12 | @OptIn(ExperimentalPagingApi::class) 13 | class DataMediator( 14 | private val remoteService: RemoteService, 15 | private val LocalDatabase: LocalDatabase 16 | ) : RemoteMediator() { 17 | 18 | override suspend fun load( 19 | loadType: LoadType, 20 | state: PagingState 21 | ): MediatorResult { 22 | 23 | return try { 24 | 25 | val loadKey = when(loadType){ 26 | LoadType.REFRESH -> null 27 | LoadType.PREPEND -> return MediatorResult.Success(endOfPaginationReached = true) 28 | LoadType.APPEND ->{ 29 | state.lastItemOrNull() 30 | ?: return MediatorResult.Success(endOfPaginationReached = true) 31 | getRedditKeys() 32 | } 33 | } 34 | 35 | val response = remoteService.fetchPosts( 36 | loadSize = state.config.pageSize, 37 | after = loadKey?.after, 38 | before = loadKey?.before 39 | ) 40 | 41 | val listing = response.body()?.data 42 | 43 | val feedPosts = listing?.children?.map { it.data } 44 | 45 | if (feedPosts != null) { 46 | LocalDatabase.withTransaction { 47 | LocalDatabase.keysDao() 48 | .savePostsKeys(PostsKeys(0, listing.after, listing.before)) 49 | LocalDatabase.postsDao().savePosts(feedPosts) 50 | } 51 | 52 | } 53 | MediatorResult.Success(endOfPaginationReached = listing?.after == null) 54 | 55 | } catch (exception: IOException) { 56 | MediatorResult.Error(exception) 57 | } catch (exception: HttpException) { 58 | MediatorResult.Error(exception) 59 | } 60 | 61 | } 62 | 63 | private suspend fun getRedditKeys(): PostsKeys? { 64 | return LocalDatabase.keysDao().getPostsKeys().firstOrNull() 65 | } 66 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/paging/usingRemoteSource/repositories/DataSource.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.paging.usingRemoteSource.repositories 2 | 3 | import androidx.paging.ExperimentalPagingApi 4 | import androidx.paging.PagingSource 5 | import androidx.paging.PagingState 6 | import com.demo.code.paging.usingRemoteSource.models.FeedPost 7 | import com.demo.code.paging.usingRemoteSource.networking.RemoteService 8 | 9 | @OptIn(ExperimentalPagingApi::class) 10 | class DataSource( 11 | private val service: RemoteService, 12 | ) : PagingSource() { 13 | 14 | override suspend fun load(params: LoadParams): LoadResult { 15 | 16 | try { 17 | // Load page 1 if undefined. 18 | val nextPageNumber = params.key ?: 1 19 | val response = service.fetchPosts(nextPageNumber) 20 | val data = response.body()?.data 21 | val feedPosts = response.body()?.data?.children?.map { it.data } 22 | return if (feedPosts != null) { 23 | LoadResult.Page( 24 | data = feedPosts, 25 | prevKey = null, // Only paging forward. 26 | nextKey = nextPageNumber + 1 27 | ) 28 | }else{ 29 | LoadResult.Error(Exception("data is null")) 30 | } 31 | } 32 | catch (e: Exception) { 33 | // Handle errors in this block 34 | return LoadResult.Error(e) 35 | } 36 | } 37 | 38 | override fun getRefreshKey(state: PagingState): Int { 39 | return state.hashCode() 40 | } 41 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/paging/usingRemoteSource/repositories/Repository.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.paging.usingRemoteSource.repositories 2 | 3 | import android.content.Context 4 | import androidx.paging.* 5 | import com.demo.code.paging.usingRemoteSource.database.LocalDatabase 6 | import com.demo.code.paging.usingRemoteSource.models.FeedPost 7 | import com.demo.code.paging.usingRemoteSource.networking.ApiClient 8 | import com.demo.code.paging.usingRemoteSource.networking.RemoteService 9 | import kotlinx.coroutines.flow.Flow 10 | 11 | class Repository(context: Context) { 12 | 13 | // Remote API reference 14 | private val apiService = ApiClient.getClient().create(RemoteService.service) 15 | 16 | /** 17 | * @return Flow of Paging data 18 | */ 19 | @OptIn(ExperimentalPagingApi::class) 20 | fun fetchPosts(): Flow> { 21 | return Pager( 22 | PagingConfig( 23 | pageSize = 30, 24 | enablePlaceholders = false, 25 | prefetchDistance = 1, 26 | initialLoadSize = 30*1, 27 | ) 28 | ){ 29 | DataSource(apiService) 30 | }.flow 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/paging/usingRemoteSource/ui/PagingFromRemoteApiActivity.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.paging.usingRemoteSource.ui 2 | 3 | import android.os.Bundle 4 | import androidx.lifecycle.ViewModelProvider 5 | import androidx.lifecycle.lifecycleScope 6 | import com.demo.code.R 7 | import com.demo.code.base.BaseActivity 8 | import com.demo.code.databinding.ActivityPagingFromRemoteApiBinding 9 | import kotlinx.coroutines.flow.collectLatest 10 | import kotlinx.coroutines.launch 11 | 12 | 13 | class PagingFromRemoteApiActivity : BaseActivity() { 14 | 15 | // Binding: View references 16 | private lateinit var binding: ActivityPagingFromRemoteApiBinding 17 | 18 | // Adapter: List of items 19 | private val adapter = RemoteApiAdapter() 20 | 21 | // viewModel reference 22 | private val remoteApiViewModel: RemoteApiViewModel by lazy { 23 | ViewModelProvider(this).get(RemoteApiViewModel.thisClass) 24 | } 25 | 26 | override fun onCreate(savedInstanceState: Bundle?) { 27 | setTheme(R.style.AppTheme) 28 | super.onCreate(savedInstanceState) 29 | binding = ActivityPagingFromRemoteApiBinding.inflate(layoutInflater) 30 | setContentView(binding.root) 31 | setupViews() 32 | fetchPosts() 33 | } 34 | 35 | private fun fetchPosts() { 36 | lifecycleScope.launch { 37 | 38 | remoteApiViewModel.fetchPosts().collectLatest { pagingData -> 39 | // We get the new data from the flow - We publish the new data to adapter 40 | adapter.submitData(pagingData) 41 | } 42 | } 43 | } 44 | 45 | private fun setupViews() { 46 | binding.apply { 47 | // Adapter: List of items 48 | rvPosts.adapter = adapter 49 | // Adapters: Header and Footer item 50 | rvPosts.adapter = adapter.withLoadStateHeaderAndFooter( 51 | // Header 52 | header = RemoteApiLoadingAdapter { adapter.retry() }, 53 | // Footer 54 | footer = RemoteApiLoadingAdapter { adapter.retry() } 55 | ) 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/paging/usingRemoteSource/ui/RemoteApiAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.paging.usingRemoteSource.ui 2 | 3 | import android.view.LayoutInflater 4 | import android.view.View 5 | import android.view.ViewGroup 6 | import android.widget.TextView 7 | import androidx.paging.PagingDataAdapter 8 | import androidx.recyclerview.widget.RecyclerView 9 | import com.demo.code.R 10 | import com.demo.code.paging.usingRemoteSource.models.FeedPost 11 | import com.demo.code.paging.usingRemoteSource.utils.DiffUtilCallBack 12 | import kotlinx.android.synthetic.main.adapter_row.view.* 13 | 14 | class RemoteApiAdapter : 15 | PagingDataAdapter(DiffUtilCallBack()) { 16 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RedditViewHolder { 17 | val view = LayoutInflater.from(parent.context).inflate(R.layout.adapter_row, parent, false) 18 | return RedditViewHolder(view) 19 | } 20 | 21 | override fun onBindViewHolder(holder: RedditViewHolder, position: Int) { 22 | getItem(position)?.let { holder.bindPost(it) } 23 | } 24 | 25 | class RedditViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { 26 | private val titleText: TextView = itemView.title 27 | 28 | fun bindPost(feedPost: FeedPost) { 29 | with(feedPost) { 30 | titleText.text = title 31 | } 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/paging/usingRemoteSource/ui/RemoteApiLoadingAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.paging.usingRemoteSource.ui 2 | 3 | import android.view.LayoutInflater 4 | import android.view.View 5 | import android.view.ViewGroup 6 | import android.widget.Button 7 | import android.widget.ProgressBar 8 | import android.widget.TextView 9 | import androidx.core.view.isVisible 10 | import androidx.paging.LoadState 11 | import androidx.paging.LoadStateAdapter 12 | import androidx.recyclerview.widget.RecyclerView 13 | import com.demo.code.R 14 | import kotlinx.android.synthetic.main.item_loading_state.view.* 15 | 16 | class RemoteApiLoadingAdapter(private val retry: () -> Unit) : 17 | LoadStateAdapter() { 18 | 19 | override fun onBindViewHolder(holder: LoadingStateViewHolder, loadState: LoadState) { 20 | holder.bindState(loadState) 21 | } 22 | 23 | override fun onCreateViewHolder( 24 | parent: ViewGroup, 25 | loadState: LoadState 26 | ): LoadingStateViewHolder { 27 | val view = LayoutInflater.from(parent.context) 28 | .inflate(R.layout.item_loading_state, parent, false) 29 | return LoadingStateViewHolder(view, retry) 30 | } 31 | 32 | 33 | class LoadingStateViewHolder(itemView: View, retry: () -> Unit) : 34 | RecyclerView.ViewHolder(itemView) { 35 | 36 | private val tvErrorMessage: TextView = itemView.tvErrorMessage 37 | private val progressBar: ProgressBar = itemView.progress_bar 38 | private val btnRetry: Button = itemView.btnRetry 39 | 40 | init { 41 | btnRetry.setOnClickListener { 42 | retry.invoke() 43 | } 44 | } 45 | 46 | 47 | fun bindState(loadState: LoadState) { 48 | if (loadState is LoadState.Error) { 49 | tvErrorMessage.text = loadState.error.localizedMessage 50 | } 51 | progressBar.isVisible = loadState is LoadState.Loading 52 | tvErrorMessage.isVisible = loadState !is LoadState.Loading 53 | btnRetry.isVisible = loadState !is LoadState.Loading 54 | } 55 | 56 | } 57 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/paging/usingRemoteSource/ui/RemoteApiViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.paging.usingRemoteSource.ui 2 | 3 | import android.app.Application 4 | import androidx.lifecycle.AndroidViewModel 5 | import androidx.lifecycle.viewModelScope 6 | import androidx.paging.PagingData 7 | import androidx.paging.cachedIn 8 | import com.demo.code.paging.usingRemoteSource.models.FeedPost 9 | import com.demo.code.paging.usingRemoteSource.repositories.Repository 10 | import kotlinx.coroutines.flow.Flow 11 | 12 | class RemoteApiViewModel(application: Application) : AndroidViewModel(application) { 13 | 14 | companion object { 15 | val thisClass = RemoteApiViewModel::class.java 16 | } 17 | 18 | private val redditRepo = Repository(application) 19 | 20 | fun fetchPosts(): Flow> { 21 | return redditRepo.fetchPosts().cachedIn(viewModelScope) 22 | } 23 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/paging/usingRemoteSource/utils/DiffUtilCallBack.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.paging.usingRemoteSource.utils 2 | 3 | import androidx.recyclerview.widget.DiffUtil 4 | import com.demo.code.paging.usingRemoteSource.models.FeedPost 5 | 6 | class DiffUtilCallBack : DiffUtil.ItemCallback() { 7 | override fun areItemsTheSame(oldItem: FeedPost, newItem: FeedPost): Boolean { 8 | return oldItem.key == newItem.key 9 | } 10 | 11 | override fun areContentsTheSame(oldItem: FeedPost, newItem: FeedPost): Boolean { 12 | return oldItem.key == newItem.key 13 | && oldItem.score == newItem.score 14 | && oldItem.commentCount == newItem.commentCount 15 | } 16 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/utils/Constants.kt: -------------------------------------------------------------------------------- 1 | package com.demo.flow.utils 2 | 3 | object Constants { 4 | var APP_URL = "https://5e510330f2c0d300147c034c.mockapi.io/" 5 | var GENERIC_ERROR_MESSAGE = "Something went wrong" 6 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/utils/placeholder.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.utils 2 | 3 | class placeholder { 4 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/workmanager/WorkManagerActivity.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.workmanager 2 | 3 | import android.os.Bundle 4 | import com.demo.code.base.BaseActivity 5 | import com.demo.code.databinding.ActivityPagingBinding 6 | import com.demo.code.databinding.ActivityWorkManagerBinding 7 | import com.demo.code.workmanager.chainingworker.ChainingWorkerActivity 8 | import com.demo.code.workmanager.exampleone.WorkManagerExampleOneActivity 9 | import com.demo.code.workmanager.simpleworker.SimpleWorkerActivity 10 | import com.demo.extensions.intent.openActivity 11 | 12 | class WorkManagerActivity : BaseActivity() { 13 | 14 | private lateinit var binding: ActivityWorkManagerBinding 15 | 16 | override fun onCreate(savedInstanceState: Bundle?) { 17 | super.onCreate(savedInstanceState) 18 | binding = ActivityWorkManagerBinding.inflate(layoutInflater) 19 | setContentView(binding.root) 20 | 21 | binding.apply { 22 | this.simpleWorkerId.setOnClickListener { 23 | openActivity(SimpleWorkerActivity::class.java) 24 | } 25 | this.chainingWorkerId.setOnClickListener { 26 | openActivity(ChainingWorkerActivity::class.java) 27 | } 28 | this.workManagerId.setOnClickListener { 29 | openActivity(WorkManagerExampleOneActivity::class.java) 30 | } 31 | } 32 | } 33 | 34 | 35 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/workmanager/chainingworker/workers/DownloadWorker.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.workmanager.chainingworker.workers 2 | 3 | import android.content.Context 4 | import androidx.work.Worker 5 | import androidx.work.WorkerParameters 6 | import androidx.work.workDataOf 7 | import java.io.File 8 | import java.io.FileOutputStream 9 | import java.net.HttpURLConnection 10 | import java.net.URL 11 | 12 | class DownloadWorker(context: Context, workerParameters: WorkerParameters) : 13 | Worker(context, workerParameters) { 14 | 15 | override fun doWork(): Result { 16 | val imageUrl = URL("https://raw.githubusercontent.com/devrath/Sample-Data/master/Android-CleanArchitecture-Kotlin/posters/038001.jpg") 17 | val connection = imageUrl.openConnection() as HttpURLConnection 18 | connection.doInput = true 19 | connection.connect() 20 | 21 | val imagePath = "owl_image_${System.currentTimeMillis()}.jpg" 22 | val inputStream = connection.inputStream 23 | val file = File(applicationContext.externalMediaDirs.first(), imagePath) 24 | 25 | val outputStream = FileOutputStream(file) 26 | outputStream.use { output -> 27 | val buffer = ByteArray(4 * 1024) 28 | 29 | var byteCount = inputStream.read(buffer) 30 | 31 | while (byteCount > 0) { 32 | output.write(buffer, 0, byteCount) 33 | 34 | byteCount = inputStream.read(buffer) 35 | } 36 | 37 | output.flush() 38 | } 39 | val output = workDataOf("image_path" to file.absolutePath) 40 | return Result.success(output) 41 | } 42 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/workmanager/chainingworker/workers/FileClearWorker.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.workmanager.chainingworker.workers 2 | 3 | import android.content.Context 4 | import androidx.work.Worker 5 | import androidx.work.WorkerParameters 6 | 7 | class FileClearWorker(context: Context, workerParameters: WorkerParameters) : 8 | Worker(context, workerParameters) { 9 | 10 | override fun doWork(): Result { 11 | val root = applicationContext.externalMediaDirs.first() 12 | 13 | return try { 14 | root.listFiles()?.forEach { child -> 15 | if (child.isDirectory) { 16 | child.deleteRecursively() 17 | } else { 18 | child.delete() 19 | } 20 | } 21 | Result.success() 22 | } catch (error: Throwable) { 23 | error.printStackTrace() 24 | Result.failure() 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/workmanager/exampleone/workers/CleanFilesWorker.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.workmanager.exampleone.workers 2 | 3 | import android.content.Context 4 | import android.util.Log 5 | import androidx.work.Worker 6 | import androidx.work.WorkerParameters 7 | import com.demo.code.workmanager.exampleone.ImageUtils 8 | 9 | private const val LOG_TAG = "CleanFilesWorker" 10 | 11 | class CleanFilesWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) { 12 | 13 | override fun doWork(): Result = try { 14 | // Sleep for debugging purposes 15 | Thread.sleep(3000) 16 | Log.d(LOG_TAG, "Cleaning files!") 17 | 18 | ImageUtils.cleanFiles(applicationContext) 19 | 20 | Log.d(LOG_TAG, "Success!") 21 | Result.success() 22 | } catch (e: Throwable) { 23 | Log.e(LOG_TAG, "Error executing work: ${e.message}", e) 24 | Result.failure() 25 | } 26 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/workmanager/exampleone/workers/CompressWorker.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.workmanager.exampleone.workers 2 | 3 | import android.content.Context 4 | import android.util.Log 5 | import androidx.work.Data 6 | import androidx.work.Worker 7 | import androidx.work.WorkerParameters 8 | import com.demo.code.workmanager.exampleone.ImageUtils 9 | 10 | private const val LOG_TAG = "CompressWorker" 11 | private const val KEY_IMAGE_PATH = "IMAGE_PATH" 12 | private const val KEY_ZIP_PATH = "ZIP_PATH" 13 | 14 | class CompressWorker(context: Context, workerParams: WorkerParameters) : 15 | Worker(context, workerParams) { 16 | 17 | override fun doWork(): Result = try { 18 | // Sleep for debugging purposes 19 | Thread.sleep(3000) 20 | Log.d(LOG_TAG, "Compressing files!") 21 | 22 | val imagePaths = inputData.keyValueMap 23 | .filter { it.key.startsWith(KEY_IMAGE_PATH) } 24 | .map { it.value as String } 25 | 26 | val zipFile = ImageUtils.createZipFile(applicationContext, imagePaths.toTypedArray()) 27 | 28 | val outputData = Data.Builder() 29 | .putString(KEY_ZIP_PATH, zipFile.path) 30 | .build() 31 | 32 | Log.d(LOG_TAG, "Success!") 33 | Result.success(outputData) 34 | } catch (e: Throwable) { 35 | Log.e(LOG_TAG, "Error executing work: " + e.message, e) 36 | Result.failure() 37 | } 38 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/workmanager/exampleone/workers/FilterWorker.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.workmanager.exampleone.workers 2 | 3 | import android.content.Context 4 | import android.net.Uri 5 | import android.provider.MediaStore 6 | import android.util.Log 7 | import androidx.work.Data 8 | import androidx.work.Worker 9 | import androidx.work.WorkerParameters 10 | import com.demo.code.workmanager.exampleone.ImageUtils 11 | 12 | private const val LOG_TAG = "FilterWorker" 13 | const val KEY_IMAGE_URI = "IMAGE_URI" 14 | const val KEY_IMAGE_INDEX = "IMAGE_INDEX" 15 | 16 | private const val IMAGE_PATH_PREFIX = "IMAGE_PATH_" 17 | 18 | class FilterWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) { 19 | 20 | override fun doWork(): Result = try { 21 | // Sleep for debugging purposes 22 | Thread.sleep(3000) 23 | Log.d(LOG_TAG, "Applying filter to image!") 24 | 25 | val imageUriString = inputData.getString(KEY_IMAGE_URI) 26 | val imageIndex = inputData.getInt(KEY_IMAGE_INDEX, 0) 27 | 28 | val bitmap = MediaStore.Images.Media.getBitmap(applicationContext.contentResolver, Uri.parse(imageUriString)) 29 | 30 | val filteredBitmap = ImageUtils.applySepiaFilter(bitmap) 31 | val filteredImageUri = ImageUtils.writeBitmapToFile(applicationContext, filteredBitmap) 32 | 33 | val outputData = 34 | Data.Builder() 35 | .putString(IMAGE_PATH_PREFIX + imageIndex, filteredImageUri.toString()) 36 | .build() 37 | 38 | Log.d(LOG_TAG, "Success!") 39 | Result.success(outputData) 40 | } catch (e: Throwable) { 41 | Log.e(LOG_TAG, "Error executing work: " + e.message, e) 42 | Result.failure() 43 | } 44 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/workmanager/exampleone/workers/UploadWorker.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.workmanager.exampleone.workers 2 | 3 | import android.content.Context 4 | import android.net.Uri 5 | import android.util.Log 6 | import androidx.work.Worker 7 | import androidx.work.WorkerParameters 8 | import com.demo.code.workmanager.exampleone.ImageUtils 9 | 10 | private const val LOG_TAG = "UploadWorker" 11 | private const val KEY_ZIP_PATH = "ZIP_PATH" 12 | 13 | class UploadWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) { 14 | 15 | override fun doWork(): Result = try { 16 | // Sleep for debugging purposes 17 | Thread.sleep(3000) 18 | Log.d(LOG_TAG, "Uploading file!") 19 | 20 | val zipPath = inputData.getString(KEY_ZIP_PATH) 21 | 22 | ImageUtils.uploadFile(Uri.parse(zipPath)) 23 | 24 | Log.d(LOG_TAG, "Success!") 25 | Result.success() 26 | } catch (e: Throwable) { 27 | Log.e(LOG_TAG, "Error executing work: " + e.message, e) 28 | Result.failure() 29 | } 30 | } -------------------------------------------------------------------------------- /app/src/main/java/com/demo/code/workmanager/simpleworker/workers/SimpleDownloadWorker.kt: -------------------------------------------------------------------------------- 1 | package com.demo.code.workmanager.simpleworker.workers 2 | 3 | import android.content.Context 4 | import androidx.work.Worker 5 | import androidx.work.WorkerParameters 6 | import java.io.File 7 | import java.io.FileOutputStream 8 | import java.net.HttpURLConnection 9 | import java.net.URL 10 | 11 | class SimpleDownloadWorker(context: Context, workerParameters: WorkerParameters) : 12 | Worker(context,workerParameters) { 13 | override fun doWork(): Result { 14 | val imageUrl = URL("https://raw.githubusercontent.com/devrath/Sample-Data/master/Android-CleanArchitecture-Kotlin/posters/038001.jpg") 15 | val connection = imageUrl.openConnection() as HttpURLConnection 16 | connection.doInput = true 17 | connection.connect() 18 | 19 | val imagePath = "owl_image.jpg" 20 | val inputStream = connection.inputStream 21 | val file = File(applicationContext.externalMediaDirs.first(), imagePath) 22 | 23 | val outputStream = FileOutputStream(file) 24 | outputStream.use { output -> 25 | val buffer = ByteArray(4 * 1024) 26 | var byteCount = inputStream.read(buffer) 27 | while (byteCount > 0) { 28 | output.write(buffer, 0, byteCount) 29 | byteCount = inputStream.read(buffer) 30 | } 31 | output.flush() 32 | } 33 | 34 | return Result.success() 35 | } 36 | } -------------------------------------------------------------------------------- /app/src/main/res/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devrath/DroidAndroidJetpack/bfa825ed41965c57a469eeb67e4b376fc8e2ab98/app/src/main/res/.DS_Store -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_camera_black_48dp.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_camera_front_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_camera_rear_black_48dp.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_flash_off_black_48dp.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /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/ic_comment.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_flash_on_black_48dp.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_home.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_menu_camera.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_menu_gallery.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_menu_slideshow.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_star.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_vertical_align_bottom_white_24px.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_vertical_align_top_white_24px.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/side_nav_bar.xml: -------------------------------------------------------------------------------- 1 | 3 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_bottom_navigation.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 16 | 17 | 18 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_camera_x.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 18 | 19 | 28 | 29 | 38 | 39 | 47 | 48 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_chain_worker.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_data_store.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_jetpack_selection.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 14 | 15 |