├── app ├── .gitignore ├── src │ ├── main │ │ ├── res │ │ │ ├── values │ │ │ │ ├── strings.xml │ │ │ │ ├── colors.xml │ │ │ │ └── styles.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 │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ ├── ic_launcher.xml │ │ │ │ └── ic_launcher_round.xml │ │ │ ├── layout │ │ │ │ ├── part4_item.xml │ │ │ │ ├── main_activity.xml │ │ │ │ ├── part2_activity.xml │ │ │ │ ├── part3_activity.xml │ │ │ │ ├── part2_item.xml │ │ │ │ ├── part5_activity.xml │ │ │ │ ├── part3_login_dialog.xml │ │ │ │ ├── part5_item.xml │ │ │ │ ├── part4_activity.xml │ │ │ │ └── part1_activity.xml │ │ │ ├── drawable-v24 │ │ │ │ └── ic_launcher_foreground.xml │ │ │ └── drawable │ │ │ │ └── ic_launcher_background.xml │ │ ├── java │ │ │ └── azadev │ │ │ │ └── android │ │ │ │ └── architecture │ │ │ │ ├── feat │ │ │ │ ├── part5 │ │ │ │ │ ├── Part5ListItemData.kt │ │ │ │ │ ├── Part5Activity.kt │ │ │ │ │ ├── Part5ViewModel.kt │ │ │ │ │ └── Part5ListAdapter.kt │ │ │ │ ├── part4 │ │ │ │ │ ├── Part4ItemEntity.kt │ │ │ │ │ ├── Part4Dao.kt │ │ │ │ │ ├── Part4ViewModel.kt │ │ │ │ │ ├── Part4Database.kt │ │ │ │ │ ├── Part4Activity.kt │ │ │ │ │ └── Part4ListAdapter.kt │ │ │ │ ├── part3 │ │ │ │ │ ├── Part3ViewModel.kt │ │ │ │ │ ├── Part3LoginDialogViewModel.kt │ │ │ │ │ ├── Part3Activity.kt │ │ │ │ │ └── Part3LoginDialogFragment.kt │ │ │ │ ├── part2 │ │ │ │ │ ├── Part2ViewModel.kt │ │ │ │ │ ├── Part2Activity.kt │ │ │ │ │ └── Part2ListAdapter.kt │ │ │ │ ├── part1 │ │ │ │ │ ├── Part1Activity.kt │ │ │ │ │ └── Part1ViewModel.kt │ │ │ │ └── main │ │ │ │ │ └── MainActivity.kt │ │ │ │ └── core │ │ │ │ ├── view │ │ │ │ └── constants.kt │ │ │ │ ├── databinding │ │ │ │ ├── adapters │ │ │ │ │ ├── gone.kt │ │ │ │ │ ├── visible.kt │ │ │ │ │ └── invisible.kt │ │ │ │ └── inflaters │ │ │ │ │ └── SetContentView.kt │ │ │ │ ├── utils │ │ │ │ ├── async.kt │ │ │ │ ├── math.kt │ │ │ │ └── random.kt │ │ │ │ └── arch │ │ │ │ ├── livedata │ │ │ │ ├── liveData.kt │ │ │ │ └── SingleLiveEvent.kt │ │ │ │ └── viewmodel │ │ │ │ └── GetViewModel.kt │ │ └── AndroidManifest.xml │ ├── test │ │ └── java │ │ │ └── azadev │ │ │ └── android │ │ │ └── ExampleUnitTest.kt │ └── androidTest │ │ └── java │ │ └── azadev │ │ └── android │ │ └── architecture │ │ └── ExampleInstrumentedTest.kt ├── proguard-rules.pro └── build.gradle ├── settings.gradle ├── etc └── readme-files │ ├── main.png │ ├── part-1.gif │ ├── part-2.gif │ ├── part-3.gif │ ├── part-4.gif │ ├── part-5.gif │ └── app-architecture.png ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .idea ├── codeStyles │ ├── codeStyleConfig.xml │ └── Project.xml ├── vcs.xml ├── inspectionProfiles │ └── Project_Default.xml └── gradle.xml ├── .gitignore ├── gradle.properties ├── LICENSE.txt ├── gradlew.bat ├── gradlew └── README.md /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /etc/readme-files/main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anizoptera/Android-Architecture-Best-Practices/HEAD/etc/readme-files/main.png -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Architecture Best Practices 3 | 4 | -------------------------------------------------------------------------------- /etc/readme-files/part-1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anizoptera/Android-Architecture-Best-Practices/HEAD/etc/readme-files/part-1.gif -------------------------------------------------------------------------------- /etc/readme-files/part-2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anizoptera/Android-Architecture-Best-Practices/HEAD/etc/readme-files/part-2.gif -------------------------------------------------------------------------------- /etc/readme-files/part-3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anizoptera/Android-Architecture-Best-Practices/HEAD/etc/readme-files/part-3.gif -------------------------------------------------------------------------------- /etc/readme-files/part-4.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anizoptera/Android-Architecture-Best-Practices/HEAD/etc/readme-files/part-4.gif -------------------------------------------------------------------------------- /etc/readme-files/part-5.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anizoptera/Android-Architecture-Best-Practices/HEAD/etc/readme-files/part-5.gif -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anizoptera/Android-Architecture-Best-Practices/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /etc/readme-files/app-architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anizoptera/Android-Architecture-Best-Practices/HEAD/etc/readme-files/app-architecture.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anizoptera/Android-Architecture-Best-Practices/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anizoptera/Android-Architecture-Best-Practices/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anizoptera/Android-Architecture-Best-Practices/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anizoptera/Android-Architecture-Best-Practices/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anizoptera/Android-Architecture-Best-Practices/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anizoptera/Android-Architecture-Best-Practices/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anizoptera/Android-Architecture-Best-Practices/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anizoptera/Android-Architecture-Best-Practices/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anizoptera/Android-Architecture-Best-Practices/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anizoptera/Android-Architecture-Best-Practices/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #008577 4 | #00574B 5 | #D81B60 6 | 7 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /app/src/main/java/azadev/android/architecture/feat/part5/Part5ListItemData.kt: -------------------------------------------------------------------------------- 1 | package azadev.android.architecture.feat.part5 2 | 3 | data class Part5ListItemData( 4 | val id: Int, 5 | val name: String, 6 | val isSelected: Boolean = false, 7 | val isLoading: Boolean = false 8 | ) 9 | -------------------------------------------------------------------------------- /app/src/main/java/azadev/android/architecture/core/view/constants.kt: -------------------------------------------------------------------------------- 1 | package azadev.android.architecture.core.view 2 | 3 | import android.view.ViewGroup 4 | 5 | const val MATCH_PARENT = ViewGroup.LayoutParams.MATCH_PARENT 6 | const val WRAP_CONTENT = ViewGroup.LayoutParams.WRAP_CONTENT 7 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/java/azadev/android/architecture/core/databinding/adapters/gone.kt: -------------------------------------------------------------------------------- 1 | package azadev.android.architecture.core.databinding.adapters 2 | 3 | import android.databinding.BindingAdapter 4 | import android.view.View 5 | 6 | @BindingAdapter("app:gone") 7 | fun setGone(view: View, value: Boolean) { 8 | view.visibility = if (value) View.GONE else View.VISIBLE 9 | } 10 | -------------------------------------------------------------------------------- /app/src/main/java/azadev/android/architecture/core/databinding/adapters/visible.kt: -------------------------------------------------------------------------------- 1 | package azadev.android.architecture.core.databinding.adapters 2 | 3 | import android.databinding.BindingAdapter 4 | import android.view.View 5 | 6 | @BindingAdapter("app:visible") 7 | fun setVisible(view: View, value: Boolean) { 8 | view.visibility = if (value) View.VISIBLE else View.GONE 9 | } 10 | -------------------------------------------------------------------------------- /app/src/main/java/azadev/android/architecture/core/databinding/adapters/invisible.kt: -------------------------------------------------------------------------------- 1 | package azadev.android.architecture.core.databinding.adapters 2 | 3 | import android.databinding.BindingAdapter 4 | import android.view.View 5 | 6 | @BindingAdapter("app:invisible") 7 | fun setInvisible(view: View, value: Boolean) { 8 | view.visibility = if (value) View.INVISIBLE else View.VISIBLE 9 | } 10 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/java/azadev/android/architecture/core/utils/async.kt: -------------------------------------------------------------------------------- 1 | package azadev.android.architecture.core.utils 2 | 3 | import android.os.Handler 4 | import android.os.Looper 5 | 6 | val UI_HANDLER = Handler(Looper.getMainLooper()) 7 | val UI_THREAD: Thread = Looper.getMainLooper().thread 8 | 9 | fun uiThread(f: () -> Unit) { 10 | if (Thread.currentThread() !== UI_THREAD) { 11 | UI_HANDLER.post(f) 12 | } 13 | else { 14 | f() 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /app/src/test/java/azadev/android/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package azadev.android 2 | 3 | import org.junit.Assert.assertEquals 4 | import org.junit.Test 5 | 6 | /** 7 | * Example local unit test, which will execute on the development machine (host). 8 | * 9 | * See [testing documentation](http://d.android.com/tools/testing). 10 | */ 11 | class ExampleUnitTest { 12 | @Test 13 | fun addition_isCorrect() { 14 | assertEquals(4, 2 + 2) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/res/layout/part4_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | 16 | 17 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | -------------------------------------------------------------------------------- /app/src/main/java/azadev/android/architecture/feat/part4/Part4ItemEntity.kt: -------------------------------------------------------------------------------- 1 | package azadev.android.architecture.feat.part4 2 | 3 | import android.arch.persistence.room.ColumnInfo 4 | import android.arch.persistence.room.Entity 5 | import android.arch.persistence.room.PrimaryKey 6 | 7 | @Entity(tableName = "item") 8 | data class Part4ItemEntity( 9 | @PrimaryKey(autoGenerate = true) 10 | @ColumnInfo(name = "id") 11 | var id: Int = 0, 12 | 13 | @ColumnInfo(name = "title") 14 | var title: String = "" 15 | ) 16 | -------------------------------------------------------------------------------- /app/src/main/java/azadev/android/architecture/core/utils/math.kt: -------------------------------------------------------------------------------- 1 | package azadev.android.architecture.core.utils 2 | 3 | private val BASE62 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray() 4 | 5 | fun Byte.toBase(base: Int) = (this.toInt() and 255).toBase(base) 6 | fun Int.toBase(base: Int) = toLong().toBase(base) 7 | fun Long.toBase(base: Int): String { 8 | val sb = StringBuilder() 9 | var value = this 10 | 11 | do { 12 | sb.insert(0, BASE62[(value % base).toInt()]) 13 | value /= base 14 | } 15 | while (value > 0L) 16 | 17 | return sb.toString() 18 | } 19 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/java/azadev/android/architecture/feat/part4/Part4Dao.kt: -------------------------------------------------------------------------------- 1 | package azadev.android.architecture.feat.part4 2 | 3 | import android.arch.lifecycle.LiveData 4 | import android.arch.persistence.room.Dao 5 | import android.arch.persistence.room.Insert 6 | import android.arch.persistence.room.Query 7 | 8 | @Dao 9 | interface Part4ItemDao { 10 | @Query("SELECT * FROM item ORDER BY id ASC") 11 | fun getAll(): LiveData> 12 | 13 | @Query("SELECT * FROM item WHERE ([id] % 2) <> 0 ORDER BY id ASC") 14 | fun getOdd(): LiveData> 15 | 16 | @Insert 17 | fun insert(itemEntity: Part4ItemEntity) 18 | 19 | @Query("DELETE FROM item") 20 | fun deleteAll() 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/azadev/android/architecture/feat/part3/Part3ViewModel.kt: -------------------------------------------------------------------------------- 1 | package azadev.android.architecture.feat.part3 2 | 3 | import android.arch.lifecycle.ViewModel 4 | import azadev.android.architecture.core.arch.livedata.mutableLiveData 5 | 6 | class Part3ViewModel : ViewModel() { 7 | val isLoginDialogOpen = mutableLiveData(false) 8 | 9 | val username = mutableLiveData(null) 10 | 11 | fun handleLoginButtonClick() { 12 | isLoginDialogOpen.value = true 13 | } 14 | 15 | fun handleSuccessfulLogin(username: String) { 16 | this.username.value = username 17 | isLoginDialogOpen.value = false 18 | } 19 | 20 | fun handleCancelledLogin() { 21 | isLoginDialogOpen.value = false 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/azadev/android/architecture/feat/part2/Part2ViewModel.kt: -------------------------------------------------------------------------------- 1 | package azadev.android.architecture.feat.part2 2 | 3 | import android.arch.lifecycle.ViewModel 4 | import azadev.android.architecture.core.arch.livedata.mutableLiveData 5 | 6 | private const val CHUNK_SIZE = 5 7 | 8 | class Part2ViewModel : ViewModel() { 9 | private var multiplier = -1 10 | 11 | val items = mutableLiveData(generateItems()) 12 | 13 | fun handleAddClick() { 14 | items.value = items.value!! + generateItems() 15 | } 16 | 17 | fun handleRemoveClick(id: Int) { 18 | items.value = items.value!!.filter { it != id } 19 | } 20 | 21 | private fun generateItems(): List { 22 | multiplier++ 23 | return (1..CHUNK_SIZE).map { CHUNK_SIZE * multiplier + it } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # IntelliJ 2 | # https://www.jetbrains.com/help/idea/2016.1/synchronizing-and-sharing-settings.html#d1526026e189 3 | /.idea/caches/ 4 | /.idea/dictionaries/ 5 | /.idea/libraries/ 6 | /projectFilesBackup*/ 7 | *.iml 8 | 9 | # User-specific settings 10 | /.idea/runConfigurations.xml 11 | /.idea/tasks.xml 12 | /.idea/workspace.xml 13 | 14 | # Environment-specific settings 15 | /.idea/misc.xml 16 | /.idea/modules.xml 17 | 18 | # May contain sensitive data 19 | /.idea/dataSources.ids 20 | /.idea/datasources.xml 21 | 22 | 23 | # Local properties 24 | local.properties 25 | signing.properties 26 | 27 | 28 | # Build/Work flow 29 | build/ 30 | gen/ 31 | out/ 32 | /sources/ 33 | /captures/ 34 | 35 | 36 | # Other 37 | .gradle/ 38 | .DS_Store 39 | .externalNativeBuild 40 | -------------------------------------------------------------------------------- /app/src/androidTest/java/azadev/android/architecture/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package azadev.android.architecture 2 | 3 | import android.support.test.InstrumentationRegistry 4 | import android.support.test.runner.AndroidJUnit4 5 | import org.junit.Assert.assertEquals 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | /** 10 | * Instrumented test, which will execute on an Android device. 11 | * 12 | * See [testing documentation](http://d.android.com/tools/testing). 13 | */ 14 | @RunWith(AndroidJUnit4::class) 15 | class ExampleInstrumentedTest { 16 | @Test 17 | fun useAppContext() { 18 | // Context of the app under test. 19 | val appContext = InstrumentationRegistry.getTargetContext() 20 | assertEquals("azadev.archtest", appContext.packageName) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx1536m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/layout/main_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 13 |