├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── manijshrestha │ │ └── todolist │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── manijshrestha │ │ │ └── todolist │ │ │ ├── ToDoApplication.kt │ │ │ ├── data │ │ │ ├── AppDatabase.kt │ │ │ ├── Task.kt │ │ │ └── TaskDao.kt │ │ │ ├── di │ │ │ ├── AppComponent.kt │ │ │ ├── AppModule.kt │ │ │ ├── ToDoModule.kt │ │ │ └── ToDoSubComponent.kt │ │ │ └── ui │ │ │ ├── TaskAdapter.kt │ │ │ ├── ToDoActivity.kt │ │ │ ├── ToDoPresentation.kt │ │ │ └── ToDoPresenter.kt │ └── res │ │ ├── layout │ │ ├── activity_to_do.xml │ │ └── item_to_do.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── manijshrestha │ └── todolist │ └── ExampleUnitTest.kt ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/** 5 | .DS_Store 6 | /build 7 | /captures 8 | .externalNativeBuild 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ToDoList 2 | This is a sample project showcasing Room, Dagger2, RxJava/RxAndroid in a Kotlin Android project. 3 | You can follow along at https://manijshrestha.wordpress.com/2017/06/03/using-room-with-kotlin/ 4 | 5 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | apply plugin: 'kotlin-android' 4 | apply plugin: 'kotlin-kapt' 5 | 6 | android { 7 | compileSdkVersion 25 8 | buildToolsVersion "25.0.3" 9 | defaultConfig { 10 | applicationId "com.manijshrestha.todolist" 11 | minSdkVersion 21 12 | targetSdkVersion 25 13 | versionCode 1 14 | versionName "1.0" 15 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 16 | } 17 | buildTypes { 18 | release { 19 | minifyEnabled false 20 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | } 24 | 25 | dependencies { 26 | compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version" 27 | compile "com.android.support:appcompat-v7:$rootProject.versions.support_lib" 28 | compile "com.android.support:cardview-v7:$rootProject.versions.support_lib" 29 | compile "com.android.support:recyclerview-v7:$rootProject.versions.support_lib" 30 | compile "com.android.support.constraint:constraint-layout:1.0.2" 31 | 32 | compile "com.google.dagger:dagger:$rootProject.versions.dagger" 33 | compile "com.google.dagger:dagger-android:$rootProject.versions.dagger" 34 | compile "com.google.dagger:dagger-android-support:$rootProject.versions.dagger" 35 | kapt "com.google.dagger:dagger-android-processor:$rootProject.versions.dagger" 36 | kapt "com.google.dagger:dagger-compiler:$rootProject.versions.dagger" 37 | 38 | compile "io.reactivex.rxjava2:rxjava:2.1.0" 39 | compile "io.reactivex.rxjava2:rxandroid:2.0.1" 40 | 41 | //Room 42 | compile "android.arch.persistence.room:runtime:$rootProject.versions.arch_comp" 43 | compile "android.arch.persistence.room:rxjava2:$rootProject.versions.arch_comp" 44 | kapt "android.arch.persistence.room:compiler:$rootProject.versions.arch_comp" 45 | 46 | testCompile 'junit:junit:4.12' 47 | 48 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 49 | exclude group: 'com.android.support', module: 'support-annotations' 50 | }) 51 | } 52 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/manijshrestha/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/manijshrestha/todolist/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.manijshrestha.todolist 2 | 3 | import android.support.test.InstrumentationRegistry 4 | import android.support.test.runner.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumentation test, which will execute on an Android device. 13 | * 14 | * @see [Testing documentation](http://d.android.com/tools/testing) 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | @Throws(Exception::class) 20 | fun useAppContext() { 21 | // Context of the app under test. 22 | val appContext = InstrumentationRegistry.getTargetContext() 23 | assertEquals("com.manijshrestha.todolist", appContext.packageName) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/java/com/manijshrestha/todolist/ToDoApplication.kt: -------------------------------------------------------------------------------- 1 | package com.manijshrestha.todolist 2 | 3 | import android.app.Activity 4 | import android.app.Application 5 | import com.manijshrestha.todolist.di.AppComponent 6 | import com.manijshrestha.todolist.di.AppModule 7 | import com.manijshrestha.todolist.di.DaggerAppComponent 8 | import dagger.android.AndroidInjector 9 | import dagger.android.DispatchingAndroidInjector 10 | import dagger.android.HasActivityInjector 11 | import javax.inject.Inject 12 | 13 | class ToDoApplication : Application(), HasActivityInjector { 14 | 15 | @Inject lateinit var activityInjector: DispatchingAndroidInjector 16 | 17 | lateinit var appComponent: AppComponent 18 | 19 | override fun onCreate() { 20 | super.onCreate() 21 | 22 | appComponent = DaggerAppComponent.builder().appModule(AppModule(applicationContext)).build() 23 | 24 | appComponent.inject(this) 25 | } 26 | 27 | override fun activityInjector(): AndroidInjector = activityInjector 28 | 29 | } -------------------------------------------------------------------------------- /app/src/main/java/com/manijshrestha/todolist/data/AppDatabase.kt: -------------------------------------------------------------------------------- 1 | package com.manijshrestha.todolist.data 2 | 3 | import android.arch.persistence.room.Database 4 | import android.arch.persistence.room.RoomDatabase 5 | 6 | @Database(entities = arrayOf(Task::class), version = 1, exportSchema = false) 7 | abstract class AppDatabase : RoomDatabase() { 8 | 9 | abstract fun taskDao(): TaskDao 10 | } -------------------------------------------------------------------------------- /app/src/main/java/com/manijshrestha/todolist/data/Task.kt: -------------------------------------------------------------------------------- 1 | package com.manijshrestha.todolist.data 2 | 3 | import android.arch.persistence.room.ColumnInfo 4 | import android.arch.persistence.room.Entity 5 | import android.arch.persistence.room.PrimaryKey 6 | 7 | @Entity(tableName = "task") 8 | data class Task(@ColumnInfo(name = "completed_flag") var completed: Boolean = false, 9 | @ColumnInfo(name = "task_desciption") var description: String) { 10 | @ColumnInfo(name = "id") 11 | @PrimaryKey(autoGenerate = true) var id: Long = 0 12 | } -------------------------------------------------------------------------------- /app/src/main/java/com/manijshrestha/todolist/data/TaskDao.kt: -------------------------------------------------------------------------------- 1 | package com.manijshrestha.todolist.data 2 | 3 | import android.arch.persistence.room.* 4 | import android.arch.persistence.room.OnConflictStrategy.REPLACE 5 | import io.reactivex.Flowable 6 | 7 | @Dao interface TaskDao { 8 | 9 | @Query("select * from task") 10 | fun getAllTasks(): Flowable> 11 | 12 | @Query("select * from task where id = :p0") 13 | fun findTaskById(id: Long): Task 14 | 15 | @Insert(onConflict = REPLACE) 16 | fun insertTask(task: Task) 17 | 18 | @Update(onConflict = REPLACE) 19 | fun updateTask(task: Task) 20 | 21 | @Delete 22 | fun deleteTask(task: Task) 23 | } -------------------------------------------------------------------------------- /app/src/main/java/com/manijshrestha/todolist/di/AppComponent.kt: -------------------------------------------------------------------------------- 1 | package com.manijshrestha.todolist.di 2 | 3 | import com.manijshrestha.todolist.ToDoApplication 4 | import dagger.Component 5 | 6 | @Component(modules = arrayOf(AppModule::class, 7 | ToDoModule::class)) 8 | interface AppComponent { 9 | 10 | fun inject(application: ToDoApplication) 11 | } -------------------------------------------------------------------------------- /app/src/main/java/com/manijshrestha/todolist/di/AppModule.kt: -------------------------------------------------------------------------------- 1 | package com.manijshrestha.todolist.di 2 | 3 | import android.arch.persistence.room.Room 4 | import android.content.Context 5 | import com.manijshrestha.todolist.data.AppDatabase 6 | import dagger.Module 7 | import dagger.Provides 8 | 9 | @Module class AppModule(private val context: Context) { 10 | 11 | @Provides fun providesAppContext() = context 12 | 13 | @Provides fun providesAppDatabase(context: Context): AppDatabase = 14 | Room.databaseBuilder(context, AppDatabase::class.java, "my-todo-db").build() 15 | 16 | @Provides fun providesToDoDao(database: AppDatabase) = database.taskDao() 17 | } -------------------------------------------------------------------------------- /app/src/main/java/com/manijshrestha/todolist/di/ToDoModule.kt: -------------------------------------------------------------------------------- 1 | package com.manijshrestha.todolist.di 2 | 3 | import android.app.Activity 4 | import com.manijshrestha.todolist.ui.ToDoActivity 5 | import dagger.Binds 6 | import dagger.Module 7 | import dagger.android.ActivityKey 8 | import dagger.android.AndroidInjector 9 | import dagger.multibindings.IntoMap 10 | 11 | @Module(subcomponents = arrayOf(ToDoSubComponent::class)) 12 | abstract class ToDoModule { 13 | 14 | @Binds 15 | @IntoMap 16 | @ActivityKey(ToDoActivity::class) 17 | internal abstract fun bindsToDoActivityInjectorFactory(builder: ToDoSubComponent.Builder): AndroidInjector.Factory 18 | } -------------------------------------------------------------------------------- /app/src/main/java/com/manijshrestha/todolist/di/ToDoSubComponent.kt: -------------------------------------------------------------------------------- 1 | package com.manijshrestha.todolist.di 2 | 3 | import com.manijshrestha.todolist.ui.ToDoActivity 4 | import dagger.Subcomponent 5 | import dagger.android.AndroidInjector 6 | 7 | @Subcomponent interface ToDoSubComponent : AndroidInjector { 8 | 9 | @Subcomponent.Builder abstract class Builder : AndroidInjector.Builder() 10 | } -------------------------------------------------------------------------------- /app/src/main/java/com/manijshrestha/todolist/ui/TaskAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.manijshrestha.todolist.ui 2 | 3 | import com.manijshrestha.todolist.data.Task 4 | 5 | class TaskAdapter(var tasks: List) : android.support.v7.widget.RecyclerView.Adapter() { 6 | 7 | override fun onCreateViewHolder(parent: android.view.ViewGroup, type: Int): com.manijshrestha.todolist.ui.TaskAdapter.TaskViewHolder { 8 | return TaskViewHolder(parent) 9 | } 10 | 11 | override fun onBindViewHolder(viewHolder: com.manijshrestha.todolist.ui.TaskAdapter.TaskViewHolder, position: Int) { 12 | viewHolder.bind(tasks[position]) 13 | } 14 | 15 | override fun getItemCount(): Int = tasks.size 16 | 17 | inner class TaskViewHolder(parent: android.view.ViewGroup) : android.support.v7.widget.RecyclerView.ViewHolder(android.view.LayoutInflater.from(parent.context).inflate(com.manijshrestha.todolist.R.layout.item_to_do, parent, false)) { 18 | 19 | fun bind(task: Task) = with(itemView) { 20 | val taskCb = findViewById(com.manijshrestha.todolist.R.id.task_cb) as android.widget.CheckBox 21 | taskCb.text = task.description 22 | taskCb.isChecked = task.completed 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /app/src/main/java/com/manijshrestha/todolist/ui/ToDoActivity.kt: -------------------------------------------------------------------------------- 1 | package com.manijshrestha.todolist.ui 2 | 3 | import android.os.Bundle 4 | import android.support.v7.app.AppCompatActivity 5 | import android.support.v7.widget.LinearLayoutManager 6 | import android.support.v7.widget.RecyclerView 7 | import android.widget.Button 8 | import android.widget.EditText 9 | import com.manijshrestha.todolist.R 10 | import com.manijshrestha.todolist.data.Task 11 | import dagger.android.AndroidInjection 12 | import javax.inject.Inject 13 | 14 | class ToDoActivity : AppCompatActivity(), ToDoPresentation { 15 | 16 | @Inject lateinit var presenter: ToDoPresenter 17 | 18 | var taskET: EditText? = null 19 | var addBtn: Button? = null 20 | var recyclerView: RecyclerView? = null 21 | 22 | override fun onCreate(savedInstanceState: Bundle?) { 23 | AndroidInjection.inject(this) 24 | super.onCreate(savedInstanceState) 25 | setContentView(R.layout.activity_to_do) 26 | taskET = findViewById(R.id.task_et) as EditText 27 | addBtn = findViewById(R.id.add_btn) as Button 28 | recyclerView = findViewById(R.id.tasks_rv) as RecyclerView 29 | recyclerView?.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false) 30 | recyclerView?.adapter = TaskAdapter(emptyList()) 31 | 32 | addBtn?.setOnClickListener({ presenter.addNewTask(taskET?.text.toString()) }) 33 | 34 | presenter.onCreate(this) 35 | } 36 | 37 | override fun onDestroy() { 38 | presenter.onDestroy() 39 | super.onDestroy() 40 | } 41 | 42 | override fun showTasks(tasks: List) { 43 | recyclerView?.adapter = TaskAdapter(tasks) 44 | } 45 | 46 | override fun taskAddedAt(position: Int) { 47 | recyclerView?.adapter?.notifyItemInserted(position) 48 | } 49 | 50 | override fun scrollTo(position: Int) { 51 | recyclerView?.smoothScrollToPosition(position) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /app/src/main/java/com/manijshrestha/todolist/ui/ToDoPresentation.kt: -------------------------------------------------------------------------------- 1 | package com.manijshrestha.todolist.ui 2 | 3 | import com.manijshrestha.todolist.data.Task 4 | 5 | interface ToDoPresentation { 6 | 7 | fun showTasks(tasks: List) 8 | 9 | fun taskAddedAt(position: Int) 10 | 11 | fun scrollTo(position: Int) 12 | } -------------------------------------------------------------------------------- /app/src/main/java/com/manijshrestha/todolist/ui/ToDoPresenter.kt: -------------------------------------------------------------------------------- 1 | package com.manijshrestha.todolist.ui 2 | 3 | import com.manijshrestha.todolist.data.Task 4 | import com.manijshrestha.todolist.data.TaskDao 5 | import io.reactivex.Observable 6 | import io.reactivex.android.schedulers.AndroidSchedulers 7 | import io.reactivex.disposables.CompositeDisposable 8 | import io.reactivex.schedulers.Schedulers 9 | import javax.inject.Inject 10 | 11 | class ToDoPresenter @Inject constructor(val taskDao: TaskDao) { 12 | 13 | val compositeDisposable = CompositeDisposable() 14 | var tasks = ArrayList() 15 | 16 | var presentation: ToDoPresentation? = null 17 | 18 | fun onCreate(toDoPresentation: ToDoPresentation) { 19 | presentation = toDoPresentation 20 | loadTasks() 21 | } 22 | 23 | fun onDestroy() { 24 | compositeDisposable.dispose() 25 | presentation = null 26 | } 27 | 28 | fun loadTasks() { 29 | compositeDisposable.add(taskDao.getAllTasks() 30 | .subscribeOn(Schedulers.io()) 31 | .observeOn(AndroidSchedulers.mainThread()) 32 | .subscribe({ 33 | tasks.clear() 34 | tasks.addAll(it) 35 | (tasks.size - 1).takeIf { it >= 0 }?.let { 36 | presentation?.taskAddedAt(it) 37 | presentation?.scrollTo(it) 38 | } 39 | })) 40 | 41 | presentation?.showTasks(tasks) 42 | } 43 | 44 | fun addNewTask(taskDescription: String) { 45 | val newTask = Task(description = taskDescription) 46 | compositeDisposable.add(Observable.fromCallable { taskDao.insertTask(newTask) } 47 | .subscribeOn(Schedulers.io()) 48 | .observeOn(AndroidSchedulers.mainThread()) 49 | .subscribe()) 50 | } 51 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_to_do.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 19 | 20 | 24 | 25 | 32 | 33 |