├── app ├── .gitignore ├── src │ ├── main │ │ ├── res │ │ │ ├── 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 │ │ │ │ ├── strings.xml │ │ │ │ ├── colors.xml │ │ │ │ └── styles.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ ├── ic_launcher.xml │ │ │ │ └── ic_launcher_round.xml │ │ │ ├── layout │ │ │ │ ├── activity_splash.xml │ │ │ │ ├── fragment_random_joke.xml │ │ │ │ └── activity_main.xml │ │ │ ├── drawable-v24 │ │ │ │ └── ic_launcher_foreground.xml │ │ │ └── drawable │ │ │ │ └── ic_launcher_background.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── sucho │ │ │ │ └── kodeinexample │ │ │ │ ├── DataObject.kt │ │ │ │ ├── data │ │ │ │ ├── model │ │ │ │ │ ├── JokeResponse.kt │ │ │ │ │ ├── JokeListResponse.kt │ │ │ │ │ ├── Joke.kt │ │ │ │ │ └── GithubUser.kt │ │ │ │ └── services │ │ │ │ │ ├── GithubApiService.kt │ │ │ │ │ └── ApiService.kt │ │ │ │ ├── utils │ │ │ │ ├── IRxSchedulers.kt │ │ │ │ ├── ViewModelFactory.kt │ │ │ │ └── PrefsUtils.kt │ │ │ │ ├── di │ │ │ │ ├── FragmentModule.kt │ │ │ │ ├── ActivityModule.kt │ │ │ │ ├── ServiceModule.kt │ │ │ │ ├── AppModule.kt │ │ │ │ └── NetworkModule.kt │ │ │ │ ├── feature │ │ │ │ ├── base │ │ │ │ │ ├── navigator │ │ │ │ │ │ ├── ActivityNavigator.kt │ │ │ │ │ │ └── Navigator.kt │ │ │ │ │ ├── BaseViewModel.kt │ │ │ │ │ ├── BaseActivity.kt │ │ │ │ │ └── BaseDialogFragment.kt │ │ │ │ ├── main │ │ │ │ │ ├── randomjoke │ │ │ │ │ │ ├── RandomJokeViewModel.kt │ │ │ │ │ │ └── RandomJokeFragment.kt │ │ │ │ │ ├── MainActivity.kt │ │ │ │ │ └── MainViewModel.kt │ │ │ │ └── splash │ │ │ │ │ ├── SplashViewModel.kt │ │ │ │ │ └── SplashActivity.kt │ │ │ │ └── CustomApplication.kt │ │ └── AndroidManifest.xml │ └── test │ │ └── java │ │ └── com │ │ └── sucho │ │ └── kodeinexample │ │ ├── IherbTestRunner.java │ │ ├── mock │ │ ├── MockApplication.kt │ │ ├── MockServiceModule.kt │ │ ├── MockAppModule.kt │ │ └── MockNetworkModule.kt │ │ └── activity │ │ └── MainActivityTest.kt ├── proguard-rules.pro └── build.gradle ├── settings.gradle ├── .DS_Store ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── .gitignore ├── README.md ├── gradlew.bat ├── gradlew └── LICENSE /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suchoX/Kodein-MVVM/HEAD/.DS_Store -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suchoX/Kodein-MVVM/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suchoX/Kodein-MVVM/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suchoX/Kodein-MVVM/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suchoX/Kodein-MVVM/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suchoX/Kodein-MVVM/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suchoX/Kodein-MVVM/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suchoX/Kodein-MVVM/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/suchoX/Kodein-MVVM/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/suchoX/Kodein-MVVM/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/suchoX/Kodein-MVVM/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/suchoX/Kodein-MVVM/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/java/com/sucho/kodeinexample/DataObject.kt: -------------------------------------------------------------------------------- 1 | package com.sucho.kodeinexample 2 | 3 | data class DataObject( 4 | val name: String, 5 | val id: Long 6 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/sucho/kodeinexample/data/model/JokeResponse.kt: -------------------------------------------------------------------------------- 1 | package com.sucho.kodeinexample.data.model 2 | 3 | data class JokeResponse( 4 | val type: String, 5 | val value: Joke 6 | ) 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Kodein Example 3 | Kodein-MVVM 4 | Random Joke 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/java/com/sucho/kodeinexample/utils/IRxSchedulers.kt: -------------------------------------------------------------------------------- 1 | package com.sucho.kodeinexample.utils 2 | 3 | import io.reactivex.Scheduler 4 | 5 | interface IRxSchedulers { 6 | fun main(): Scheduler 7 | fun io(): Scheduler 8 | } -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Aug 27 13:33:35 IST 2018 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-4.4-all.zip 7 | -------------------------------------------------------------------------------- /app/src/main/java/com/sucho/kodeinexample/di/FragmentModule.kt: -------------------------------------------------------------------------------- 1 | package com.sucho.kodeinexample.di 2 | 3 | import org.kodein.di.Kodein.Module 4 | 5 | private const val MODULE_NAME = "Fragment Module" 6 | 7 | val fragmentModule = Module(MODULE_NAME, false) { 8 | //Add Fragment Dependencies 9 | 10 | } -------------------------------------------------------------------------------- /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/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | #ffffff 7 | #009688 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/java/com/sucho/kodeinexample/data/services/GithubApiService.kt: -------------------------------------------------------------------------------- 1 | package com.sucho.kodeinexample.data.services 2 | 3 | import com.sucho.kodeinexample.data.model.GithubUser 4 | import io.reactivex.Observable 5 | import retrofit2.http.GET 6 | 7 | interface GithubApiService { 8 | @GET("/users/octocat") fun getOctocatProfile(): Observable 9 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sucho/kodeinexample/data/model/JokeListResponse.kt: -------------------------------------------------------------------------------- 1 | package com.sucho.kodeinexample.data.model 2 | 3 | import com.google.gson.annotations.SerializedName 4 | import com.sucho.kodeinexample.data.model.Joke 5 | 6 | data class JokeListResponse( 7 | @SerializedName("type") 8 | val type: String, 9 | @SerializedName("value") 10 | val value: ArrayList) -------------------------------------------------------------------------------- /app/src/main/java/com/sucho/kodeinexample/data/model/Joke.kt: -------------------------------------------------------------------------------- 1 | package com.sucho.kodeinexample.data.model 2 | 3 | import android.os.Parcelable 4 | import com.google.gson.annotations.SerializedName 5 | import kotlinx.android.parcel.Parcelize 6 | 7 | @Parcelize 8 | data class Joke( 9 | @SerializedName("id") 10 | val id: Int, 11 | @SerializedName("joke") 12 | val joke: String): Parcelable -------------------------------------------------------------------------------- /app/src/main/java/com/sucho/kodeinexample/feature/base/navigator/ActivityNavigator.kt: -------------------------------------------------------------------------------- 1 | package com.sucho.kodeinexample.feature.base.navigator 2 | 3 | import android.app.Activity 4 | import com.sucho.kodeinexample.feature.base.navigator.Navigator 5 | 6 | class ActivityNavigator constructor(private val activity: Activity) : Navigator() { 7 | 8 | override fun getActivity(): Activity = activity 9 | 10 | } -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/java/com/sucho/kodeinexample/data/services/ApiService.kt: -------------------------------------------------------------------------------- 1 | package com.sucho.kodeinexample.data.services 2 | 3 | import com.sucho.kodeinexample.data.model.JokeListResponse 4 | import com.sucho.kodeinexample.data.model.JokeResponse 5 | import io.reactivex.Single 6 | import retrofit2.http.GET 7 | 8 | interface ApiService { 9 | @GET("/jokes/random/") fun getRandomJoke(): Single 10 | @GET("/jokes/random/5") fun getFiveRandomJokes(): Single 11 | 12 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sucho/kodeinexample/feature/main/randomjoke/RandomJokeViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.sucho.kodeinexample.feature.main.randomjoke 2 | 3 | import android.content.Context 4 | import com.sucho.kodeinexample.feature.base.BaseViewModel 5 | import org.kodein.di.KodeinAware 6 | import org.kodein.di.android.closestKodein 7 | import org.kodein.di.generic.kcontext 8 | 9 | class RandomJokeViewModel constructor(context: Context): BaseViewModel(), KodeinAware { 10 | override val kodein by closestKodein(context) 11 | override val kodeinContext = kcontext(context) 12 | 13 | } -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/src/main/java/com/sucho/kodeinexample/di/ActivityModule.kt: -------------------------------------------------------------------------------- 1 | package com.sucho.kodeinexample.di 2 | 3 | import android.arch.lifecycle.ViewModelProvider 4 | import com.sucho.kodeinexample.feature.base.navigator.ActivityNavigator 5 | import com.sucho.kodeinexample.feature.base.navigator.Navigator 6 | import com.sucho.kodeinexample.utils.ViewModelFactory 7 | import org.kodein.di.Kodein.Module 8 | import org.kodein.di.generic.bind 9 | import org.kodein.di.generic.instance 10 | import org.kodein.di.generic.singleton 11 | 12 | private const val MODULE_NAME = "Activity Module" 13 | 14 | val activityModule = Module(MODULE_NAME, false) { 15 | bind() with singleton { ActivityNavigator(instance()) } 16 | bind() with singleton { ViewModelFactory(instance("ActivityContext")) } 17 | } -------------------------------------------------------------------------------- /app/src/test/java/com/sucho/kodeinexample/IherbTestRunner.java: -------------------------------------------------------------------------------- 1 | package com.sucho.kodeinexample; 2 | 3 | import android.os.Build; 4 | 5 | import com.sucho.kodeinexample.mock.MockApplication; 6 | 7 | import org.junit.runners.model.InitializationError; 8 | import org.robolectric.RobolectricTestRunner; 9 | import org.robolectric.annotation.Config; 10 | 11 | public class IherbTestRunner extends RobolectricTestRunner{ 12 | public IherbTestRunner(Class testClass) throws InitializationError { 13 | super(testClass); 14 | } 15 | 16 | 17 | @Override 18 | protected Config buildGlobalConfig() { 19 | return new Config.Builder() 20 | .setSdk(Build.VERSION_CODES.M) 21 | .setApplication(MockApplication.class) 22 | .build(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the ART/Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | out/ 15 | 16 | # Gradle files 17 | .gradle/ 18 | build/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Test Files 24 | accusdk/test 25 | 26 | # Proguard folder generated by Eclipse 27 | proguard/ 28 | 29 | # Log Files 30 | *.log 31 | 32 | # Android Studio Navigation editor temp files 33 | .navigation/ 34 | 35 | # Android Studio captures folder 36 | captures/ 37 | 38 | # Intellij 39 | *.iml 40 | .idea/ 41 | 42 | # Keystore files 43 | *.jks 44 | 45 | # External native build folder generated in Android Studio 2.2 and later 46 | .externalNativeBuild 47 | 48 | # Google Services (e.g. APIs or Firebase) 49 | google-services.json 50 | 51 | # Freeline 52 | freeline.py 53 | freeline/ 54 | freeline_project_description.json -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_splash.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 14 | 15 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_random_joke.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 14 | 15 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/test/java/com/sucho/kodeinexample/mock/MockApplication.kt: -------------------------------------------------------------------------------- 1 | package com.sucho.kodeinexample.mock 2 | 3 | import android.app.Application 4 | import android.content.Context 5 | import org.kodein.di.Kodein 6 | import org.kodein.di.KodeinAware 7 | import org.kodein.di.generic.bind 8 | import org.kodein.di.generic.singleton 9 | import org.robolectric.TestLifecycleApplication 10 | import java.lang.reflect.Method 11 | 12 | class MockApplication : Application(), KodeinAware, TestLifecycleApplication { 13 | 14 | override val kodein = Kodein.lazy { 15 | bind("ApplicationContext") with singleton { this@MockApplication.applicationContext } 16 | bind() with singleton { this@MockApplication } 17 | import(mockAppModule) 18 | import(mockNetworkModule) 19 | import(mockServiceModule) 20 | } 21 | 22 | 23 | override fun beforeTest(method: Method) {} 24 | override fun prepareTest(o: Any) {} 25 | override fun afterTest(method: Method) {} 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/java/com/sucho/kodeinexample/feature/splash/SplashViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.sucho.kodeinexample.feature.splash 2 | 3 | import android.content.Context 4 | import android.os.Handler 5 | import com.sucho.kodeinexample.feature.base.BaseViewModel 6 | import com.sucho.kodeinexample.feature.base.navigator.Navigator 7 | import com.sucho.kodeinexample.feature.main.MainActivity 8 | import com.sucho.kodeinexample.utils.PrefsUtils 9 | import org.kodein.di.KodeinAware 10 | import org.kodein.di.android.closestKodein 11 | import org.kodein.di.generic.instance 12 | import org.kodein.di.generic.kcontext 13 | 14 | class SplashViewModel constructor(context: Context) : BaseViewModel(), KodeinAware { 15 | override val kodein by closestKodein(context) 16 | override val kodeinContext = kcontext(context) 17 | 18 | private val navigator: Navigator by instance() 19 | 20 | fun gotoMainActivity() { 21 | Handler().postDelayed({ 22 | navigator.startActivityAndFinish(MainActivity::class.java) 23 | }, 2000) 24 | } 25 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sucho/kodeinexample/CustomApplication.kt: -------------------------------------------------------------------------------- 1 | package com.sucho.kodeinexample 2 | 3 | import android.app.Application 4 | import android.content.Context 5 | import com.sucho.kodeinexample.di.appModule 6 | import com.sucho.kodeinexample.di.networkModule 7 | import com.sucho.kodeinexample.di.serviceModule 8 | import org.kodein.di.Kodein 9 | import org.kodein.di.KodeinAware 10 | import org.kodein.di.generic.bind 11 | import org.kodein.di.generic.singleton 12 | import timber.log.Timber 13 | 14 | class CustomApplication : Application(), KodeinAware { 15 | override val kodein = Kodein.lazy { 16 | bind("ApplicationContext") with singleton { this@CustomApplication.applicationContext } 17 | bind() with singleton { this@CustomApplication } 18 | import(appModule) 19 | import(networkModule) 20 | import(serviceModule) 21 | } 22 | 23 | override fun onCreate() { 24 | super.onCreate() 25 | 26 | if (BuildConfig.DEBUG) { 27 | Timber.plant(Timber.DebugTree()) 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sucho/kodeinexample/feature/splash/SplashActivity.kt: -------------------------------------------------------------------------------- 1 | package com.sucho.kodeinexample.feature.splash 2 | 3 | import android.os.Bundle 4 | import android.view.Window 5 | import android.view.WindowManager 6 | import com.sucho.kodeinexample.R 7 | import com.sucho.kodeinexample.databinding.SplashActivityBinding 8 | import com.sucho.kodeinexample.feature.base.BaseActivity 9 | 10 | class SplashActivity : BaseActivity() { 11 | override fun getViewModelClass(): Class = SplashViewModel::class.java 12 | 13 | override fun layoutId(): Int = R.layout.activity_splash 14 | 15 | override fun onCreate(savedInstanceState: Bundle?) { 16 | fullScreen() 17 | super.onCreate(savedInstanceState) 18 | viewModel.gotoMainActivity() 19 | } 20 | 21 | private fun fullScreen() { 22 | requestWindowFeature(Window.FEATURE_NO_TITLE) 23 | this.window.setFlags( 24 | WindowManager.LayoutParams.FLAG_FULLSCREEN, 25 | WindowManager.LayoutParams.FLAG_FULLSCREEN 26 | ) 27 | } 28 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sucho/kodeinexample/di/ServiceModule.kt: -------------------------------------------------------------------------------- 1 | package com.sucho.kodeinexample.di 2 | 3 | import com.sucho.kodeinexample.data.model.GithubUser 4 | import com.sucho.kodeinexample.data.services.ApiService 5 | import com.sucho.kodeinexample.data.services.GithubApiService 6 | import io.reactivex.Observable 7 | import io.reactivex.schedulers.Schedulers 8 | import org.kodein.di.Kodein 9 | import org.kodein.di.generic.bind 10 | import org.kodein.di.generic.instance 11 | import org.kodein.di.generic.singleton 12 | 13 | private const val MODULE_NAME = "Service Module" 14 | 15 | val serviceModule = Kodein.Module(MODULE_NAME, false) { 16 | bind() with singleton { GithubService(instance(),instance()) } 17 | } 18 | 19 | interface IGithubService { 20 | fun getUserProfile(): Observable 21 | } 22 | 23 | class GithubService(val apiService: ApiService, val githubApiService: GithubApiService): IGithubService{ 24 | override fun getUserProfile(): Observable { 25 | return githubApiService.getOctocatProfile().subscribeOn(Schedulers.io()) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/sucho/kodeinexample/utils/ViewModelFactory.kt: -------------------------------------------------------------------------------- 1 | package com.sucho.kodeinexample.utils 2 | 3 | import android.arch.lifecycle.ViewModel 4 | import android.arch.lifecycle.ViewModelProvider 5 | import android.content.Context 6 | import com.sucho.kodeinexample.feature.main.MainViewModel 7 | import com.sucho.kodeinexample.feature.main.randomjoke.RandomJokeViewModel 8 | import com.sucho.kodeinexample.feature.splash.SplashViewModel 9 | 10 | @Suppress("UNCHECKED_CAST") 11 | class ViewModelFactory constructor(private val context: Context): ViewModelProvider.Factory { 12 | override fun create(modelClass: Class): T { 13 | if(modelClass.isAssignableFrom(MainViewModel::class.java)) { 14 | return MainViewModel(context) as T 15 | } else if(modelClass.isAssignableFrom(SplashViewModel::class.java)) { 16 | return SplashViewModel(context) as T 17 | }else if(modelClass.isAssignableFrom(RandomJokeViewModel::class.java)) { 18 | return RandomJokeViewModel(context) as T 19 | } 20 | throw IllegalArgumentException("Unknown class name") 21 | } 22 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sucho/kodeinexample/utils/PrefsUtils.kt: -------------------------------------------------------------------------------- 1 | package com.sucho.kodeinexample.utils 2 | 3 | import android.content.SharedPreferences 4 | import android.util.Log 5 | 6 | class PrefsUtils constructor(private val prefs: SharedPreferences) { 7 | companion object { 8 | private const val PREFS_LOCATION_PERMISSION_REQUESTED = "Location permission requested" 9 | private const val PREFS_STORAGE_PERMISSION_REQUESTED = "Storage permission requested" 10 | } 11 | 12 | fun getLocationPermissionRequested(): Boolean { 13 | return prefs.getBoolean(PREFS_LOCATION_PERMISSION_REQUESTED, false) 14 | } 15 | 16 | fun setLocationPermissionRequested(b: Boolean) { 17 | prefs.edit().putBoolean(PREFS_LOCATION_PERMISSION_REQUESTED, b).apply() 18 | } 19 | 20 | fun getStoragePermissionRequested(): Boolean { 21 | return prefs.getBoolean(PREFS_STORAGE_PERMISSION_REQUESTED, false) 22 | } 23 | 24 | fun setStoragePermissionRequested(b: Boolean) { 25 | prefs.edit().putBoolean(PREFS_STORAGE_PERMISSION_REQUESTED, b).apply() 26 | } 27 | 28 | fun test() { 29 | Log.e("DDDD", "DDDD") 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/java/com/sucho/kodeinexample/feature/base/BaseViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.sucho.kodeinexample.feature.base 2 | 3 | import android.arch.lifecycle.LifecycleObserver 4 | import android.arch.lifecycle.ViewModel 5 | import android.content.Context 6 | import io.reactivex.disposables.CompositeDisposable 7 | import io.reactivex.disposables.Disposable 8 | import org.kodein.di.Kodein 9 | import org.kodein.di.KodeinAware 10 | import org.kodein.di.generic.kcontext 11 | import timber.log.Timber 12 | 13 | abstract class BaseViewModel : ViewModel(), LifecycleObserver { 14 | 15 | private var compositeDisposable: CompositeDisposable? = null 16 | 17 | override fun onCleared() { 18 | super.onCleared() 19 | Timber.d("unsubscribeFromDataStore(): ") 20 | if (compositeDisposable != null) { 21 | compositeDisposable!!.dispose() 22 | compositeDisposable!!.clear() 23 | compositeDisposable = null 24 | } 25 | } 26 | 27 | protected fun addDisposable(disposable: Disposable) { 28 | if (compositeDisposable == null) { 29 | compositeDisposable = CompositeDisposable() 30 | } 31 | compositeDisposable!!.add(disposable) 32 | } 33 | 34 | } -------------------------------------------------------------------------------- /app/src/test/java/com/sucho/kodeinexample/mock/MockServiceModule.kt: -------------------------------------------------------------------------------- 1 | package com.sucho.kodeinexample.mock 2 | 3 | import com.sucho.kodeinexample.data.model.GithubUser 4 | import com.sucho.kodeinexample.data.services.ApiService 5 | import com.sucho.kodeinexample.data.services.GithubApiService 6 | import com.sucho.kodeinexample.di.IGithubService 7 | import io.reactivex.Observable 8 | import org.kodein.di.Kodein 9 | import org.kodein.di.generic.bind 10 | import org.kodein.di.generic.instance 11 | import org.kodein.di.generic.singleton 12 | 13 | private const val MODULE_NAME = "Service Module" 14 | 15 | val mockServiceModule = Kodein.Module(MODULE_NAME, false) { 16 | bind() with singleton { GithubService(instance(), instance()) } 17 | } 18 | 19 | class GithubService(val apiService: ApiService,val githubApiService: GithubApiService) : IGithubService { 20 | override fun getUserProfile(): Observable { 21 | return Observable.just(GithubUser("","","","Github","","", 22 | "",12,"",123,"","","","", 23 | "",123,"","","","","",123, 24 | 123,"","",true,"","", 25 | "","","")) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/sucho/kodeinexample/data/model/GithubUser.kt: -------------------------------------------------------------------------------- 1 | package com.sucho.kodeinexample.data.model 2 | 3 | data class GithubUser( 4 | val avatar_url: String, 5 | val bio: Any?, 6 | val blog: String, 7 | val company: String, 8 | val created_at: String, 9 | val email: Any?, 10 | val events_url: String, 11 | val followers: Int, 12 | val followers_url: String, 13 | val following: Int, 14 | val following_url: String, 15 | val gists_url: String, 16 | val gravatar_id: String, 17 | val hireable: Any?, 18 | val html_url: String, 19 | val id: Int, 20 | val location: String, 21 | val login: String, 22 | val name: String, 23 | val node_id: String, 24 | val organizations_url: String, 25 | val public_gists: Int, 26 | val public_repos: Int, 27 | val received_events_url: String, 28 | val repos_url: String, 29 | val site_admin: Boolean, 30 | val starred_url: String, 31 | val subscriptions_url: String, 32 | val type: String, 33 | val updated_at: String, 34 | val url: String 35 | ) -------------------------------------------------------------------------------- /app/src/test/java/com/sucho/kodeinexample/activity/MainActivityTest.kt: -------------------------------------------------------------------------------- 1 | package com.sucho.kodeinexample.activity 2 | 3 | 4 | import androidx.test.core.app.ActivityScenario 5 | import com.sucho.kodeinexample.IherbTestRunner 6 | import com.sucho.kodeinexample.feature.main.MainActivity 7 | import org.junit.Assert 8 | import org.junit.Before 9 | import org.junit.Test 10 | import org.junit.runner.RunWith 11 | import java.lang.Thread.sleep 12 | 13 | 14 | /** 15 | * Example local unit test, which will execute on the development machine (host). 16 | * 17 | * See [testing documentation](http://d.android.com/tools/testing). 18 | */ 19 | 20 | @RunWith(IherbTestRunner::class) 21 | class MainActivityTest { 22 | private lateinit var testObj: ActivityScenario 23 | 24 | @Before 25 | fun setup() { 26 | testObj = ActivityScenario.launch(MainActivity::class.java) 27 | } 28 | 29 | @Test 30 | fun testCompanyName() { 31 | testObj.onActivity { 32 | it.viewModel.fetchRandomJoke() 33 | sleep(10000) 34 | val companyName = it.viewModel.stringLiveData.value 35 | Assert.assertTrue(companyName == "Github") 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 12 | 16 | 26 | 27 |