Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conductor integration, and Kotlin support #58

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
File renamed without changes.
54 changes: 54 additions & 0 deletions android-mvvm-conductor/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'

buildscript {
ext.kotlin_version = '1.1.2-2'
ext.moduleArtifact = 'conductor'
repositories {
jcenter()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}

android {
compileSdkVersion 25
buildToolsVersion "25.0.3"

defaultConfig {
minSdkVersion 16
targetSdkVersion 25
versionCode 1
versionName "1.0"

testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}

dataBinding {
enabled = true
}

buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}

dependencies {
provided project(path: ':android-mvvm-core')
provided project(path: ':android-mvvm-kotlin')

testCompile 'junit:junit:4.12'

compile 'com.bluelinelabs:conductor:2.1.2'
compile 'com.bluelinelabs:conductor-rxlifecycle2:2.1.2'
compile 'com.trello.rxlifecycle2:rxlifecycle-kotlin:2.0.1'
compile "com.android.support:support-v4:$support_library_version"
provided "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"

}

apply from: rootProject.file('gradle/bintray-push.gradle')
25 changes: 25 additions & 0 deletions android-mvvm-conductor/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/gabor/Library/Android/sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# Add any project specific keep options here:

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
1 change: 1 addition & 0 deletions android-mvvm-conductor/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<manifest package="com.manaschaudhari.android_mvvm"/>
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.manaschaudhari.android_mvvm.conductor

import com.bluelinelabs.conductor.rxlifecycle2.ControllerEvent
import com.trello.rxlifecycle2.LifecycleProvider
import com.trello.rxlifecycle2.kotlin.bindUntilEvent
import io.reactivex.BackpressureStrategy
import io.reactivex.Completable
import io.reactivex.Flowable
import io.reactivex.Observable

/**
* Created by gabor on 2017. 05. 01..
*/

typealias ControllerLifecycleProvider = LifecycleProvider<ControllerEvent>

/**
* Convenience function for [Observable]s to bind to a controller's lifecycle. This makes it possible
* to terminate a stream when the specified [end] controller event occurs. Also, this function takes care
* of resubscribing to the Observable when the specified [start] lifecycle event occurs.
*
* @param lp The lifecycle provider instance, usually the Controller itself (in case of an [RxController]).
*/
fun <E> Observable<E>.attachToLifecycle(lp: ControllerLifecycleProvider, start: ControllerEvent = ControllerEvent.ATTACH, end: ControllerEvent = ControllerEvent.DETACH): Observable<E> = lp.lifecycle()
.filter { it == start }
.switchMap { this.bindUntilEvent(lp, end) }
.bindUntilEvent(lp, ControllerEvent.DESTROY)

/*fun <E> Single<E>.attachToLifecycle(lp: ControllerLifecycleProvider, start: ControllerEvent = ControllerEvent.ATTACH, end: ControllerEvent = ControllerEvent.DETACH): Single<E> = lp.lifecycle()
.filter { it == start }
.flatMapSingle { this.bindUntilEvent(lp, end) }
.bindUntilEvent(lp, ControllerEvent.DESTROY)*/

fun <E> Flowable<E>.attachToLifecycle(lp: ControllerLifecycleProvider, start: ControllerEvent = ControllerEvent.ATTACH, end: ControllerEvent = ControllerEvent.DETACH): Flowable<E> = lp.lifecycle()
.toFlowable(BackpressureStrategy.LATEST)
.filter { it == start }
.switchMap { this.bindUntilEvent(lp, end) }
.bindUntilEvent(lp, ControllerEvent.DESTROY)

fun Completable.attachToLifecycle(lp: ControllerLifecycleProvider, start: ControllerEvent = ControllerEvent.ATTACH, end: ControllerEvent = ControllerEvent.DETACH): Completable = lp.lifecycle()
.filter { it == start }
.flatMapCompletable { this.bindUntilEvent(lp, end) }
.bindUntilEvent(lp, ControllerEvent.DESTROY)
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package com.manaschaudhari.android_mvvm.conductor

import android.databinding.DataBindingUtil
import android.databinding.ViewDataBinding
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.bluelinelabs.conductor.Controller
import com.bluelinelabs.conductor.rxlifecycle2.RxController
import com.manaschaudhari.android_mvvm.adapters.ViewModelBinder
import com.manaschaudhari.android_mvvm.utils.BindingUtils
import com.trello.rxlifecycle2.internal.Preconditions

/**
* A [Controller] that uses DataBinding to inflate a layout, and asks implementations
* to create a [ViewModel] instance when necessary. This class is designed to integrate
* with Conductor, and to take care of retaining the ViewModel across orientation changes.
*
* With that, it is possible for ViewModels to hold a reference to the [Controller] (either
* directly, or indirectly), and not cause any memory leaks. This is possible
* because the Controller will be retained across orientation changes as well.
*
* Created by Gabor Szanto on 2017. 04. 29..
*/
abstract class MvvmController<Binding : ViewDataBinding, ViewModel : com.manaschaudhari.android_mvvm.ViewModel> : RxController {
constructor() : super()
constructor(args: Bundle?) : super(args)

/**
* The [ViewModel] instance that will be held by implementations. This instance will be
* constructed once, and persisted across orientation changes.
*/
abstract var viewModel: ViewModel

/**
* The generic [Binding] instance. This will be recreated whenever the Controller decides
* to recreate its View.
*/
lateinit var binding: Binding

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup): View {
val vm = this::class.java.getDeclaredField("viewModel")
vm.isAccessible = true
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why use reflection?

if (vm.get(this) == null)
createViewModel()

binding = DataBindingUtil.inflate(inflater, getLayoutId(), container, false)
return binding.root
}

override fun onDestroyView(view: View) {
val binding = this::class.java.getField("binding")
binding.isAccessible = true
binding.set(this, null)
super.onDestroyView(view)
}

private val defaultBinder: com.manaschaudhari.android_mvvm.adapters.ViewModelBinder
get() {
val defaultBinder = BindingUtils.getDefaultBinder()
Preconditions.checkNotNull<ViewModelBinder>(defaultBinder, "Default Binder")
return defaultBinder!!
}

override fun onAttach(view: View) {
super.onAttach(view)
defaultBinder.bind(binding, viewModel)
}

override fun onDetach(view: View) {
defaultBinder.bind(binding, null)
binding.executePendingBindings()
super.onDetach(view)
}

/**
* The [Controller] instance is about to be destroyed, so we need to clear out the
* reference to the [ViewModel].
*
* Note: we need to use reflection here to keep Kotlin happy about the nullability. :)
*/
override fun onDestroy() {
val viewModel = this::class.java.getField("viewModel")
viewModel.isAccessible = true
viewModel.set(this, null)
super.onDestroy()
}

/**
* Implementations should return the ID of the layout to be inflated.
*/
abstract fun getLayoutId(): Int

/**
* Implementations should create the ViewModel in this method, either manually, or
* by using dependency injection (e.g. Dagger 2)
*/
abstract fun createViewModel()
}
1 change: 1 addition & 0 deletions android-mvvm-core/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
30 changes: 20 additions & 10 deletions android-mvvm/build.gradle → android-mvvm-core/build.gradle
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
apply plugin: 'com.android.library'

