├── app ├── .gitignore ├── src │ ├── main │ │ ├── res │ │ │ ├── values │ │ │ │ └── strings.xml │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ │ └── ic_launcher.png │ │ │ └── layout │ │ │ │ ├── activity_weather.xml │ │ │ │ ├── activity_main.xml │ │ │ │ ├── activity_split_view.xml │ │ │ │ └── fragment_weather.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── petrulak │ │ │ │ └── cleankotlin │ │ │ │ ├── platform │ │ │ │ ├── bus │ │ │ │ │ ├── data │ │ │ │ │ │ ├── DataBus.kt │ │ │ │ │ │ └── WeatherDataBus.kt │ │ │ │ │ ├── event │ │ │ │ │ │ ├── BaseEventBus.kt │ │ │ │ │ │ ├── events │ │ │ │ │ │ │ ├── FragmentSyncEvent.kt │ │ │ │ │ │ │ ├── WeatherDummyEvent.kt │ │ │ │ │ │ │ └── BaseEvent.kt │ │ │ │ │ │ └── EventBus.kt │ │ │ │ │ └── BaseBus.kt │ │ │ │ ├── extensions │ │ │ │ │ ├── Global.kt │ │ │ │ │ └── RxExtensions.kt │ │ │ │ ├── analytics │ │ │ │ │ ├── AnalyticsManager.kt │ │ │ │ │ ├── MixpanelAnalyticsManager.kt │ │ │ │ │ └── AnalyticsManagerImpl.kt │ │ │ │ ├── navigation │ │ │ │ │ └── Navigator.kt │ │ │ │ ├── logging │ │ │ │ │ └── ErrorReportingTree.kt │ │ │ │ └── connectivity │ │ │ │ │ └── ConnectivityChecker.kt │ │ │ │ ├── ui │ │ │ │ ├── base │ │ │ │ │ ├── BasePresenterLifecycle.kt │ │ │ │ │ ├── BaseView.java │ │ │ │ │ ├── BasePresenter.java │ │ │ │ │ ├── BasePresenterImpl.kt │ │ │ │ │ ├── BaseFragment.kt │ │ │ │ │ └── BaseActivity.kt │ │ │ │ ├── example1 │ │ │ │ │ ├── ExampleParcelable.kt │ │ │ │ │ ├── fragment │ │ │ │ │ │ ├── Example1Contract.kt │ │ │ │ │ │ ├── Example1Presenter.kt │ │ │ │ │ │ └── Example1Fragment.kt │ │ │ │ │ └── Example1Activity.kt │ │ │ │ ├── example2 │ │ │ │ │ ├── ViewState.kt │ │ │ │ │ ├── Example2Contract.kt │ │ │ │ │ ├── Example2Presenter.kt │ │ │ │ │ └── Example2Activity.kt │ │ │ │ ├── example3 │ │ │ │ │ ├── fragment │ │ │ │ │ │ ├── Example3Contract.kt │ │ │ │ │ │ ├── Example3Fragment.kt │ │ │ │ │ │ └── Example3Presenter.kt │ │ │ │ │ └── Example3Activity.kt │ │ │ │ └── main │ │ │ │ │ └── MainActivity.kt │ │ │ │ ├── di │ │ │ │ ├── scope │ │ │ │ │ └── ViewScope.java │ │ │ │ ├── module │ │ │ │ │ ├── MapperModule.kt │ │ │ │ │ ├── DataModule.kt │ │ │ │ │ ├── InteractorModule.kt │ │ │ │ │ ├── PresenterModule.kt │ │ │ │ │ ├── ApplicationModule.kt │ │ │ │ │ └── NetworkModule.kt │ │ │ │ └── component │ │ │ │ │ ├── ViewComponent.kt │ │ │ │ │ └── ApplicationComponent.kt │ │ │ │ └── App.kt │ │ └── AndroidManifest.xml │ └── androidTest │ │ └── java │ │ └── com │ │ └── petrulak │ │ └── cleankotlin │ │ ├── di │ │ ├── component │ │ │ ├── ApplicationMockComponent.kt │ │ │ └── ViewMockComponent.kt │ │ └── module │ │ │ ├── PresenterMockModule.kt │ │ │ ├── InteractorMockModule.kt │ │ │ └── ApplicationMockModule.kt │ │ ├── MockAppTestRunner.kt │ │ ├── MockApp.kt │ │ └── ui │ │ └── example2 │ │ ├── Example2PresenterTest.kt │ │ └── Example2ActivityTest.kt ├── proguard-rules.pro └── build.gradle ├── settings.gradle ├── data ├── src │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── petrulak │ │ │ │ └── cleankotlin │ │ │ │ └── data │ │ │ │ ├── source │ │ │ │ ├── remote │ │ │ │ │ ├── model │ │ │ │ │ │ └── WeatherDto.kt │ │ │ │ │ ├── WeatherApiClient.kt │ │ │ │ │ ├── WeatherRemoteSource.kt │ │ │ │ │ └── RequestInterceptor.kt │ │ │ │ ├── RemoteSource.kt │ │ │ │ ├── local │ │ │ │ │ ├── model │ │ │ │ │ │ └── WeatherEntity.kt │ │ │ │ │ ├── dao │ │ │ │ │ │ ├── BaseDao.kt │ │ │ │ │ │ └── WeatherDao.kt │ │ │ │ │ ├── WeatherDatabase.kt │ │ │ │ │ └── WeatherLocalSource.kt │ │ │ │ └── LocalSource.kt │ │ │ │ ├── mapper │ │ │ │ ├── WeatherEntityMapper.kt │ │ │ │ ├── WeatherMapper.kt │ │ │ │ └── base │ │ │ │ │ └── Mapper.kt │ │ │ │ └── repository │ │ │ │ └── WeatherRepositoryImpl.kt │ │ └── res │ │ │ └── values │ │ │ └── strings.xml │ ├── androidTest │ │ └── java │ │ │ └── com │ │ │ └── petrulak │ │ │ └── cleankotlin │ │ │ └── data │ │ │ └── source │ │ │ └── local │ │ │ └── WeatherLocalSourceTest.kt │ └── test │ │ └── java │ │ └── com │ │ └── petrulak │ │ └── cleankotlin │ │ └── data │ │ └── mapper │ │ ├── WeatherEntityMapperTest.kt │ │ └── WeatherMapperTest.kt └── build.gradle ├── domain ├── src │ └── main │ │ ├── AndroidManifest.xml │ │ ├── res │ │ └── values │ │ │ └── strings.xml │ │ └── java │ │ └── com │ │ └── petrulak │ │ └── cleankotlin │ │ └── domain │ │ ├── model │ │ └── Weather.kt │ │ ├── executor │ │ ├── SchedulerProvider.kt │ │ └── SchedulerProviderImpl.kt │ │ ├── interactor │ │ ├── definition │ │ │ ├── GetWeatherUseCase.kt │ │ │ ├── GetWeatherLocallyUseCase.kt │ │ │ └── GetWeatherRemotelyUseCase.kt │ │ ├── base │ │ │ ├── SingleInteractor.kt │ │ │ ├── FlowableInteractor.kt │ │ │ ├── CompletableInteractor.kt │ │ │ └── BaseInteractor.kt │ │ ├── GetWeatherUseCaseImpl.kt │ │ ├── GetWeatherLocallyUseCaseImpl.kt │ │ └── GetWeatherRemotelyUseCaseImpl.kt │ │ └── repository │ │ └── WeatherRepository.kt └── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .gitignore ├── config.gradle ├── .travis.yml ├── gradle.properties ├── LICENSE ├── README.md ├── goodies.md └── gradlew /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':data', ':domain' 2 | -------------------------------------------------------------------------------- /data/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /domain/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /domain/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | domain 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Kotlin Clean Architecture 3 | 4 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Petrulak/android-kotlin-mvp-clean-architecture/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | .DS_Store 5 | build/ 6 | art/ 7 | gradlew.* 8 | .idea/ 9 | captures/ 10 | .externalNativeBuild 11 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Petrulak/android-kotlin-mvp-clean-architecture/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Petrulak/android-kotlin-mvp-clean-architecture/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Petrulak/android-kotlin-mvp-clean-architecture/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Petrulak/android-kotlin-mvp-clean-architecture/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Petrulak/android-kotlin-mvp-clean-architecture/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/java/com/petrulak/cleankotlin/platform/bus/data/DataBus.kt: -------------------------------------------------------------------------------- 1 | package com.petrulak.cleankotlin.platform.bus.data 2 | 3 | 4 | class DataBus { 5 | val weatherDataBus = WeatherDataBus() 6 | } -------------------------------------------------------------------------------- /domain/src/main/java/com/petrulak/cleankotlin/domain/model/Weather.kt: -------------------------------------------------------------------------------- 1 | package com.petrulak.cleankotlin.domain.model 2 | 3 | 4 | data class Weather(val id: Long, val name: String, var visibility: Int) -------------------------------------------------------------------------------- /app/src/main/java/com/petrulak/cleankotlin/ui/base/BasePresenterLifecycle.kt: -------------------------------------------------------------------------------- 1 | package com.petrulak.cleankotlin.ui.base 2 | 3 | interface BasePresenterLifecycle { 4 | 5 | fun start() 6 | 7 | fun stop() 8 | } -------------------------------------------------------------------------------- /data/src/main/java/com/petrulak/cleankotlin/data/source/remote/model/WeatherDto.kt: -------------------------------------------------------------------------------- 1 | package com.petrulak.cleankotlin.data.source.remote.model 2 | 3 | class WeatherDto(val id: Long?, val name: String?, val visibility: Int?) -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_weather.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/src/main/java/com/petrulak/cleankotlin/platform/bus/data/WeatherDataBus.kt: -------------------------------------------------------------------------------- 1 | package com.petrulak.cleankotlin.platform.bus.data 2 | 3 | import com.petrulak.cleankotlin.domain.model.Weather 4 | import com.petrulak.cleankotlin.platform.bus.BaseBus 5 | 6 | class WeatherDataBus : BaseBus() -------------------------------------------------------------------------------- /app/src/main/java/com/petrulak/cleankotlin/ui/base/BaseView.java: -------------------------------------------------------------------------------- 1 | package com.petrulak.cleankotlin.ui.base; 2 | 3 | import android.support.annotation.NonNull; 4 | 5 | public interface BaseView

