├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle.kts ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── mi │ │ └── best_practice │ │ ├── App.kt │ │ └── koin │ │ └── AppComponent.kt │ └── res │ ├── drawable-v24 │ └── ic_launcher_foreground.xml │ ├── drawable │ └── ic_launcher_background.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 │ └── values │ ├── colors.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle.kts ├── buildSrc ├── build.gradle.kts └── src │ └── main │ └── kotlin │ ├── CommonDependency.kt │ ├── base_plugin │ ├── BaseGradlePlugin.kt │ └── PluginExtensions.kt │ └── constants │ ├── AndroidConfig.kt │ ├── GradleDependency.kt │ ├── LibraryDependency.kt │ ├── ModulesDependency.kt │ └── TestLibraryDependency.kt ├── config └── detekt.yml ├── dashboard ├── .gitignore ├── build.gradle.kts ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── mi │ │ └── dashboard │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── mi │ │ │ └── dashboard │ │ │ ├── DashboardViewModel.kt │ │ │ ├── FirstFragment.kt │ │ │ ├── MainActivity.kt │ │ │ ├── SecondFragment.kt │ │ │ └── koin │ │ │ └── FeatureDashboardModule.kt │ └── res │ │ ├── layout │ │ ├── activity_dashboard.xml │ │ ├── content_main.xml │ │ ├── fragment_first.xml │ │ └── fragment_second.xml │ │ ├── menu │ │ └── menu_main.xml │ │ ├── navigation │ │ └── nav_graph.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── mi │ └── dashboard │ └── ExampleUnitTest.kt ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── local.properties ├── navigation ├── .gitignore ├── build.gradle.kts ├── consumer-rules.pro ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── mi │ └── navigation │ ├── ExtraConstant.kt │ └── Navigation.kt └── settings.gradle.kts /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Android kotlin best practice include : 2 | Modularization 3 | Gradle Depedency managment 4 | Gradle rewritten in Kotlin DSL 5 | Custom Plugin(dependencies with no duplication) 6 | Static Code Analytics (KTLint-Detekt) 7 | KOIN service locator instead of Dagger (dependency injection) 8 | and more... 9 | 10 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id(GradlePluginId.ANDROID_APP) 3 | id(GradlePluginId.BASE_GRADLE_PLUGIN) 4 | } 5 | 6 | dependencies { 7 | implementation(project(ModulesDependency.NAVIGATION)) 8 | implementation(project(ModulesDependency.DASHBOARD)) 9 | } 10 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle.kts.kts. 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/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/mi/best_practice/App.kt: -------------------------------------------------------------------------------- 1 | package com.mi.best_practice 2 | 3 | import android.app.Application 4 | import com.mi.best_practice.koin.appModules 5 | import org.koin.android.ext.koin.androidContext 6 | import org.koin.android.ext.koin.androidLogger 7 | import org.koin.core.context.startKoin 8 | 9 | class App : Application() { 10 | 11 | override fun onCreate() { 12 | super.onCreate() 13 | configureDi() 14 | } 15 | 16 | private fun configureDi() { 17 | startKoin { 18 | androidLogger() 19 | androidContext(this@App) 20 | modules(appModules) 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/com/mi/best_practice/koin/AppComponent.kt: -------------------------------------------------------------------------------- 1 | package com.mi.best_practice.koin 2 | 3 | import com.mi.dashboard.koin.featureDashboardModule 4 | 5 | val appModules = listOf(featureDashboardModule) 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_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MoIbrahim15/Android-Kotlin-Modularization-MVI/102e64cc3c5b961b649c6c60e1c0b3f2c8e7b4c3/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MoIbrahim15/Android-Kotlin-Modularization-MVI/102e64cc3c5b961b649c6c60e1c0b3f2c8e7b4c3/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MoIbrahim15/Android-Kotlin-Modularization-MVI/102e64cc3c5b961b649c6c60e1c0b3f2c8e7b4c3/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MoIbrahim15/Android-Kotlin-Modularization-MVI/102e64cc3c5b961b649c6c60e1c0b3f2c8e7b4c3/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MoIbrahim15/Android-Kotlin-Modularization-MVI/102e64cc3c5b961b649c6c60e1c0b3f2c8e7b4c3/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MoIbrahim15/Android-Kotlin-Modularization-MVI/102e64cc3c5b961b649c6c60e1c0b3f2c8e7b4c3/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MoIbrahim15/Android-Kotlin-Modularization-MVI/102e64cc3c5b961b649c6c60e1c0b3f2c8e7b4c3/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MoIbrahim15/Android-Kotlin-Modularization-MVI/102e64cc3c5b961b649c6c60e1c0b3f2c8e7b4c3/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MoIbrahim15/Android-Kotlin-Modularization-MVI/102e64cc3c5b961b649c6c60e1c0b3f2c8e7b4c3/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MoIbrahim15/Android-Kotlin-Modularization-MVI/102e64cc3c5b961b649c6c60e1c0b3f2c8e7b4c3/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #6200EE 4 | #3700B3 5 | #03DAC5 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Best Practice 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jlleitschuh.gradle.ktlint.reporter.ReporterType 2 | 3 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 4 | plugins { 5 | id(GradlePluginId.KTLINT_GRADLE) version CoreVersion.KTLINT_GRADLE 6 | id(GradlePluginId.DETEKT) version CoreVersion.DETEKT 7 | } 8 | 9 | buildscript { 10 | repositories { 11 | // Android plugin & support libraries 12 | google() 13 | 14 | // Main open-source repository 15 | jcenter() 16 | 17 | // Ktlint Gradle 18 | maven(GradlePluginId.KTLINT_MAVEN) 19 | } 20 | dependencies { 21 | classpath(GradleClasspath.ANDROID_GRADLE) 22 | classpath(kotlin(GradleClasspath.KOTLIN_PlUGIN, version = CoreVersion.KOTLIN)) 23 | classpath(GradleClasspath.SAFE_ARGS) 24 | classpath(GradleClasspath.KTLINT_CLASSPATH) 25 | } 26 | } 27 | 28 | allprojects { 29 | repositories { 30 | google() 31 | jcenter() 32 | } 33 | 34 | // We want to apply ktlint at all project level because it also checks build gradle files 35 | plugins.apply(GradlePluginId.KTLINT_GRADLE) 36 | // Ktlint configuration for sub-projects 37 | ktlint { 38 | version.set(CoreVersion.KTLINT) 39 | verbose.set(true) 40 | android.set(true) 41 | reporters { 42 | reporter(ReporterType.CHECKSTYLE) 43 | } 44 | ignoreFailures.set(true) 45 | filter { 46 | exclude("**/generated/**") 47 | } 48 | } 49 | 50 | plugins.apply(GradlePluginId.DETEKT) 51 | 52 | detekt { 53 | config = files("${project.rootDir}/config/detekt.yml") 54 | parallel = true 55 | } 56 | } 57 | 58 | tasks.register("clean", Delete::class) { 59 | delete(rootProject.buildDir) 60 | } 61 | -------------------------------------------------------------------------------- /buildSrc/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | `kotlin-dsl` 3 | } 4 | 5 | //The kotlin-dsl plugin requires a repository to be declared 6 | repositories { 7 | mavenCentral() 8 | google() 9 | jcenter() 10 | } 11 | 12 | gradlePlugin { 13 | plugins { 14 | register("base-gradle-plugin") { 15 | id = "base-gradle-plugin" 16 | implementationClass = "base_plugin.BaseGradlePlugin" 17 | } 18 | } 19 | } 20 | 21 | 22 | dependencies { 23 | /* Depend on the android gradle plugin, since we want to access it in our plugin */ 24 | implementation("com.android.tools.build:gradle:3.5.3") 25 | /* Depend on the kotlin plugin, since we want to access it in our plugin */ 26 | implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.61") 27 | } 28 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/CommonDependency.kt: -------------------------------------------------------------------------------- 1 | import org.gradle.api.artifacts.Dependency 2 | import org.gradle.api.artifacts.dsl.DependencyHandler 3 | 4 | /* 5 | Define common dependencies, so they can be easily updated across feature modules 6 | */ 7 | fun DependencyHandler.addTestDependencies() { 8 | testImplementation(TestLibraryDependency.JUNIT) 9 | testImplementation(TestLibraryDependency.KOIN) 10 | androidTestImplementation(TestLibraryDependency.JUNIT_ANDROID) 11 | androidTestImplementation(TestLibraryDependency.ESPRESSO) 12 | } 13 | 14 | /* 15 | * These extensions mimic the extensions that are generated on the fly by Gradle. 16 | * They are used here to provide above dependency syntax that mimics Gradle Kotlin DSL syntax in module\build.gradle.kts files. 17 | */ 18 | fun DependencyHandler.implementation(dependencyNotation: Any): Dependency? = 19 | add("implementation", dependencyNotation) 20 | 21 | private fun DependencyHandler.api(dependencyNotation: Any): Dependency? = 22 | add("api", dependencyNotation) 23 | 24 | private fun DependencyHandler.kapt(dependencyNotation: Any): Dependency? = 25 | add("kapt", dependencyNotation) 26 | 27 | private fun DependencyHandler.testImplementation(dependencyNotation: Any): Dependency? = 28 | add("testImplementation", dependencyNotation) 29 | 30 | private fun DependencyHandler.androidTestImplementation(dependencyNotation: Any): Dependency? = 31 | add("androidTestImplementation", dependencyNotation) 32 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/base_plugin/BaseGradlePlugin.kt: -------------------------------------------------------------------------------- 1 | package base_plugin 2 | 3 | import org.gradle.api.Plugin 4 | import org.gradle.api.Project 5 | 6 | open class BaseGradlePlugin : Plugin { 7 | override fun apply(project: Project) { 8 | project.configureDefaultPlugins() 9 | project.configureAndroidApp() 10 | project.configureDependencies() 11 | } 12 | } -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/base_plugin/PluginExtensions.kt: -------------------------------------------------------------------------------- 1 | package base_plugin 2 | import com.android.build.gradle.BaseExtension 3 | import org.gradle.api.Project 4 | import org.gradle.api.JavaVersion 5 | import org.gradle.kotlin.dsl.getByType 6 | import org.gradle.kotlin.dsl.dependencies 7 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompile 8 | 9 | internal fun Project.configureDefaultPlugins() { 10 | plugins.apply(GradlePluginId.ANDROID) 11 | plugins.apply(GradlePluginId.ANDROID_EXT) 12 | } 13 | 14 | private typealias AndroidBaseExtension = BaseExtension 15 | 16 | internal fun Project.configureAndroidApp() = this.extensions.getByType().run { 17 | compileSdkVersion(AndroidConfig.COMPILE_SDK_VERSION) 18 | defaultConfig { 19 | minSdkVersion(AndroidConfig.MIN_SDK_VERSION) 20 | targetSdkVersion(AndroidConfig.TARGET_SDK_VERSION) 21 | versionCode = AndroidConfig.VERSION_CODE 22 | versionName = AndroidConfig.VERSION_NAME 23 | testInstrumentationRunner = AndroidConfig.TEST_INSTRUMENTATION_RUNNER 24 | } 25 | buildTypes { 26 | getByName(BuildType.DEBUG) { 27 | isTestCoverageEnabled = true 28 | } 29 | getByName(BuildType.RELEASE) { 30 | isMinifyEnabled = false 31 | proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro") 32 | } 33 | } 34 | 35 | compileOptions { 36 | sourceCompatibility = JavaVersion.VERSION_1_8 37 | targetCompatibility = JavaVersion.VERSION_1_8 38 | } 39 | 40 | project.tasks.withType(KotlinCompile::class.java).configureEach { 41 | kotlinOptions { 42 | jvmTarget = "1.8" 43 | } 44 | } 45 | } 46 | 47 | internal fun Project.configureDependencies() = this.dependencies { 48 | add("implementation", LibraryDependency.KOTLIN_STD) 49 | //koin - di 50 | add("implementation", LibraryDependency.KOIN) 51 | add("implementation", LibraryDependency.KOIN_VIEWMODEL) 52 | add("implementation", LibraryDependency.KOIN_SCOPE) 53 | } 54 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/constants/AndroidConfig.kt: -------------------------------------------------------------------------------- 1 | object AndroidConfig { 2 | 3 | const val COMPILE_SDK_VERSION = 29 4 | const val MIN_SDK_VERSION = 21 5 | const val TARGET_SDK_VERSION = 29 6 | const val VERSION_CODE = 1 7 | const val VERSION_NAME = "1" 8 | 9 | const val ID = "com.mi.best_practice" 10 | const val TEST_INSTRUMENTATION_RUNNER = "androidx.test.runner.AndroidJUnitRunner" 11 | } 12 | 13 | 14 | interface BuildType { 15 | 16 | companion object { 17 | const val RELEASE = "release" 18 | const val DEBUG = "release" 19 | } 20 | 21 | val isMinifyEnabled: Boolean 22 | } 23 | 24 | object BuildTypeDebug : BuildType { 25 | override val isMinifyEnabled = false 26 | } 27 | 28 | object BuildTypeRelease : BuildType { 29 | override val isMinifyEnabled = true 30 | } -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/constants/GradleDependency.kt: -------------------------------------------------------------------------------- 1 | object CoreVersion{ 2 | const val KOTLIN = "1.3.61" 3 | const val NAVIGATION = "2.2.0" 4 | const val ANDROID_GRADLE = "3.5.3" 5 | const val KTLINT_GRADLE = "9.2.1" 6 | const val KTLINT = "0.34.2" 7 | const val DETEKT = "1.0.0" 8 | } 9 | 10 | object GradlePluginId { 11 | const val ANDROID_APP = "com.android.application" 12 | const val ANDROID_LIB = "com.android.library" 13 | const val ANDROID = "kotlin-android" 14 | const val ANDROID_EXT = "kotlin-android-extensions" 15 | const val SAFE_ARGS = "androidx.navigation.safeargs" 16 | const val BASE_GRADLE_PLUGIN = "base-gradle-plugin" 17 | const val KTLINT_GRADLE = "org.jlleitschuh.gradle.ktlint" 18 | const val KTLINT_MAVEN = "https://plugins.gradle.org/m2/" 19 | const val DETEKT = "io.gitlab.arturbosch.detekt" 20 | } 21 | 22 | object GradleClasspath { 23 | const val KOTLIN_PlUGIN = "gradle-plugin" 24 | const val ANDROID_GRADLE = "com.android.tools.build:gradle:${CoreVersion.ANDROID_GRADLE}" 25 | const val SAFE_ARGS = "androidx.navigation:navigation-safe-args-gradle-plugin:${CoreVersion.NAVIGATION}" 26 | const val KTLINT_CLASSPATH ="org.jlleitschuh.gradle:ktlint-gradle:${CoreVersion.KTLINT_GRADLE}" 27 | } 28 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/constants/LibraryDependency.kt: -------------------------------------------------------------------------------- 1 | object LibraryDependency { 2 | object Version { 3 | const val SUPPORT_LIB = "1.1.0" 4 | const val CONSTRAINT = "1.1.3" 5 | const val KOIN = "2.0.1" 6 | } 7 | 8 | const val KOTLIN_STD = "org.jetbrains.kotlin:kotlin-stdlib:${CoreVersion.KOTLIN}" 9 | const val CORE = "androidx.core:core-ktx:${Version.SUPPORT_LIB}" 10 | const val APPCOMPAT = "androidx.appcompat:appcompat:${Version.SUPPORT_LIB}" 11 | const val MATERIAL = "com.google.android.material:material:${Version.SUPPORT_LIB}" 12 | const val CONSTRAINT = "androidx.constraintlayout:constraintlayout:${Version.CONSTRAINT}" 13 | const val NAVIGATION_FRAGMENT = "androidx.navigation:navigation-fragment-ktx:${CoreVersion.NAVIGATION}" 14 | const val NAVIGATION_UI = "androidx.navigation:navigation-ui-ktx:${CoreVersion.NAVIGATION}" 15 | const val KOIN = "org.koin:koin-android:${Version.KOIN}" 16 | const val KOIN_VIEWMODEL = "org.koin:koin-androidx-viewmodel:${Version.KOIN}" 17 | const val KOIN_SCOPE = "org.koin:koin-androidx-scope:${Version.KOIN}" 18 | } -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/constants/ModulesDependency.kt: -------------------------------------------------------------------------------- 1 | object ModulesDependency { 2 | const val APP = ":app" 3 | const val NAVIGATION = ":navigation" 4 | const val DASHBOARD = ":dashboard" 5 | } -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/constants/TestLibraryDependency.kt: -------------------------------------------------------------------------------- 1 | object TestLibraryDependency{ 2 | object Version{ 3 | const val JUNIT = "4.13" 4 | const val JUNIT_ANDROID = "1.1.1" 5 | const val ESPRESSO = "3.2.0" 6 | } 7 | 8 | const val JUNIT = "junit:junit:${Version.JUNIT}" 9 | const val JUNIT_ANDROID = "androidx.test.ext:junit:${Version.JUNIT_ANDROID}" 10 | const val ESPRESSO = "androidx.test.espresso:espresso-core:${Version.ESPRESSO}" 11 | const val KOIN ="org.koin:koin-test:${LibraryDependency.Version.KOIN}" 12 | } -------------------------------------------------------------------------------- /config/detekt.yml: -------------------------------------------------------------------------------- 1 | build: 2 | maxIssues: 0 3 | excludeCorrectable: false 4 | weights: 5 | # complexity: 2 6 | # LongParameterList: 1 7 | # style: 1 8 | # comments: 1 9 | 10 | config: 11 | validation: true 12 | # when writing own rules with new properties, exclude the property path e.g.: "my_rule_set,.*>.*>[my_property]" 13 | excludes: "" 14 | 15 | processors: 16 | active: true 17 | exclude: 18 | - 'DetektProgressListener' 19 | # - 'FunctionCountProcessor' 20 | # - 'PropertyCountProcessor' 21 | # - 'ClassCountProcessor' 22 | # - 'PackageCountProcessor' 23 | # - 'KtFileCountProcessor' 24 | 25 | console-reports: 26 | active: true 27 | exclude: 28 | - 'ProjectStatisticsReport' 29 | - 'ComplexityReport' 30 | - 'NotificationReport' 31 | # - 'FindingsReport' 32 | - 'FileBasedFindingsReport' 33 | 34 | comments: 35 | active: true 36 | excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" 37 | CommentOverPrivateFunction: 38 | active: false 39 | CommentOverPrivateProperty: 40 | active: false 41 | EndOfSentenceFormat: 42 | active: false 43 | endOfSentenceFormat: ([.?!][ \t\n\r\f<])|([.?!:]$) 44 | UndocumentedPublicClass: 45 | active: false 46 | searchInNestedClass: true 47 | searchInInnerClass: true 48 | searchInInnerObject: true 49 | searchInInnerInterface: true 50 | UndocumentedPublicFunction: 51 | active: false 52 | UndocumentedPublicProperty: 53 | active: false 54 | 55 | complexity: 56 | active: true 57 | ComplexCondition: 58 | active: true 59 | threshold: 4 60 | ComplexInterface: 61 | active: false 62 | threshold: 10 63 | includeStaticDeclarations: false 64 | ComplexMethod: 65 | active: true 66 | threshold: 15 67 | ignoreSingleWhenExpression: false 68 | ignoreSimpleWhenEntries: false 69 | ignoreNestingFunctions: false 70 | nestingFunctions: run,let,apply,with,also,use,forEach,isNotNull,ifNull 71 | LabeledExpression: 72 | active: false 73 | ignoredLabels: "" 74 | LargeClass: 75 | active: true 76 | threshold: 600 77 | LongMethod: 78 | active: true 79 | threshold: 60 80 | LongParameterList: 81 | active: true 82 | threshold: 6 83 | ignoreDefaultParameters: false 84 | MethodOverloading: 85 | active: false 86 | threshold: 6 87 | NestedBlockDepth: 88 | active: true 89 | threshold: 4 90 | StringLiteralDuplication: 91 | active: false 92 | excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" 93 | threshold: 3 94 | ignoreAnnotation: true 95 | excludeStringsWithLessThan5Characters: true 96 | ignoreStringsRegex: '$^' 97 | TooManyFunctions: 98 | active: true 99 | excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" 100 | thresholdInFiles: 11 101 | thresholdInClasses: 11 102 | thresholdInInterfaces: 11 103 | thresholdInObjects: 11 104 | thresholdInEnums: 11 105 | ignoreDeprecated: false 106 | ignorePrivate: false 107 | ignoreOverridden: false 108 | 109 | coroutines: 110 | active: true 111 | GlobalCoroutineUsage: 112 | active: false 113 | RedundantSuspendModifier: 114 | active: false 115 | 116 | empty-blocks: 117 | active: true 118 | EmptyCatchBlock: 119 | active: true 120 | allowedExceptionNameRegex: "^(_|(ignore|expected).*)" 121 | EmptyClassBlock: 122 | active: true 123 | EmptyDefaultConstructor: 124 | active: true 125 | EmptyDoWhileBlock: 126 | active: true 127 | EmptyElseBlock: 128 | active: true 129 | EmptyFinallyBlock: 130 | active: true 131 | EmptyForBlock: 132 | active: true 133 | EmptyFunctionBlock: 134 | active: true 135 | ignoreOverridden: false 136 | EmptyIfBlock: 137 | active: true 138 | EmptyInitBlock: 139 | active: true 140 | EmptyKtFile: 141 | active: true 142 | EmptySecondaryConstructor: 143 | active: true 144 | EmptyWhenBlock: 145 | active: true 146 | EmptyWhileBlock: 147 | active: true 148 | 149 | exceptions: 150 | active: true 151 | ExceptionRaisedInUnexpectedLocation: 152 | active: false 153 | methodNames: 'toString,hashCode,equals,finalize' 154 | InstanceOfCheckForException: 155 | active: false 156 | excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" 157 | NotImplementedDeclaration: 158 | active: false 159 | PrintStackTrace: 160 | active: false 161 | RethrowCaughtException: 162 | active: false 163 | ReturnFromFinally: 164 | active: false 165 | ignoreLabeled: false 166 | SwallowedException: 167 | active: false 168 | ignoredExceptionTypes: 'InterruptedException,NumberFormatException,ParseException,MalformedURLException' 169 | allowedExceptionNameRegex: "^(_|(ignore|expected).*)" 170 | ThrowingExceptionFromFinally: 171 | active: false 172 | ThrowingExceptionInMain: 173 | active: false 174 | ThrowingExceptionsWithoutMessageOrCause: 175 | active: false 176 | exceptions: 'IllegalArgumentException,IllegalStateException,IOException' 177 | ThrowingNewInstanceOfSameException: 178 | active: false 179 | TooGenericExceptionCaught: 180 | active: true 181 | excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" 182 | exceptionNames: 183 | - ArrayIndexOutOfBoundsException 184 | - Error 185 | - Exception 186 | - IllegalMonitorStateException 187 | - NullPointerException 188 | - IndexOutOfBoundsException 189 | - RuntimeException 190 | - Throwable 191 | allowedExceptionNameRegex: "^(_|(ignore|expected).*)" 192 | TooGenericExceptionThrown: 193 | active: true 194 | exceptionNames: 195 | - Error 196 | - Exception 197 | - Throwable 198 | - RuntimeException 199 | 200 | formatting: 201 | active: true 202 | android: false 203 | autoCorrect: true 204 | AnnotationOnSeparateLine: 205 | active: false 206 | autoCorrect: true 207 | ChainWrapping: 208 | active: true 209 | autoCorrect: true 210 | CommentSpacing: 211 | active: true 212 | autoCorrect: true 213 | EnumEntryNameCase: 214 | active: false 215 | autoCorrect: true 216 | Filename: 217 | active: true 218 | FinalNewline: 219 | active: true 220 | autoCorrect: true 221 | ImportOrdering: 222 | active: false 223 | autoCorrect: true 224 | Indentation: 225 | active: false 226 | autoCorrect: true 227 | indentSize: 4 228 | continuationIndentSize: 4 229 | MaximumLineLength: 230 | active: true 231 | maxLineLength: 120 232 | ModifierOrdering: 233 | active: true 234 | autoCorrect: true 235 | MultiLineIfElse: 236 | active: true 237 | autoCorrect: true 238 | NoBlankLineBeforeRbrace: 239 | active: true 240 | autoCorrect: true 241 | NoConsecutiveBlankLines: 242 | active: true 243 | autoCorrect: true 244 | NoEmptyClassBody: 245 | active: true 246 | autoCorrect: true 247 | NoEmptyFirstLineInMethodBlock: 248 | active: false 249 | autoCorrect: true 250 | NoLineBreakAfterElse: 251 | active: true 252 | autoCorrect: true 253 | NoLineBreakBeforeAssignment: 254 | active: true 255 | autoCorrect: true 256 | NoMultipleSpaces: 257 | active: true 258 | autoCorrect: true 259 | NoSemicolons: 260 | active: true 261 | autoCorrect: true 262 | NoTrailingSpaces: 263 | active: true 264 | autoCorrect: true 265 | NoUnitReturn: 266 | active: true 267 | autoCorrect: true 268 | NoUnusedImports: 269 | active: true 270 | autoCorrect: true 271 | NoWildcardImports: 272 | active: true 273 | PackageName: 274 | active: true 275 | autoCorrect: true 276 | ParameterListWrapping: 277 | active: true 278 | autoCorrect: true 279 | indentSize: 4 280 | SpacingAroundColon: 281 | active: true 282 | autoCorrect: true 283 | SpacingAroundComma: 284 | active: true 285 | autoCorrect: true 286 | SpacingAroundCurly: 287 | active: true 288 | autoCorrect: true 289 | SpacingAroundDot: 290 | active: true 291 | autoCorrect: true 292 | SpacingAroundKeyword: 293 | active: true 294 | autoCorrect: true 295 | SpacingAroundOperators: 296 | active: true 297 | autoCorrect: true 298 | SpacingAroundParens: 299 | active: true 300 | autoCorrect: true 301 | SpacingAroundRangeOperator: 302 | active: true 303 | autoCorrect: true 304 | StringTemplate: 305 | active: true 306 | autoCorrect: true 307 | 308 | naming: 309 | active: true 310 | ClassNaming: 311 | active: true 312 | excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" 313 | classPattern: '[A-Z$][a-zA-Z0-9$]*' 314 | ConstructorParameterNaming: 315 | active: true 316 | excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" 317 | parameterPattern: '[a-z][A-Za-z0-9]*' 318 | privateParameterPattern: '[a-z][A-Za-z0-9]*' 319 | excludeClassPattern: '$^' 320 | ignoreOverridden: true 321 | EnumNaming: 322 | active: true 323 | excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" 324 | enumEntryPattern: '^[A-Z][_a-zA-Z0-9]*' 325 | ForbiddenClassName: 326 | active: false 327 | excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" 328 | forbiddenName: '' 329 | FunctionMaxLength: 330 | active: false 331 | excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" 332 | maximumFunctionNameLength: 30 333 | FunctionMinLength: 334 | active: false 335 | excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" 336 | minimumFunctionNameLength: 3 337 | FunctionNaming: 338 | active: true 339 | excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" 340 | functionPattern: '^([a-z$][a-zA-Z$0-9]*)|(`.*`)$' 341 | excludeClassPattern: '$^' 342 | ignoreOverridden: true 343 | FunctionParameterNaming: 344 | active: true 345 | excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" 346 | parameterPattern: '[a-z][A-Za-z0-9]*' 347 | excludeClassPattern: '$^' 348 | ignoreOverridden: true 349 | InvalidPackageDeclaration: 350 | active: false 351 | rootPackage: '' 352 | MatchingDeclarationName: 353 | active: true 354 | MemberNameEqualsClassName: 355 | active: true 356 | ignoreOverridden: true 357 | ObjectPropertyNaming: 358 | active: true 359 | excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" 360 | constantPattern: '[A-Za-z][_A-Za-z0-9]*' 361 | propertyPattern: '[A-Za-z][_A-Za-z0-9]*' 362 | privatePropertyPattern: '(_)?[A-Za-z][_A-Za-z0-9]*' 363 | PackageNaming: 364 | active: true 365 | excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" 366 | packagePattern: '^[a-z]+(\.[a-z][A-Za-z0-9]*)*$' 367 | TopLevelPropertyNaming: 368 | active: true 369 | excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" 370 | constantPattern: '[A-Z][_A-Z0-9]*' 371 | propertyPattern: '[A-Za-z][_A-Za-z0-9]*' 372 | privatePropertyPattern: '_?[A-Za-z][_A-Za-z0-9]*' 373 | VariableMaxLength: 374 | active: false 375 | excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" 376 | maximumVariableNameLength: 64 377 | VariableMinLength: 378 | active: false 379 | excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" 380 | minimumVariableNameLength: 1 381 | VariableNaming: 382 | active: true 383 | excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" 384 | variablePattern: '[a-z][A-Za-z0-9]*' 385 | privateVariablePattern: '(_)?[a-z][A-Za-z0-9]*' 386 | excludeClassPattern: '$^' 387 | ignoreOverridden: true 388 | 389 | performance: 390 | active: true 391 | ArrayPrimitive: 392 | active: true 393 | ForEachOnRange: 394 | active: true 395 | excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" 396 | SpreadOperator: 397 | active: true 398 | excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" 399 | UnnecessaryTemporaryInstantiation: 400 | active: true 401 | 402 | potential-bugs: 403 | active: true 404 | Deprecation: 405 | active: false 406 | DuplicateCaseInWhenExpression: 407 | active: true 408 | EqualsAlwaysReturnsTrueOrFalse: 409 | active: true 410 | EqualsWithHashCodeExist: 411 | active: true 412 | ExplicitGarbageCollectionCall: 413 | active: true 414 | HasPlatformType: 415 | active: false 416 | ImplicitDefaultLocale: 417 | active: false 418 | InvalidRange: 419 | active: true 420 | IteratorHasNextCallsNextMethod: 421 | active: true 422 | IteratorNotThrowingNoSuchElementException: 423 | active: true 424 | LateinitUsage: 425 | active: false 426 | excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" 427 | excludeAnnotatedProperties: "" 428 | ignoreOnClassesPattern: "" 429 | MapGetWithNotNullAssertionOperator: 430 | active: false 431 | MissingWhenCase: 432 | active: true 433 | RedundantElseInWhen: 434 | active: true 435 | UnconditionalJumpStatementInLoop: 436 | active: false 437 | UnreachableCode: 438 | active: true 439 | UnsafeCallOnNullableType: 440 | active: true 441 | UnsafeCast: 442 | active: false 443 | UselessPostfixExpression: 444 | active: false 445 | WrongEqualsTypeParameter: 446 | active: true 447 | 448 | style: 449 | active: true 450 | CollapsibleIfStatements: 451 | active: false 452 | DataClassContainsFunctions: 453 | active: false 454 | conversionFunctionPrefix: 'to' 455 | DataClassShouldBeImmutable: 456 | active: false 457 | EqualsNullCall: 458 | active: true 459 | EqualsOnSignatureLine: 460 | active: false 461 | ExplicitCollectionElementAccessMethod: 462 | active: false 463 | ExplicitItLambdaParameter: 464 | active: false 465 | ExpressionBodySyntax: 466 | active: false 467 | includeLineWrapping: false 468 | ForbiddenComment: 469 | active: true 470 | values: 'TODO:,FIXME:,STOPSHIP:' 471 | allowedPatterns: "" 472 | ForbiddenImport: 473 | active: false 474 | imports: '' 475 | forbiddenPatterns: "" 476 | ForbiddenMethodCall: 477 | active: false 478 | methods: '' 479 | ForbiddenPublicDataClass: 480 | active: false 481 | ignorePackages: '*.internal,*.internal.*' 482 | ForbiddenVoid: 483 | active: false 484 | ignoreOverridden: false 485 | ignoreUsageInGenerics: false 486 | FunctionOnlyReturningConstant: 487 | active: true 488 | ignoreOverridableFunction: true 489 | excludedFunctions: 'describeContents' 490 | excludeAnnotatedFunction: "dagger.Provides" 491 | LibraryCodeMustSpecifyReturnType: 492 | active: true 493 | LoopWithTooManyJumpStatements: 494 | active: true 495 | maxJumpCount: 1 496 | MagicNumber: 497 | active: true 498 | excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" 499 | ignoreNumbers: '-1,0,1,2' 500 | ignoreHashCodeFunction: true 501 | ignorePropertyDeclaration: false 502 | ignoreLocalVariableDeclaration: false 503 | ignoreConstantDeclaration: true 504 | ignoreCompanionObjectPropertyDeclaration: true 505 | ignoreAnnotation: false 506 | ignoreNamedArgument: true 507 | ignoreEnums: false 508 | ignoreRanges: false 509 | MandatoryBracesIfStatements: 510 | active: false 511 | MaxLineLength: 512 | active: true 513 | maxLineLength: 120 514 | excludePackageStatements: true 515 | excludeImportStatements: true 516 | excludeCommentStatements: false 517 | MayBeConst: 518 | active: true 519 | ModifierOrder: 520 | active: true 521 | NestedClassesVisibility: 522 | active: false 523 | NewLineAtEndOfFile: 524 | active: true 525 | NoTabs: 526 | active: false 527 | OptionalAbstractKeyword: 528 | active: true 529 | OptionalUnit: 530 | active: false 531 | OptionalWhenBraces: 532 | active: false 533 | PreferToOverPairSyntax: 534 | active: false 535 | ProtectedMemberInFinalClass: 536 | active: true 537 | RedundantExplicitType: 538 | active: false 539 | RedundantVisibilityModifierRule: 540 | active: false 541 | ReturnCount: 542 | active: true 543 | max: 2 544 | excludedFunctions: "equals" 545 | excludeLabeled: false 546 | excludeReturnFromLambda: true 547 | excludeGuardClauses: false 548 | SafeCast: 549 | active: true 550 | SerialVersionUIDInSerializableClass: 551 | active: false 552 | SpacingBetweenPackageAndImports: 553 | active: false 554 | ThrowsCount: 555 | active: true 556 | max: 2 557 | TrailingWhitespace: 558 | active: false 559 | UnderscoresInNumericLiterals: 560 | active: false 561 | acceptableDecimalLength: 5 562 | UnnecessaryAbstractClass: 563 | active: true 564 | excludeAnnotatedClasses: "dagger.Module" 565 | UnnecessaryAnnotationUseSiteTarget: 566 | active: false 567 | UnnecessaryApply: 568 | active: false 569 | UnnecessaryInheritance: 570 | active: true 571 | UnnecessaryLet: 572 | active: false 573 | UnnecessaryParentheses: 574 | active: false 575 | UntilInsteadOfRangeTo: 576 | active: false 577 | UnusedImports: 578 | active: false 579 | UnusedPrivateClass: 580 | active: true 581 | UnusedPrivateMember: 582 | active: false 583 | allowedNames: "(_|ignored|expected|serialVersionUID)" 584 | UseArrayLiteralsInAnnotations: 585 | active: false 586 | UseCheckOrError: 587 | active: false 588 | UseDataClass: 589 | active: false 590 | excludeAnnotatedClasses: "" 591 | allowVars: false 592 | UseIfInsteadOfWhen: 593 | active: false 594 | UseRequire: 595 | active: false 596 | UselessCallOnNotNull: 597 | active: true 598 | UtilityClassWithPublicConstructor: 599 | active: true 600 | VarCouldBeVal: 601 | active: false 602 | WildcardImport: 603 | active: true 604 | excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" 605 | excludeImports: 'java.util.*,kotlinx.android.synthetic.*' 606 | -------------------------------------------------------------------------------- /dashboard/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /dashboard/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id(GradlePluginId.ANDROID_LIB) 3 | id(GradlePluginId.BASE_GRADLE_PLUGIN) 4 | id(GradlePluginId.SAFE_ARGS) 5 | } 6 | 7 | dependencies { 8 | 9 | // support 10 | implementation(LibraryDependency.APPCOMPAT) 11 | implementation(LibraryDependency.CORE) 12 | implementation(LibraryDependency.MATERIAL) 13 | implementation(LibraryDependency.CONSTRAINT) 14 | implementation(LibraryDependency.NAVIGATION_FRAGMENT) 15 | implementation(LibraryDependency.NAVIGATION_UI) 16 | 17 | addTestDependencies() 18 | } 19 | -------------------------------------------------------------------------------- /dashboard/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.kts. 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 -------------------------------------------------------------------------------- /dashboard/src/androidTest/java/com/mi/dashboard/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.mi.dashboard 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4 4 | import androidx.test.platform.app.InstrumentationRegistry 5 | import org.junit.Assert.* 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.getInstrumentation().targetContext 20 | assertEquals("com.mi.dashboard", appContext.packageName) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /dashboard/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /dashboard/src/main/java/com/mi/dashboard/DashboardViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.mi.dashboard 2 | 3 | import androidx.lifecycle.ViewModel 4 | 5 | class DashboardViewModel : ViewModel() 6 | -------------------------------------------------------------------------------- /dashboard/src/main/java/com/mi/dashboard/FirstFragment.kt: -------------------------------------------------------------------------------- 1 | package com.mi.dashboard 2 | 3 | import android.os.Bundle 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import android.widget.Button 8 | import androidx.fragment.app.Fragment 9 | import androidx.navigation.fragment.findNavController 10 | 11 | /** 12 | * A simple [Fragment] subclass as the default destination in the navigation. 13 | */ 14 | class FirstFragment : Fragment() { 15 | 16 | override fun onCreateView( 17 | inflater: LayoutInflater, 18 | container: ViewGroup?, 19 | savedInstanceState: Bundle? 20 | ): View? { 21 | // Inflate the layout for this fragment 22 | return inflater.inflate(R.layout.fragment_first, container, false) 23 | } 24 | 25 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 26 | super.onViewCreated(view, savedInstanceState) 27 | 28 | view.findViewById