def isCi() {
buildscript {
ext.moduleArtifact = 'core'
}

static def isCi() {
return "true".equals(System.getenv('CI'))
}

android {
compileSdkVersion 24
buildToolsVersion "24.0.0"
compileSdkVersion 25
buildToolsVersion "25.0.3"

defaultConfig {
minSdkVersion 9
targetSdkVersion 24
minSdkVersion 16
targetSdkVersion 25
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
Expand Down Expand Up @@ -60,13 +64,19 @@ if (isCi()) {
}

dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile "io.reactivex.rxjava2:rxjava:$rx_version"

compile "com.android.support:recyclerview-v7:$support_library_version"
compile "com.android.support:support-v4:$support_library_version"

androidTestCompile 'com.android.support.test:runner:0.5'
androidTestCompile 'com.android.support.test:rules:0.5'
compile 'io.reactivex:rxjava:1.1.7'
compile 'com.android.support:recyclerview-v7:24.2.1'
compile 'com.android.support:appcompat-v7:24.2.1'
androidTestCompile "com.android.support:recyclerview-v7:$support_library_version"
}

apply from: rootProject.file('gradle/bintray-push.gradle')
apply from: rootProject.file('gradle/bintray-push.gradle')
repositories {
mavenCentral()
jcenter()
}
Binary file not shown.
6 changes: 6 additions & 0 deletions android-mvvm-core/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#Mon Dec 28 10:00:20 PST 2015
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
Loading