> { 6 | void attachPresenter(@NonNull P presenter); 7 | } -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Oct 10 14:15:31 CEST 2017 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.1-all.zip 7 | -------------------------------------------------------------------------------- /app/src/main/java/com/petrulak/cleankotlin/ui/base/BasePresenter.java: -------------------------------------------------------------------------------- 1 | package com.petrulak.cleankotlin.ui.base; 2 | 3 | import android.support.annotation.NonNull; 4 | 5 | public interface BasePresenter extends BasePresenterLifecycle { 6 | void attachView(@NonNull V view); 7 | } -------------------------------------------------------------------------------- /domain/src/main/java/com/petrulak/cleankotlin/domain/executor/SchedulerProvider.kt: -------------------------------------------------------------------------------- 1 | package com.petrulak.cleankotlin.domain.executor 2 | 3 | 4 | import io.reactivex.Scheduler 5 | 6 | interface SchedulerProvider { 7 | fun ui(): Scheduler 8 | fun io(): Scheduler 9 | fun computation(): Scheduler 10 | } -------------------------------------------------------------------------------- /app/src/main/java/com/petrulak/cleankotlin/platform/bus/event/BaseEventBus.kt: -------------------------------------------------------------------------------- 1 | package com.petrulak.cleankotlin.platform.bus.event 2 | 3 | import com.petrulak.cleankotlin.platform.bus.BaseBus 4 | import com.petrulak.cleankotlin.platform.bus.event.events.BaseEvent 5 | 6 | 7 | open class BaseEventBus : BaseBus>() -------------------------------------------------------------------------------- /data/src/main/java/com/petrulak/cleankotlin/data/source/RemoteSource.kt: -------------------------------------------------------------------------------- 1 | package com.petrulak.cleankotlin.data.source 2 | 3 | import com.petrulak.cleankotlin.data.source.remote.model.WeatherDto 4 | import io.reactivex.Single 5 | 6 | interface RemoteSource { 7 | fun getWeatherForCity(city: String): Single 8 | } 9 | -------------------------------------------------------------------------------- /domain/src/main/java/com/petrulak/cleankotlin/domain/interactor/definition/GetWeatherUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.petrulak.cleankotlin.domain.interactor.definition 2 | 3 | import com.petrulak.cleankotlin.domain.model.Weather 4 | import io.reactivex.Flowable 5 | 6 | interface GetWeatherUseCase { 7 | 8 | fun execute(city: String): Flowable 9 | } -------------------------------------------------------------------------------- /app/src/main/java/com/petrulak/cleankotlin/ui/example1/ExampleParcelable.kt: -------------------------------------------------------------------------------- 1 | package com.petrulak.cleankotlin.ui.example1 2 | 3 | import android.annotation.SuppressLint 4 | import android.os.Parcelable 5 | import kotlinx.android.parcel.Parcelize 6 | 7 | @Parcelize 8 | @SuppressLint("ParcelCreator") 9 | class ExampleParcelable(val message: String) : Parcelable -------------------------------------------------------------------------------- /domain/src/main/java/com/petrulak/cleankotlin/domain/interactor/definition/GetWeatherLocallyUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.petrulak.cleankotlin.domain.interactor.definition 2 | 3 | import com.petrulak.cleankotlin.domain.model.Weather 4 | import io.reactivex.Flowable 5 | 6 | interface GetWeatherLocallyUseCase { 7 | 8 | fun execute(city: String): Flowable 9 | } -------------------------------------------------------------------------------- /domain/src/main/java/com/petrulak/cleankotlin/domain/interactor/definition/GetWeatherRemotelyUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.petrulak.cleankotlin.domain.interactor.definition 2 | 3 | import com.petrulak.cleankotlin.domain.model.Weather 4 | import io.reactivex.Single 5 | 6 | interface GetWeatherRemotelyUseCase { 7 | 8 | fun execute(city: String): Single 9 | } -------------------------------------------------------------------------------- /app/src/main/java/com/petrulak/cleankotlin/platform/extensions/Global.kt: -------------------------------------------------------------------------------- 1 | package com.petrulak.cleankotlin.platform.extensions 2 | 3 | /** 4 | * Since we don't need our objects to be thread safe most of the time, we can use `LazyThreadSafetyMode.NONE` which has lower 5 | * overhead. 6 | */ 7 | fun lazyFast(operation: () -> T): Lazy = lazy(LazyThreadSafetyMode.NONE) { 8 | operation() 9 | } -------------------------------------------------------------------------------- /data/src/main/java/com/petrulak/cleankotlin/data/source/local/model/WeatherEntity.kt: -------------------------------------------------------------------------------- 1 | package com.petrulak.cleankotlin.data.source.local.model 2 | 3 | import android.arch.persistence.room.Entity 4 | import android.arch.persistence.room.PrimaryKey 5 | 6 | @Entity 7 | data class WeatherEntity( 8 | @PrimaryKey() 9 | val uid: Long, 10 | val name: String, 11 | val visibility: Int 12 | ) 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/java/com/petrulak/cleankotlin/platform/bus/event/events/FragmentSyncEvent.kt: -------------------------------------------------------------------------------- 1 | package com.petrulak.cleankotlin.platform.bus.event.events 2 | 3 | import com.petrulak.cleankotlin.platform.bus.event.BaseEventBus 4 | 5 | class FragmentSyncEvent : BaseEventBus() { 6 | 7 | companion object { 8 | val ACTION_SYNC_ON = "SYNC_ON" 9 | val ACTION_SYNC_OFF = "SYNC_OFF" 10 | } 11 | } -------------------------------------------------------------------------------- /app/src/main/java/com/petrulak/cleankotlin/di/scope/ViewScope.java: -------------------------------------------------------------------------------- 1 | package com.petrulak.cleankotlin.di.scope; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.RetentionPolicy; 5 | 6 | import javax.inject.Scope; 7 | 8 | /** 9 | * A component that has this scope will exist as long as a view does. 10 | */ 11 | @Scope 12 | @Retention(RetentionPolicy.RUNTIME) 13 | public @interface ViewScope { 14 | } -------------------------------------------------------------------------------- /app/src/main/java/com/petrulak/cleankotlin/platform/analytics/AnalyticsManager.kt: -------------------------------------------------------------------------------- 1 | package com.petrulak.cleankotlin.platform.analytics 2 | 3 | interface AnalyticsManager { 4 | 5 | fun setUser(email: String) 6 | 7 | fun setCustomAttributes(attributes: HashMap) 8 | 9 | fun trackEvent(event: String) 10 | 11 | fun trackEvent(event: String, map: HashMap) 12 | 13 | fun clear() 14 | } -------------------------------------------------------------------------------- /app/src/main/java/com/petrulak/cleankotlin/platform/bus/event/EventBus.kt: -------------------------------------------------------------------------------- 1 | package com.petrulak.cleankotlin.platform.bus.event 2 | 3 | import com.petrulak.cleankotlin.platform.bus.event.events.FragmentSyncEvent 4 | import com.petrulak.cleankotlin.platform.bus.event.events.WeatherDummyEvent 5 | 6 | 7 | class EventBus { 8 | val fragmentSyncEvent = FragmentSyncEvent() 9 | val weatherDummyEvent = WeatherDummyEvent() 10 | } -------------------------------------------------------------------------------- /app/src/main/java/com/petrulak/cleankotlin/platform/bus/event/events/WeatherDummyEvent.kt: -------------------------------------------------------------------------------- 1 | package com.petrulak.cleankotlin.platform.bus.event.events 2 | 3 | import com.petrulak.cleankotlin.domain.model.Weather 4 | import com.petrulak.cleankotlin.platform.bus.event.BaseEventBus 5 | 6 | class WeatherDummyEvent : BaseEventBus() { 7 | 8 | companion object { 9 | val ACTION_HELLO = "ACTION_HELLO" 10 | } 11 | } -------------------------------------------------------------------------------- /app/src/main/java/com/petrulak/cleankotlin/ui/base/BasePresenterImpl.kt: -------------------------------------------------------------------------------- 1 | package com.petrulak.cleankotlin.ui.base 2 | 3 | import io.reactivex.disposables.CompositeDisposable 4 | 5 | 6 | open class BasePresenterImpl : BasePresenterLifecycle { 7 | 8 | val disposables = CompositeDisposable() 9 | 10 | override fun start() { 11 | } 12 | 13 | override fun stop() { 14 | disposables.clear() 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/petrulak/cleankotlin/ui/example2/ViewState.kt: -------------------------------------------------------------------------------- 1 | package com.petrulak.cleankotlin.ui.example2 2 | 3 | import com.petrulak.cleankotlin.domain.model.Weather 4 | 5 | 6 | sealed class ViewState { 7 | class Init : ViewState() 8 | class Loading : ViewState() 9 | class Success(val item: Weather) : ViewState() 10 | class LoadingFinished : ViewState() 11 | class Error(val error: Throwable) : ViewState() 12 | } -------------------------------------------------------------------------------- /data/src/main/java/com/petrulak/cleankotlin/data/source/remote/WeatherApiClient.kt: -------------------------------------------------------------------------------- 1 | package com.petrulak.cleankotlin.data.source.remote 2 | 3 | import com.petrulak.cleankotlin.data.source.remote.model.WeatherDto 4 | import io.reactivex.Single 5 | import retrofit2.http.GET 6 | import retrofit2.http.Query 7 | 8 | 9 | interface WeatherApiClient { 10 | @GET("data/2.5/weather") 11 | fun getWeatherForCity(@Query("q") city: String): Single 12 | } -------------------------------------------------------------------------------- /data/src/main/java/com/petrulak/cleankotlin/data/source/LocalSource.kt: -------------------------------------------------------------------------------- 1 | package com.petrulak.cleankotlin.data.source 2 | 3 | import com.petrulak.cleankotlin.data.source.local.model.WeatherEntity 4 | import com.petrulak.cleankotlin.domain.model.Weather 5 | import io.reactivex.Completable 6 | import io.reactivex.Flowable 7 | 8 | interface LocalSource { 9 | fun getWeatherForCity(name: String): Flowable 10 | fun save(weather: Weather): Completable 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/java/com/petrulak/cleankotlin/platform/bus/BaseBus.kt: -------------------------------------------------------------------------------- 1 | package com.petrulak.cleankotlin.platform.bus 2 | 3 | import com.jakewharton.rxrelay2.PublishRelay 4 | import io.reactivex.BackpressureStrategy 5 | import io.reactivex.Flowable 6 | 7 | 8 | open class BaseBus { 9 | 10 | protected val bus = PublishRelay.create() 11 | 12 | val flowable: Flowable get() = bus.toFlowable(BackpressureStrategy.LATEST) 13 | 14 | fun emmit(event: T) = bus.accept(event) 15 | } -------------------------------------------------------------------------------- /config.gradle: -------------------------------------------------------------------------------- 1 | ext { 2 | config = [ 3 | package : "com.petrulak.cleankotlin", 4 | buildToolsVersion : "26.0.2", 5 | compileSdkVersion : 26, 6 | minSdkVersion : 24, 7 | targetSdkVersion : 25, 8 | versionCode : 1, 9 | versionName : "1.0", 10 | testInstrumentationRunner: "com.petrulak.cleankotlin.MockAppTestRunner" 11 | ] 12 | } -------------------------------------------------------------------------------- /app/src/main/java/com/petrulak/cleankotlin/platform/bus/event/events/BaseEvent.kt: -------------------------------------------------------------------------------- 1 | package com.petrulak.cleankotlin.platform.bus.event.events 2 | 3 | open class BaseEvent { 4 | 5 | val eventType: String 6 | val payload: T? 7 | 8 | constructor(eventType: String, payload: T) { 9 | this.eventType = eventType 10 | this.payload = payload 11 | } 12 | 13 | constructor(eventType: String) { 14 | this.eventType = eventType 15 | payload = null 16 | } 17 | } -------------------------------------------------------------------------------- /data/src/main/java/com/petrulak/cleankotlin/data/source/local/dao/BaseDao.kt: -------------------------------------------------------------------------------- 1 | package com.petrulak.cleankotlin.data.source.local.dao 2 | 3 | import android.arch.persistence.room.Delete 4 | import android.arch.persistence.room.Insert 5 | import android.arch.persistence.room.Update 6 | 7 | 8 | interface BaseDao { 9 | 10 | @Insert 11 | fun insert(obj: T) 12 | 13 | @Insert 14 | fun insert(vararg obj: T) 15 | 16 | @Update 17 | fun update(obj: T) 18 | 19 | @Delete 20 | fun delete(obj: T) 21 | } -------------------------------------------------------------------------------- /data/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | data 3 | 4 | hr 5 | min 6 | hrs 7 | 8 | 9 | %s hr 10 | %s hrs 11 | 12 | 13 | 14 | 15 | %s min 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /data/src/main/java/com/petrulak/cleankotlin/data/source/local/WeatherDatabase.kt: -------------------------------------------------------------------------------- 1 | package com.petrulak.cleankotlin.data.source.local 2 | 3 | import android.arch.persistence.room.Database 4 | import android.arch.persistence.room.RoomDatabase 5 | import com.petrulak.cleankotlin.data.source.local.dao.WeatherDao 6 | import com.petrulak.cleankotlin.data.source.local.model.WeatherEntity 7 | 8 | 9 | @Database(entities = arrayOf(WeatherEntity::class), version = 1) 10 | abstract class WeatherDatabase : RoomDatabase() { 11 | abstract fun weatherDao(): WeatherDao 12 | } -------------------------------------------------------------------------------- /app/src/androidTest/java/com/petrulak/cleankotlin/di/component/ApplicationMockComponent.kt: -------------------------------------------------------------------------------- 1 | package com.petrulak.cleankotlin.di.component 2 | 3 | import com.petrulak.cleankotlin.di.module.ApplicationMockModule 4 | import com.petrulak.cleankotlin.di.module.InteractorMockModule 5 | import dagger.Component 6 | import javax.inject.Singleton 7 | 8 | @Singleton 9 | @Component( 10 | modules = arrayOf( 11 | ApplicationMockModule::class, 12 | InteractorMockModule::class 13 | ) 14 | ) 15 | interface ApplicationMockComponent : ApplicationComponent { 16 | 17 | } -------------------------------------------------------------------------------- /app/src/main/java/com/petrulak/cleankotlin/ui/example2/Example2Contract.kt: -------------------------------------------------------------------------------- 1 | package com.petrulak.cleankotlin.ui.example2 2 | 3 | import com.petrulak.cleankotlin.ui.base.BasePresenter 4 | import com.petrulak.cleankotlin.ui.base.BaseView 5 | 6 | interface Example2Contract { 7 | interface View : BaseView { 8 | override fun attachPresenter(presenter: Presenter) 9 | var viewState: ViewState 10 | } 11 | 12 | interface Presenter : BasePresenter { 13 | override fun attachView(view: View) 14 | fun refresh(city: String) 15 | } 16 | } -------------------------------------------------------------------------------- /app/src/main/java/com/petrulak/cleankotlin/platform/navigation/Navigator.kt: -------------------------------------------------------------------------------- 1 | package com.petrulak.cleankotlin.platform.navigation 2 | 3 | import android.content.Intent 4 | import android.os.Bundle 5 | import android.support.v7.app.AppCompatActivity 6 | import com.petrulak.cleankotlin.ui.base.BaseActivity 7 | 8 | class Navigator { 9 | 10 | fun navigate(source: BaseActivity, target: Class, bundle: Bundle? = null) { 11 | val intent = Intent(source, target) 12 | bundle?.let { intent.putExtras(it) } 13 | source.startActivity(intent) 14 | } 15 | } -------------------------------------------------------------------------------- /domain/src/main/java/com/petrulak/cleankotlin/domain/repository/WeatherRepository.kt: -------------------------------------------------------------------------------- 1 | package com.petrulak.cleankotlin.domain.repository 2 | 3 | import com.petrulak.cleankotlin.domain.model.Weather 4 | import io.reactivex.Completable 5 | import io.reactivex.Flowable 6 | import io.reactivex.Single 7 | 8 | interface WeatherRepository { 9 | fun getWeatherForCity(city: String): Flowable 10 | fun getWeatherForCityRemotely(city: String): Single 11 | fun getWeatherForCityLocally(name: String): Flowable 12 | fun save(weather: Weather): Completable 13 | } 14 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/petrulak/cleankotlin/MockAppTestRunner.kt: -------------------------------------------------------------------------------- 1 | package com.petrulak.cleankotlin 2 | 3 | 4 | import android.app.Application 5 | import android.content.Context 6 | import android.support.test.runner.AndroidJUnitRunner 7 | 8 | class MockAppTestRunner : AndroidJUnitRunner() { 9 | @Throws(InstantiationException::class, IllegalAccessException::class, ClassNotFoundException::class) 10 | override fun newApplication(cl: ClassLoader, className: String, context: Context): Application { 11 | return super.newApplication(cl, MockApp::class.java.name, context) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/com/petrulak/cleankotlin/platform/logging/ErrorReportingTree.kt: -------------------------------------------------------------------------------- 1 | package com.petrulak.cleankotlin.platform.logging 2 | 3 | import android.util.Log 4 | import timber.log.Timber 5 | 6 | class ErrorReportingTree : Timber.Tree() { 7 | 8 | override fun log(priority: Int, tag: String?, message: String?, t: Throwable?) { 9 | if (priority == Log.VERBOSE || priority == Log.DEBUG) { 10 | return 11 | } 12 | 13 | // Crashlytics.log(priority, tag, message) 14 | 15 | if (t != null) { 16 | // Crashlytics.logException(t) 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /domain/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'kotlin-android' 3 | 4 | def conf = rootProject.ext.config 5 | 6 | android { 7 | 8 | compileSdkVersion conf.compileSdkVersion 9 | buildToolsVersion conf.buildToolsVersion 10 | 11 | defaultConfig { 12 | minSdkVersion conf.minSdkVersion 13 | targetSdkVersion conf.targetSdkVersion 14 | versionCode conf.versionCode 15 | } 16 | } 17 | 18 | dependencies { 19 | compile Deps.rxJava 20 | compile Deps.rxAndroid 21 | compile Deps.dagger 22 | compile Deps.timber 23 | compile Deps.kotlin_stdLib 24 | } 25 | -------------------------------------------------------------------------------- /domain/src/main/java/com/petrulak/cleankotlin/domain/executor/SchedulerProviderImpl.kt: -------------------------------------------------------------------------------- 1 | package com.petrulak.cleankotlin.domain.executor 2 | 3 | import io.reactivex.Scheduler 4 | import io.reactivex.android.schedulers.AndroidSchedulers 5 | import io.reactivex.schedulers.Schedulers 6 | 7 | class SchedulerProviderImpl : SchedulerProvider { 8 | 9 | override fun ui(): Scheduler { 10 | return AndroidSchedulers.mainThread() 11 | } 12 | 13 | override fun io(): Scheduler { 14 | return Schedulers.io() 15 | } 16 | 17 | override fun computation(): Scheduler { 18 | return Schedulers.computation() 19 | } 20 | } -------------------------------------------------------------------------------- /app/src/main/java/com/petrulak/cleankotlin/ui/example1/fragment/Example1Contract.kt: -------------------------------------------------------------------------------- 1 | package com.petrulak.cleankotlin.ui.example1.fragment 2 | 3 | import com.petrulak.cleankotlin.domain.model.Weather 4 | import com.petrulak.cleankotlin.ui.base.BasePresenter 5 | import com.petrulak.cleankotlin.ui.base.BaseView 6 | 7 | interface Example1Contract { 8 | interface View : BaseView { 9 | override fun attachPresenter(presenter: Presenter) 10 | fun showWeather(data: Weather) 11 | } 12 | 13 | interface Presenter : BasePresenter { 14 | override fun attachView(view: View) 15 | fun refresh() 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/petrulak/cleankotlin/ui/example3/fragment/Example3Contract.kt: -------------------------------------------------------------------------------- 1 | package com.petrulak.cleankotlin.ui.example3.fragment 2 | 3 | import com.petrulak.cleankotlin.domain.model.Weather 4 | import com.petrulak.cleankotlin.ui.base.BasePresenter 5 | import com.petrulak.cleankotlin.ui.base.BaseView 6 | 7 | interface Example3Contract { 8 | interface View : BaseView { 9 | override fun attachPresenter(presenter: Presenter) 10 | fun showWeather(data: Weather) 11 | } 12 | 13 | interface Presenter : BasePresenter { 14 | override fun attachView(view: View) 15 | fun refresh() 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: android 3 | android: 4 | components: 5 | - tools 6 | - platform-tools 7 | 8 | # The SDK version used to compile your project 9 | - android-26 10 | 11 | # The BuildTools version used by your project 12 | - build-tools-26.0.2 13 | 14 | # Support library 15 | - extra-android-support 16 | - extra-android-m2repository 17 | - extra-google-m2repository 18 | before_script: 19 | - echo no | android create avd --force -n test -t android-26 --abi armeabi-v7a 20 | - emulator -avd test -no-skin -no-audio -no-window & 21 | - android-wait-for-emulator 22 | - adb shell input keyevent 82 & 23 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/petrulak/cleankotlin/MockApp.kt: -------------------------------------------------------------------------------- 1 | package com.petrulak.cleankotlin 2 | 3 | import com.petrulak.cleankotlin.di.component.ApplicationComponent 4 | import com.petrulak.cleankotlin.di.component.DaggerApplicationMockComponent 5 | import com.petrulak.cleankotlin.di.module.ApplicationMockModule 6 | import com.petrulak.cleankotlin.di.module.InteractorMockModule 7 | 8 | class MockApp : App() { 9 | 10 | override fun initializeAppComponent(): ApplicationComponent { 11 | return DaggerApplicationMockComponent.builder() 12 | .applicationMockModule(ApplicationMockModule(this)) 13 | .interactorMockModule(InteractorMockModule()) 14 | .build() 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/petrulak/cleankotlin/di/module/PresenterMockModule.kt: -------------------------------------------------------------------------------- 1 | package com.petrulak.cleankotlin.di.module 2 | 3 | import com.petrulak.cleankotlin.di.scope.ViewScope 4 | import com.petrulak.cleankotlin.domain.interactor.definition.GetWeatherRemotelyUseCase 5 | import com.petrulak.cleankotlin.ui.example2.Example2Contract 6 | import com.petrulak.cleankotlin.ui.example2.Example2Presenter 7 | import dagger.Module 8 | import dagger.Provides 9 | 10 | @Module 11 | class PresenterMockModule { 12 | 13 | @ViewScope 14 | @Provides 15 | internal fun example2Presenter(remotelyUseCaseGet: GetWeatherRemotelyUseCase): Example2Contract.Presenter { 16 | return Example2Presenter(remotelyUseCaseGet) 17 | } 18 | 19 | } -------------------------------------------------------------------------------- /app/src/androidTest/java/com/petrulak/cleankotlin/di/component/ViewMockComponent.kt: -------------------------------------------------------------------------------- 1 | package com.petrulak.cleankotlin.di.component 2 | 3 | import com.petrulak.cleankotlin.di.module.PresenterMockModule 4 | import com.petrulak.cleankotlin.di.scope.ViewScope 5 | import com.petrulak.cleankotlin.ui.example2.Example2ActivityTest 6 | import com.petrulak.cleankotlin.ui.example2.Example2PresenterTest 7 | import dagger.Component 8 | 9 | @ViewScope 10 | @Component( 11 | dependencies = arrayOf( 12 | ApplicationMockComponent::class 13 | ), 14 | modules = arrayOf(PresenterMockModule::class) 15 | ) 16 | interface ViewMockComponent { 17 | 18 | fun inject(item: Example2ActivityTest) 19 | fun inject(item: Example2PresenterTest) 20 | } -------------------------------------------------------------------------------- /data/src/main/java/com/petrulak/cleankotlin/data/source/local/dao/WeatherDao.kt: -------------------------------------------------------------------------------- 1 | package com.petrulak.cleankotlin.data.source.local.dao 2 | 3 | import android.arch.persistence.room.Dao 4 | import android.arch.persistence.room.Query 5 | import com.petrulak.cleankotlin.data.source.local.model.WeatherEntity 6 | import io.reactivex.Flowable 7 | 8 | @Dao 9 | interface WeatherDao : BaseDao { 10 | 11 | @Query("SELECT * FROM WeatherEntity") 12 | fun getAll(): Flowable> 13 | 14 | @Query("SELECT * FROM WeatherEntity where name = :name") 15 | fun getByName(name: String): Flowable 16 | 17 | @Query("DELETE from WeatherEntity where uid =:id") 18 | fun deleteWeatherById(id: Long): Int 19 | 20 | } -------------------------------------------------------------------------------- /data/src/main/java/com/petrulak/cleankotlin/data/source/remote/WeatherRemoteSource.kt: -------------------------------------------------------------------------------- 1 | package com.petrulak.cleankotlin.data.source.remote 2 | 3 | 4 | import com.petrulak.cleankotlin.data.source.RemoteSource 5 | import com.petrulak.cleankotlin.data.source.remote.model.WeatherDto 6 | import io.reactivex.Single 7 | import retrofit2.Retrofit 8 | import javax.inject.Inject 9 | import javax.inject.Singleton 10 | 11 | @Singleton 12 | class WeatherRemoteSource 13 | @Inject 14 | constructor(retrofit: Retrofit) : RemoteSource { 15 | 16 | private val client: WeatherApiClient = retrofit.create(WeatherApiClient::class.java) 17 | 18 | override fun getWeatherForCity(city: String): Single { 19 | return client.getWeatherForCity(city) 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | -------------------------------------------------------------------------------- /domain/src/main/java/com/petrulak/cleankotlin/domain/interactor/base/SingleInteractor.kt: -------------------------------------------------------------------------------- 1 | package com.petrulak.cleankotlin.domain.interactor.base 2 | 3 | import com.petrulak.cleankotlin.domain.executor.SchedulerProvider 4 | import io.reactivex.Single 5 | 6 | abstract class SingleInteractor( 7 | private val schedulerProvider: SchedulerProvider 8 | ) : BaseInteractor() { 9 | 10 | abstract fun buildUseCase(parameters: Parameters): Single 11 | 12 | fun execute(onNext: (T) -> Unit, onError: (Throwable) -> Unit = {}, params: Parameters) { 13 | val single = buildUseCase(params).subscribeOn(schedulerProvider.io()).observeOn(schedulerProvider.ui()) 14 | val disposable = single.subscribeWith(getDisposableSingleObserver(onNext, onError)) 15 | disposables.add(disposable) 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /domain/src/main/java/com/petrulak/cleankotlin/domain/interactor/base/FlowableInteractor.kt: -------------------------------------------------------------------------------- 1 | package com.petrulak.cleankotlin.domain.interactor.base 2 | 3 | import com.petrulak.cleankotlin.domain.executor.SchedulerProvider 4 | import io.reactivex.Flowable 5 | 6 | abstract class FlowableInteractor( 7 | private val schedulerProvider: SchedulerProvider 8 | ) : BaseInteractor() { 9 | 10 | abstract fun buildUseCase(parameters: Parameters): Flowable 11 | 12 | fun execute(onNext: (T) -> Unit, onError: (Throwable) -> Unit = {}, params: Parameters) { 13 | val flowable = buildUseCase(params).subscribeOn(schedulerProvider.io()).observeOn(schedulerProvider.ui()) 14 | val disposable = flowable.subscribeWith(getDisposableSubscriber(onNext, onError)) 15 | disposables.add(disposable) 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 |