├── app
├── .gitignore
├── src
│ └── main
│ │ ├── res
│ │ ├── values
│ │ │ ├── strings.xml
│ │ │ ├── colors.xml
│ │ │ ├── styles.xml
│ │ │ └── dimens.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
│ │ │ ├── fragment_main.xml
│ │ │ └── activity_main.xml
│ │ ├── navigation
│ │ │ └── nav_graph.xml
│ │ ├── drawable-v24
│ │ │ └── ic_launcher_foreground.xml
│ │ └── drawable
│ │ │ └── ic_launcher_background.xml
│ │ ├── java
│ │ └── com
│ │ │ └── enginebai
│ │ │ └── project
│ │ │ ├── di
│ │ │ ├── AppModule.kt
│ │ │ ├── UtilsModule.kt
│ │ │ └── NetworkModule.kt
│ │ │ ├── AppContext.kt
│ │ │ ├── MainActivity.kt
│ │ │ └── utils
│ │ │ └── ExceptionHandler.kt
│ │ └── AndroidManifest.xml
├── build.gradle.kts
└── proguard-rules.pro
├── settings.gradle.kts
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .idea
├── encodings.xml
├── codeStyles
│ ├── codeStyleConfig.xml
│ └── Project.xml
├── kotlinScripting.xml
├── compiler.xml
├── runConfigurations.xml
├── misc.xml
└── jarRepositories.xml
├── .circleci
└── config.yml
├── LICENSE
├── gradle.properties
├── EXTENSIONS.md
├── gradlew.bat
├── gradlew
├── .gitignore
└── README.md
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Base
3 |
4 |
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | include(":app")
2 | // TODO: 1. change your project name
3 | rootProject.name="AndroidBase"
4 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/enginebai/AndroidBase/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/enginebai/AndroidBase/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/enginebai/AndroidBase/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/enginebai/AndroidBase/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/enginebai/AndroidBase/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/enginebai/AndroidBase/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/enginebai/AndroidBase/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/enginebai/AndroidBase/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/enginebai/AndroidBase/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/enginebai/AndroidBase/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/enginebai/AndroidBase/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/kotlinScripting.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2.1
2 |
3 | orbs:
4 | android: circleci/android@0.2.0
5 |
6 | jobs:
7 | build:
8 | executor: android/android
9 |
10 | steps:
11 | - checkout
12 | - run:
13 | command: ./gradlew build
14 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #008577
4 | #00574B
5 | #D81B60
6 |
7 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Oct 09 12:15:45 CST 2019
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
7 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/java/com/enginebai/project/di/AppModule.kt:
--------------------------------------------------------------------------------
1 | package com.enginebai.project.di
2 |
3 | import com.enginebai.project.utils.ExceptionHandler
4 | import com.google.gson.Gson
5 | import org.koin.dsl.module
6 |
7 | val appModule = module {
8 | single { Gson() }
9 | single(createdAtStart = true) { ExceptionHandler() }
10 | }
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/java/com/enginebai/project/AppContext.kt:
--------------------------------------------------------------------------------
1 | package com.enginebai.project
2 |
3 | import com.enginebai.base.BaseApplication
4 | import com.enginebai.project.di.networkModule
5 | import com.enginebai.project.di.appModule
6 | import com.enginebai.project.di.logModule
7 |
8 | class AppContext : BaseApplication() {
9 | override fun defineDependencies() = listOf(networkModule, logModule, appModule)
10 | }
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 32dp
4 | 16dp
5 | 12dp
6 | 8dp
7 | 4dp
8 |
9 | 20sp
10 | 18sp
11 | 16sp
12 | 14sp
13 | 12sp
14 |
--------------------------------------------------------------------------------
/app/src/main/res/navigation/nav_graph.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
--------------------------------------------------------------------------------
/app/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("com.android.application")
3 | commonPlugins.forEach { id(it) }
4 | id("androidx.navigation.safeargs.kotlin")
5 | }
6 |
7 | //apply(from = "../dependencies.gradle.kts")
8 | configAndroid()
9 | importCommonDependencies()
10 |
11 | android {
12 | defaultConfig {
13 | applicationId = Versions.App.id
14 | }
15 | }
16 |
17 | dependencies {
18 | implementation(Dependencies.androidBase)
19 | implementation(Dependencies.Navigation.fragmentKtx)
20 | implementation(Dependencies.Navigation.uiKtx)
21 |
22 | implementation(Dependencies.okhttp)
23 | implementation(Dependencies.okhttpLogging)
24 | implementation(Dependencies.Retrofit.core)
25 | implementation(Dependencies.Retrofit.gsonConverter)
26 | implementation(Dependencies.Retrofit.rxJavaAdapter)
27 | }
28 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/app/src/main/java/com/enginebai/project/di/UtilsModule.kt:
--------------------------------------------------------------------------------
1 | package com.enginebai.project.di
2 |
3 | import com.enginebai.base.BuildConfig
4 | import com.enginebai.base.utils.logging.TimberLoggerDebugTree
5 | import com.orhanobut.logger.AndroidLogAdapter
6 | import com.orhanobut.logger.FormatStrategy
7 | import com.orhanobut.logger.PrettyFormatStrategy
8 | import org.koin.dsl.module
9 | import timber.log.Timber
10 |
11 | const val LOG_TAG = "enginebai"
12 |
13 | val logModule = module {
14 | single { (formatStrategy: FormatStrategy) ->
15 | object : AndroidLogAdapter(formatStrategy) {
16 | override fun isLoggable(priority: Int, tag: String?): Boolean {
17 | return BuildConfig.DEBUG && tag == LOG_TAG
18 | }
19 | }
20 | }
21 |
22 | single {
23 | PrettyFormatStrategy.newBuilder()
24 | .tag(LOG_TAG)
25 | .methodCount(3)
26 | .methodOffset(5) // avoid timber internal stack track
27 | .build()
28 | }
29 |
30 | single { TimberLoggerDebugTree() }
31 | }
32 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Engine Bai
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | Android Lint
12 |
13 |
14 |
15 |
16 | Spelling
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 | # Kotlin code style for this project: "official" or "obsolete":
21 | kotlin.code.style=official
22 |
--------------------------------------------------------------------------------
/.idea/jarRepositories.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/app/src/main/java/com/enginebai/project/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.enginebai.project
2 |
3 | import com.enginebai.base.view.BaseActivity
4 | import com.enginebai.project.utils.ExceptionHandler
5 | import io.reactivex.android.schedulers.AndroidSchedulers
6 | import io.reactivex.disposables.Disposable
7 | import org.koin.android.ext.android.inject
8 | import java.util.concurrent.TimeUnit
9 |
10 | // TODO: 4. rename the package for your project
11 | class MainActivity : BaseActivity() {
12 |
13 | private val exceptionHandler: ExceptionHandler by inject()
14 | private var exceptionHandlerDisposable: Disposable? = null
15 |
16 | override fun onStart() {
17 | super.onStart()
18 | // You will handle the error message at single activity.
19 | if (null == exceptionHandlerDisposable || false == exceptionHandlerDisposable?.isDisposed) {
20 | exceptionHandlerDisposable = exceptionHandler.errorMessageToDisplay
21 | .filter { it.isNotBlank() }
22 | .throttleFirst(2, TimeUnit.SECONDS)
23 | .observeOn(AndroidSchedulers.mainThread())
24 | .doOnNext { handleErrorMessage(it) }
25 | .subscribe()
26 | }
27 | }
28 |
29 | override fun onStop() {
30 | exceptionHandlerDisposable?.dispose()
31 | super.onStop()
32 | }
33 |
34 | override fun getLayoutId() = R.layout.activity_main
35 |
36 | override fun handleErrorMessage(message: String) {
37 | TODO("Not yet implemented")
38 | }
39 | }
--------------------------------------------------------------------------------
/EXTENSIONS.md:
--------------------------------------------------------------------------------
1 | # Useful Extension Functions
2 | ## Coroutine
3 | ```kotlin
4 | suspend fun fetchApi(): User { ... }
5 |
6 | // Retry with exponential backoff
7 | retry {
8 | fetchApi()
9 | }
10 |
11 | val controller = CoroutineRunningController()
12 | controller.cancelPreviousThenRun(fetchApi())
13 | controller.queueTask(fetchApi())
14 | controller.joinPreviousOrRun(fetchApi())
15 | ```
16 |
17 | ## View
18 | ```kotlin
19 | // Prevent multiple/duplicate click
20 | View.debounceClick() { ... }
21 |
22 | // Visibility
23 | View.visible()
24 | View.invisible()
25 | View.gone()
26 | View.showIf { ... }
27 | View.hideIf { ... }
28 | View.goneIf { ... }
29 |
30 | // Keyboard
31 | View.showKeyboard()
32 | View.hideKeyboard()
33 |
34 | // Dimension
35 | View.px2dp()
36 | View.dp2px()
37 | View.px2sp()
38 | View.sp2px()
39 |
40 | // Resources
41 | View.getColor(R.color.colorPrimary)
42 | View.getDrawable(R.drawable.ic_launcher)
43 | ```
44 |
45 | ## EditText
46 | ```kotlin
47 | EditText.textChanged { s -> ... }
48 | EditText.textChanged().doOnNext { s -> ... }.subscribe()
49 | EditText.validate(errorMessage = "The name should not be empty") { !it.isNullOrBlank() }
50 | EditText.validateEmail { ... }
51 | EditText.validateEmail("It's not valid email")
52 | EditText.showPassword()
53 | EditText.hidePassword()
54 | ```
55 |
56 | ## TextView
57 | ```kotlin
58 | // Set view gone while text is null or empty.
59 | TextView.setTextWithExistence("1234")
60 |
61 | // Set view invislble while text is null or empty.
62 | TextView.setTextWithVisibility("5678")
63 | ```
64 |
65 | ## String
66 | ```kotlin
67 | String?.isValidEmail(): Boolean
68 | ```
69 |
70 |
71 |
--------------------------------------------------------------------------------
/app/src/main/java/com/enginebai/project/di/NetworkModule.kt:
--------------------------------------------------------------------------------
1 | package com.enginebai.project.di
2 |
3 | import com.enginebai.base.extensions.EnumGsonSerializedNameConverterFactory
4 | import com.enginebai.base.extensions.EnumHasValueConverterFactory
5 | import com.enginebai.project.BuildConfig
6 | import okhttp3.OkHttpClient
7 | import okhttp3.Protocol
8 | import okhttp3.logging.HttpLoggingInterceptor
9 | import org.koin.dsl.module
10 | import retrofit2.Retrofit
11 | import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
12 | import retrofit2.converter.gson.GsonConverterFactory
13 |
14 | val networkModule = module {
15 | single {
16 | HttpLoggingInterceptor().apply {
17 | level = if (BuildConfig.DEBUG)
18 | HttpLoggingInterceptor.Level.BODY
19 | else
20 | HttpLoggingInterceptor.Level.NONE
21 | }
22 | }
23 | single {
24 | val builder = OkHttpClient.Builder()
25 | builder.addInterceptor(get())
26 | builder.protocols(listOf(Protocol.HTTP_1_1, Protocol.HTTP_2))
27 | builder.build()
28 | }
29 |
30 | single { GsonConverterFactory.create() }
31 | single { EnumGsonSerializedNameConverterFactory }
32 | single { EnumHasValueConverterFactory }
33 | single { RxJava2CallAdapterFactory.create() }
34 | single {
35 | // TODO: 5. Set your API base url
36 | Retrofit.Builder()
37 | .baseUrl("")
38 | .addCallAdapterFactory(get())
39 | .addConverterFactory(get())
40 | .addConverterFactory(get())
41 | .client(get())
42 | .build()
43 | }
44 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/app/src/main/java/com/enginebai/project/utils/ExceptionHandler.kt:
--------------------------------------------------------------------------------
1 | package com.enginebai.project.utils
2 |
3 | import io.reactivex.exceptions.OnErrorNotImplementedException
4 | import io.reactivex.exceptions.UndeliverableException
5 | import io.reactivex.functions.Consumer
6 | import io.reactivex.plugins.RxJavaPlugins
7 | import io.reactivex.subjects.PublishSubject
8 | import retrofit2.HttpException
9 | import timber.log.Timber
10 | import java.net.ConnectException
11 | import java.net.SocketException
12 | import java.net.SocketTimeoutException
13 | import java.net.UnknownHostException
14 |
15 | /**
16 | * The exception handler for the application, it will handle RxJava exception automatically,
17 | * and for non-RxJava you call `exceptionHandler.accept(e)`, and add the new branch condition
18 | * for your custom exception you want to handle.
19 | *
20 | * If you want to just display error message, call `errorMessageToDisplay.onNext(error message)`.
21 | * If you want to propagate the exception, call `handledException.onNext(e)`.
22 | *
23 | * To display the error message, you need to subscribe the `errorMessageToDisplay` subject.
24 | * To handle the propagated exception, you need to subscribe the `handledException` subject.
25 | */
26 | class ExceptionHandler : Consumer {
27 |
28 | // Emit strings if you handle the exception and just display error message.
29 | val errorMessageToDisplay = PublishSubject.create()
30 | // Emit the exception if you'd like to propagate the exception and handle in other modules or layers
31 | val handledException = PublishSubject.create()
32 |
33 | init {
34 | RxJavaPlugins.setErrorHandler(this)
35 | }
36 |
37 | override fun accept(t: Throwable) {
38 | val cause = parseCause(t)
39 | Timber.d("Handle exception: $cause from $t")
40 | when (cause) {
41 | is SocketTimeoutException, is ConnectException, is UnknownHostException, is SocketException -> {
42 | val errorMessage = "Network failed: $cause"
43 | Timber.w(errorMessage)
44 | errorMessageToDisplay.onNext(errorMessage)
45 | }
46 | is HttpException -> {
47 | val url = cause.response()?.raw()?.request?.url
48 | errorMessageToDisplay.onNext("HTTP ${cause.code()} of $url")
49 | }
50 | is ApiException -> {
51 | handledException.onNext(cause)
52 | }
53 | // TODO: add case to handle your custom exception
54 | else -> {
55 | // For the case you can't catch or handle
56 | Timber.e(cause)
57 | throw cause
58 | }
59 | }
60 | }
61 |
62 | private fun parseCause(t: Throwable): Throwable {
63 | when (t) {
64 | is OnErrorNotImplementedException, is UndeliverableException, is RuntimeException -> {
65 | t.cause?.run { return this } ?: throw ParseCauseFailException(t)
66 | }
67 | }
68 | return t
69 | }
70 | }
71 |
72 | class ParseCauseFailException(t: Throwable) : RuntimeException(t)
73 | class ApiException(t: Throwable) : RuntimeException(t)
74 |
75 | // TODO: you can create the custom exception for your logic
76 | // Such as UsernameNotFoundException(), PasswordIncorrectException()
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | xmlns:android
32 |
33 | ^$
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | xmlns:.*
43 |
44 | ^$
45 |
46 |
47 | BY_NAME
48 |
49 |
50 |
51 |
52 |
53 |
54 | .*:id
55 |
56 | http://schemas.android.com/apk/res/android
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 | .*:name
66 |
67 | http://schemas.android.com/apk/res/android
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 | name
77 |
78 | ^$
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 | style
88 |
89 | ^$
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 | .*
99 |
100 | ^$
101 |
102 |
103 | BY_NAME
104 |
105 |
106 |
107 |
108 |
109 |
110 | .*
111 |
112 | http://schemas.android.com/apk/res/android
113 |
114 |
115 | ANDROID_ATTRIBUTE_ORDER
116 |
117 |
118 |
119 |
120 |
121 |
122 | .*
123 |
124 | .*
125 |
126 |
127 | BY_NAME
128 |
129 |
130 |
131 |
132 |
133 |
134 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/.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 | ### Android template
16 | # Built application files
17 | *.apk
18 | *.ap_
19 | *.aab
20 |
21 | # Files for the ART/Dalvik VM
22 | *.dex
23 |
24 | # Java class files
25 | *.class
26 |
27 | # Generated files
28 | bin/
29 | gen/
30 | out/
31 | release/
32 |
33 | # Gradle files
34 | .gradle/
35 | build/
36 |
37 | # Local configuration file (sdk path, etc)
38 | local.properties
39 |
40 | # Proguard folder generated by Eclipse
41 | proguard/
42 |
43 | # Log Files
44 | *.log
45 |
46 | # Android Studio Navigation editor temp files
47 | .navigation/
48 |
49 | # Android Studio captures folder
50 | captures/
51 |
52 | # IntelliJ
53 | .idea/workspace.xml
54 | .idea/tasks.xml
55 | .idea/gradle.xml
56 | .idea/assetWizardSettings.xml
57 | .idea/dictionaries
58 | .idea/libraries
59 | # Android Studio 3 in .gitignore file.
60 | .idea/caches
61 | .idea/modules.xml
62 | # Comment next line if keeping position of elements in Navigation Editor is relevant for you
63 | .idea/navEditor.xml
64 |
65 | # Keystore files
66 | # Uncomment the following lines if you do not want to check your keystore files in.
67 | #*.jks
68 | #*.keystore
69 |
70 | # External native build folder generated in Android Studio 2.2 and later
71 |
72 | # Google Services (e.g. APIs or Firebase)
73 | # google-services.json
74 |
75 | # Freeline
76 | freeline.py
77 | freeline/
78 | freeline_project_description.json
79 |
80 | # fastlane
81 | fastlane/report.xml
82 | fastlane/Preview.html
83 | fastlane/screenshots
84 | fastlane/test_output
85 | fastlane/readme.md
86 |
87 | # Version control
88 | vcs.xml
89 |
90 | # lint
91 | lint/intermediates/
92 | lint/generated/
93 | lint/outputs/
94 | lint/tmp/
95 | # lint/reports/
96 |
97 | ### JetBrains template
98 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
99 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
100 |
101 | # User-specific stuff
102 | .idea/**/workspace.xml
103 | .idea/**/tasks.xml
104 | .idea/**/usage.statistics.xml
105 | .idea/**/dictionaries
106 | .idea/**/shelf
107 |
108 | # Generated files
109 | .idea/**/contentModel.xml
110 |
111 | # Sensitive or high-churn files
112 | .idea/**/dataSources/
113 | .idea/**/dataSources.ids
114 | .idea/**/dataSources.local.xml
115 | .idea/**/sqlDataSources.xml
116 | .idea/**/dynamic.xml
117 | .idea/**/uiDesigner.xml
118 | .idea/**/dbnavigator.xml
119 |
120 | # Gradle
121 | .idea/**/gradle.xml
122 | .idea/**/libraries
123 |
124 | # Gradle and Maven with auto-import
125 | # When using Gradle or Maven with auto-import, you should exclude module files,
126 | # since they will be recreated, and may cause churn. Uncomment if using
127 | # auto-import.
128 | # .idea/modules.xml
129 | # .idea/*.iml
130 | # .idea/modules
131 | # *.iml
132 | # *.ipr
133 |
134 | # CMake
135 | cmake-build-*/
136 |
137 | # Mongo Explorer plugin
138 | .idea/**/mongoSettings.xml
139 |
140 | # File-based project format
141 | *.iws
142 |
143 | # IntelliJ
144 |
145 | # mpeltonen/sbt-idea plugin
146 | .idea_modules/
147 |
148 | # JIRA plugin
149 | atlassian-ide-plugin.xml
150 |
151 | # Cursive Clojure plugin
152 | .idea/replstate.xml
153 |
154 | # Crashlytics plugin (for Android Studio and IntelliJ)
155 | com_crashlytics_export_strings.xml
156 | crashlytics.properties
157 | crashlytics-build.properties
158 | fabric.properties
159 |
160 | # Editor-based Rest Client
161 | .idea/httpRequests
162 |
163 | # Android studio 3.1+ serialized cache file
164 | .idea/caches/build_file_checksums.ser
165 |
166 | ### Kotlin template
167 | # Compiled class file
168 |
169 | # Log file
170 |
171 | # BlueJ files
172 | *.ctxt
173 |
174 | # Mobile Tools for Java (J2ME)
175 | .mtj.tmp/
176 |
177 | # Package Files #
178 | *.jar
179 | !gradle/wrapper/gradle-wrapper.jar
180 | *.war
181 | *.nar
182 | *.ear
183 | *.zip
184 | *.tar.gz
185 | *.rar
186 |
187 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
188 | hs_err_pid*
189 | ### Kotlin template
190 | # Compiled class file
191 |
192 | # Log file
193 |
194 | # BlueJ files
195 |
196 | # Mobile Tools for Java (J2ME)
197 |
198 | # Package Files #
199 |
200 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
201 |
202 | ### JetBrains template
203 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
204 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
205 |
206 | # User-specific stuff
207 |
208 | # Generated files
209 |
210 | # Sensitive or high-churn files
211 |
212 | # Gradle
213 |
214 | # Gradle and Maven with auto-import
215 | # When using Gradle or Maven with auto-import, you should exclude module files,
216 | # since they will be recreated, and may cause churn. Uncomment if using
217 | # auto-import.
218 | # .idea/modules.xml
219 | # .idea/*.iml
220 | # .idea/modules
221 | # *.iml
222 | # *.ipr
223 |
224 | # CMake
225 |
226 | # Mongo Explorer plugin
227 |
228 | # File-based project format
229 |
230 | # IntelliJ
231 |
232 | # mpeltonen/sbt-idea plugin
233 |
234 | # JIRA plugin
235 |
236 | # Cursive Clojure plugin
237 |
238 | # Crashlytics plugin (for Android Studio and IntelliJ)
239 |
240 | # Editor-based Rest Client
241 |
242 | # Android studio 3.1+ serialized cache file
243 |
244 | ### Android template
245 | # Built application files
246 |
247 | # Files for the ART/Dalvik VM
248 |
249 | # Java class files
250 |
251 | # Generated files
252 |
253 | # Gradle files
254 |
255 | # Local configuration file (sdk path, etc)
256 |
257 | # Proguard folder generated by Eclipse
258 |
259 | # Log Files
260 |
261 | # Android Studio Navigation editor temp files
262 |
263 | # Android Studio captures folder
264 |
265 | # IntelliJ
266 | # Android Studio 3 in .gitignore file.
267 | # Comment next line if keeping position of elements in Navigation Editor is relevant for you
268 |
269 | # Keystore files
270 | # Uncomment the following lines if you do not want to check your keystore files in.
271 | #*.jks
272 | #*.keystore
273 |
274 | # External native build folder generated in Android Studio 2.2 and later
275 |
276 | # Google Services (e.g. APIs or Firebase)
277 | # google-services.json
278 |
279 | # Freeline
280 |
281 | # fastlane
282 |
283 | # Version control
284 |
285 | # lint
286 | # lint/reports/
287 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |   [](https://jitpack.io/#enginebai/AndroidBase)
2 |
3 | # AndroidBase
4 | The `AndroidBase` project provides an Android app project template that setups for **Gradle Kotlin DSL**, it also provides some base classes or extensions to accerate your android development.
5 |
6 | You can [use this template](https://github.com/enginebai/AndroidBase/generate) or download the [base module](#Download-for-Base-Module).
7 |
8 | There also provides two helper branches:
9 | * If you'd like to see how the simple Gradle Kotlin DSL project works (i.e. a new android app project just created by Android Studio and use Gradle Kotlin DSL), you can checkout to the branch [`gradle-kotlin-dsl`](https://github.com/enginebai/AndroidBase/tree/gradle-kotlin-dsl) to take a look.
10 | * If you'd like to see how the base module works or contribute to this project, you can checkout to the branch [`library`](https://github.com/enginebai/AndroidBase/tree/library).
11 |
12 | ## Setup for Template Project
13 | 1. Just click on [](https://github.com/enginebai/AndroidBase/generate) button to create a new repo starting from this template. Or you can clone this project by `git clone git@github.com:enginebai/AndroidBase.git` .
14 | 1. Change your project name in `settings.gradle.kts`.
15 | 1. Set your application ID in `Versions.kt`
16 | 1. Set the package name in `AndroidManifest.xml` file of `:app` module .
17 | 1. Select `com.enginebai.project` directory in "Project" tool window and rename package for your app.
18 | 1. Specify your retrofit base URL in `NetworkModule.kt` file.
19 | 1. Start to design your main layout xml file `fragment_main.xml` and fragment class.
20 | 1. Specify your `MainFragment.kt` name in navigation graph xml file.
21 | 1. That's all. Start your app development journey now 🎉.
22 |
23 | ## Get Started with Template Project
24 | 1. You can start your development as usual in `app` module.
25 | 1. This projects encourges you to use single activity architecture with [naivgation component](https://developer.android.com/guide/navigation), you will create new fragment that **extends the `BaseFragment`** for your all UI pages.
26 | 1. This project uses [koin](https://github.com/InsertKoinIO/koin) as our dependency injection framework, you will define the modules in `di` package and add those modules in `AppContext.defineDependencies()`
27 | 1. You will handle errors with `ExceptionHandler`, we register a function that will handle errors that are passed to `Subscriber.onError(Throwable)` for RxJava; for non-RxJava exception, you will inject `ExceptionHandler` and pass exception to `accept(Throwable)` function. More detail usage you can check `ExceptionHandler`, there are some instructions that guide you how to write your custom exception handling logic.
28 |
29 | ## Good Practices of Gradle Kotlin DSL
30 | * Add all dependencies versions in `Versions.kt`
31 |
32 | ```kotlin
33 | object Versions {
34 | const val kotlin = "1.3.50"
35 | const val awesomeLibrary = "x.y.z"
36 | // TODO: add the library version
37 | ...
38 | }
39 | ```
40 | * Define all 3rd-party dependencies in `Dependencies.kt`, and use all versions definition in `Versions.kt`.
41 |
42 | ```kotlin
43 | object Dependencies {
44 | const val rxJava = "io.reactivex.rxjava2:rxjava:${Versions.rxJava}"
45 | // TODO: add standalone dependency here!
46 | ...
47 |
48 | object Kotlin {
49 | const val gradlePlugin = "org.jetbrains.kotlin:kotlin-gradle-plugin:${Versions.kotlin}"
50 | const val stdLib = "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${Versions.kotlin}"
51 | }
52 |
53 | // TODO: add inner object for sub-modules of library
54 | object AndroidX {
55 | ...
56 | }
57 | ...
58 | }
59 | ```
60 |
61 | * Always import dependency from `Dependencies.kt` in `build.gradle.kts` file.
62 |
63 | ```kotlin
64 | dependencies {
65 | implementation(Dependencies.Glide.core)
66 | "kapt"(Dependencies.Glide.compiler)
67 | implementation(project(":base"))
68 | // TODO: add by using dependency imported from `Dependencies.kt` file
69 | ...
70 | }
71 | ```
72 |
73 | * Configure android build script in `Config.kt`.
74 |
75 | ```kotlin
76 | fun Project.configAndroid() = this.extensions.getByType().run {
77 | compileSdkVersion(Versions.Android.sdk)
78 | defaultConfig {
79 | minSdkVersion(Versions.Android.minSdk)
80 | targetSdkVersion(Versions.Android.sdk)
81 | versionCode = Versions.App.versionCode
82 | versionName = Versions.App.versionName
83 | // TODO: add your configurations
84 | ...
85 | }
86 | ...
87 | }
88 | ```
89 |
90 | It's equivalent to the old way `android { ... }` block in `build.gradle` file
91 | ```groovy
92 | android {
93 | compileSdkVersion 21
94 | buildToolsVersion "21.1.2"
95 | defaultConfig {
96 | applicationId "com.enginebai.moviehunt"
97 | // TODO: add your configurations
98 | ...
99 | }
100 | ...
101 | }
102 | ```
103 |
104 |
105 | * Add all configuration variables inside `Config` object in `Config.kt`, and add `buildConfigField(...)` to include.
106 |
107 | ```kotlin
108 | object Config {
109 | const val API_ROOT = "\"https://api.themoviedb.org/3/\""
110 | const val IMAGE_API_ROOT = "\"https://image.tmdb.org/t/p/\""
111 | // TODO: add your constants here, make sure to add extra double quotes for string value.
112 | }
113 |
114 | fun Project.configAndroid() = this.extensions.getByType().run {
115 | compileSdkVersion(Versions.Android.sdk)
116 | defaultConfig {
117 | ...
118 |
119 | buildConfigField("String", "API_ROOT", Config.API_ROOT)
120 | buildConfigField("String", "IMAGE_API_KEY", Config.IMAGE_API_ROOT)
121 | // TODO: add your varialbes here imported from `Config` object
122 | ...
123 | }
124 | ...
125 | }
126 | ```
127 |
128 | * Add the common dependencies that share between modules to `Dependencies.kt`
129 |
130 | ```kotlin
131 | fun Project.importCommonDependencies() {
132 | dependencies {
133 | ...
134 | implementation(Dependencies.material)
135 | // TODO: add your common dependencies
136 | ..
137 | }
138 | }
139 | ```
140 |
141 | > **Note:** Remember to perform Gradle Sync to apply your changes when updating any files in `buildSrc`.
142 |
143 | ## Modules Structure
144 | * `:app` module: That's your app module, just like a normal Android app project. Or you can create a new modules (ex: `:common`) for that if you use multi-modules project or libraries. There is the sample `build.gradle.kts` for library module:
145 |
146 | ```kotlin
147 | plugins {
148 | id("com.android.library")
149 | commonPlugins.forEach { id(it) }
150 | }
151 |
152 | configAndroid()
153 | importCommonDependencies()
154 |
155 | dependencies {
156 | implementation(Dependencies.whatever)
157 | ...
158 | }
159 | ```
160 | * `/buildSrc`: It enables you to write the build script (`*.gradle.kts` files) in kotlin to manage dependencies and gets better IDE completion support. It gives you a way to develop build code more like regular code. More information please check [official document](https://docs.gradle.org/current/userguide/organizing_gradle_projects.html#sec:build_sources).
161 |
162 | ## Download for Base Module
163 | Step 1. Add it to your root `build.gradle.kts`:
164 |
165 | ```kotlin
166 | allprojects {
167 | repositories {
168 | ...
169 | maven("https://jitpack.io")
170 | }
171 | }
172 | ```
173 | Step 2. Add the dependency:
174 |
175 | * `Versions.kt`:
176 | ```kotlin
177 | const val androidBase = "1.0.0"
178 | ```
179 |
180 | * `Dependencies.kt`:
181 | ```kotlin
182 | const val androidBase = "com.github.enginebai:AndroidBase:${Versions.androidBase}"
183 | ```
184 |
185 | * App module `build.gradle.kts`:
186 | ```kotlin
187 | dependencies {
188 | ...
189 | implementation(Dependencies.androidBase)
190 | }
191 | ```
192 |
193 | ## Included Libraries
194 | There are some default 3rd-party libraries imported in this project, and provide some popular dependencies (following listed) in `buildSrc/Dependencies.kt` file that you can choose to use. Feel free to add/remove those dependencies.
195 |
196 | * [Android Architecture Components](https://developer.android.com/topic/libraries/architecture), part of Android Jetpack for give to project a robust design, testable and maintainable.
197 | * [Retrofit](https://github.com/square/retrofit) / [OkHttp](https://github.com/square/okhttp), Square open-source RESTful API and http client.
198 | * [RxJava](https://github.com/ReactiveX/RxJava/) / [RxAndroid](https://github.com/ReactiveX/RxAndroid), reactive programming for JVM.
199 | * [Koin](https://github.com/InsertKoinIO/koin), kotlin light-weight dependency injection.
200 | * [Timber](https://github.com/JakeWharton/timber), for logging.
201 | * [Epoxy](https://github.com/airbnb/epoxy), for RecyclerView complex view layout.
202 | * [Paging](https://developer.android.com/topic/libraries/architecture/paging), for pagination loading of RecyclerView.
203 | * [Navigation](https://developer.android.com/guide/navigation), for single activity and fragment routing.
204 | * [Room](https://developer.android.com/training/data-storage/room), for local persistence database.
205 |
206 | ## Useful Extensions
207 | * See [Extension Functions.](./EXTENSIONS.md)
208 |
209 | ## LICENSE
210 |
211 | ```
212 | Copyright (c) 2020 Engine Bai
213 |
214 | Permission is hereby granted, free of charge, to any person obtaining a copy
215 | of this software and associated documentation files (the "Software"), to deal
216 | in the Software without restriction, including without limitation the rights
217 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
218 | copies of the Software, and to permit persons to whom the Software is
219 | furnished to do so, subject to the following conditions:
220 |
221 | The above copyright notice and this permission notice shall be included in all
222 | copies or substantial portions of the Software.
223 |
224 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
225 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
226 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
227 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
228 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
229 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
230 | SOFTWARE.
231 | ```
232 |
233 |
234 |
--------------------------------------------------------------------------------