mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2024-11-22 01:15:54 +03:00
Introduce epoxy in demo app
This commit is contained in:
parent
e323c858ab
commit
06a5253fd9
14 changed files with 239 additions and 44 deletions
Binary file not shown.
|
@ -1,6 +1,11 @@
|
||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
apply plugin: 'kotlin-android'
|
apply plugin: 'kotlin-android'
|
||||||
apply plugin: 'kotlin-android-extensions'
|
apply plugin: 'kotlin-android-extensions'
|
||||||
|
apply plugin: 'kotlin-kapt'
|
||||||
|
|
||||||
|
kapt {
|
||||||
|
correctErrorTypes = true
|
||||||
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 28
|
compileSdkVersion 28
|
||||||
|
@ -20,7 +25,20 @@ android {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
configurations.all { strategy ->
|
||||||
|
strategy.resolutionStrategy.eachDependency { details ->
|
||||||
|
if (details.requested.group == 'com.android.support') {
|
||||||
|
details.useVersion "28.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|
||||||
|
def epoxy_version = "2.19.0"
|
||||||
|
|
||||||
|
|
||||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
implementation project(":matrix-sdk-android")
|
implementation project(":matrix-sdk-android")
|
||||||
|
|
||||||
|
@ -30,6 +48,9 @@ dependencies {
|
||||||
|
|
||||||
implementation 'com.jakewharton.timber:timber:4.7.1'
|
implementation 'com.jakewharton.timber:timber:4.7.1'
|
||||||
|
|
||||||
|
implementation("com.airbnb.android:epoxy:$epoxy_version")
|
||||||
|
kapt "com.airbnb.android:epoxy-processor:$epoxy_version"
|
||||||
|
|
||||||
implementation "org.koin:koin-android:$koin_version"
|
implementation "org.koin:koin-android:$koin_version"
|
||||||
implementation "org.koin:koin-android-scope:$koin_version"
|
implementation "org.koin:koin-android-scope:$koin_version"
|
||||||
implementation "org.koin:koin-android-viewmodel:$koin_version"
|
implementation "org.koin:koin-android-viewmodel:$koin_version"
|
||||||
|
@ -38,3 +59,5 @@ dependencies {
|
||||||
androidTestImplementation 'com.android.support.test:runner:1.0.2'
|
androidTestImplementation 'com.android.support.test:runner:1.0.2'
|
||||||
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
|
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
package im.vector.riotredesign.core.helpers
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
import com.airbnb.epoxy.EpoxyHolder
|
||||||
|
import kotlin.properties.ReadOnlyProperty
|
||||||
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A pattern for easier view binding with an [EpoxyHolder]
|
||||||
|
*
|
||||||
|
* See [SampleKotlinModelWithHolder] for a usage example.
|
||||||
|
*/
|
||||||
|
abstract class KotlinEpoxyHolder : EpoxyHolder() {
|
||||||
|
private lateinit var view: View
|
||||||
|
|
||||||
|
override fun bindView(itemView: View) {
|
||||||
|
view = itemView
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun <V : View> bind(id: Int): ReadOnlyProperty<KotlinEpoxyHolder, V> =
|
||||||
|
Lazy { holder: KotlinEpoxyHolder, prop ->
|
||||||
|
holder.view.findViewById(id) as V?
|
||||||
|
?: throw IllegalStateException("View ID $id for '${prop.name}' not found.")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Taken from Kotterknife.
|
||||||
|
* https://github.com/JakeWharton/kotterknife
|
||||||
|
*/
|
||||||
|
private class Lazy<V>(
|
||||||
|
private val initializer: (KotlinEpoxyHolder, KProperty<*>) -> V
|
||||||
|
) : ReadOnlyProperty<KotlinEpoxyHolder, V> {
|
||||||
|
private object EMPTY
|
||||||
|
|
||||||
|
private var value: Any? = EMPTY
|
||||||
|
|
||||||
|
override fun getValue(thisRef: KotlinEpoxyHolder, property: KProperty<*>): V {
|
||||||
|
if (value == EMPTY) {
|
||||||
|
value = initializer(thisRef, property)
|
||||||
|
}
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
return value as V
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
package im.vector.riotredesign.core.helpers
|
||||||
|
|
||||||
|
import android.support.annotation.IdRes
|
||||||
|
import android.support.annotation.LayoutRes
|
||||||
|
import android.view.View
|
||||||
|
import com.airbnb.epoxy.EpoxyModel
|
||||||
|
import kotlin.properties.ReadOnlyProperty
|
||||||
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
|
abstract class KotlinModel(
|
||||||
|
@LayoutRes private val layoutRes: Int
|
||||||
|
) : EpoxyModel<View>() {
|
||||||
|
|
||||||
|
private var view: View? = null
|
||||||
|
|
||||||
|
abstract fun bind()
|
||||||
|
|
||||||
|
override fun bind(view: View) {
|
||||||
|
this.view = view
|
||||||
|
bind()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun unbind(view: View) {
|
||||||
|
this.view = null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getDefaultLayout() = layoutRes
|
||||||
|
|
||||||
|
protected fun <V : View> bind(@IdRes id: Int) = object : ReadOnlyProperty<KotlinModel, V> {
|
||||||
|
override fun getValue(thisRef: KotlinModel, property: KProperty<*>): V {
|
||||||
|
// This is not efficient because it looks up the view by id every time (it loses
|
||||||
|
// the pattern of a "holder" to cache that look up). But it is simple to use and could
|
||||||
|
// be optimized with a map
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
return view?.findViewById(id) as V?
|
||||||
|
?: throw IllegalStateException("View ID $id for '${property.name}' not found.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,5 +2,5 @@ package im.vector.riotredesign.core.platform
|
||||||
|
|
||||||
import android.support.v4.app.Fragment
|
import android.support.v4.app.Fragment
|
||||||
|
|
||||||
class RiotFragment : Fragment() {
|
open class RiotFragment : Fragment() {
|
||||||
}
|
}
|
|
@ -1,31 +1,24 @@
|
||||||
package im.vector.riotredesign.features.home
|
package im.vector.riotredesign.features.home
|
||||||
|
|
||||||
import android.arch.lifecycle.Observer
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import im.vector.matrix.android.api.Matrix
|
|
||||||
import im.vector.matrix.android.api.session.room.Room
|
|
||||||
import im.vector.riotredesign.R
|
import im.vector.riotredesign.R
|
||||||
import im.vector.riotredesign.core.platform.RiotActivity
|
import im.vector.riotredesign.core.platform.RiotActivity
|
||||||
import org.koin.android.ext.android.inject
|
|
||||||
import timber.log.Timber
|
|
||||||
|
|
||||||
|
|
||||||
class HomeActivity : RiotActivity() {
|
class HomeActivity : RiotActivity() {
|
||||||
|
|
||||||
private val matrix by inject<Matrix>()
|
|
||||||
private val currentSession = matrix.currentSession!!
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_home)
|
setContentView(R.layout.activity_home)
|
||||||
currentSession.rooms().observe(this, Observer<List<Room>> { roomList ->
|
|
||||||
if (roomList == null) {
|
if (savedInstanceState == null) {
|
||||||
return@Observer
|
val roomListFragment = RoomListFragment.newInstance()
|
||||||
}
|
val ft = supportFragmentManager.beginTransaction()
|
||||||
Timber.v("Observe rooms: %d", roomList.size)
|
ft.replace(R.id.homeFragmentContainer, roomListFragment)
|
||||||
})
|
ft.commit()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
package im.vector.riotredesign.features.home
|
||||||
|
|
||||||
|
import com.airbnb.epoxy.TypedEpoxyController
|
||||||
|
import im.vector.matrix.android.api.session.room.Room
|
||||||
|
|
||||||
|
class RoomController(private val callback: Callback? = null) : TypedEpoxyController<List<Room>>() {
|
||||||
|
|
||||||
|
override fun buildModels(data: List<Room>?) {
|
||||||
|
data?.forEach {
|
||||||
|
RoomItem(it.roomId, listener = { callback?.onRoomSelected(it) })
|
||||||
|
.id(it.roomId)
|
||||||
|
.addTo(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Callback {
|
||||||
|
fun onRoomSelected(room: Room)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package im.vector.riotredesign.features.home
|
||||||
|
|
||||||
|
import android.widget.TextView
|
||||||
|
import im.vector.riotredesign.R
|
||||||
|
import im.vector.riotredesign.core.helpers.KotlinModel
|
||||||
|
|
||||||
|
data class RoomItem(
|
||||||
|
val title: String,
|
||||||
|
val listener: (() -> Unit)? = null
|
||||||
|
) : KotlinModel(R.layout.item_room) {
|
||||||
|
|
||||||
|
val titleView by bind<TextView>(R.id.titleView)
|
||||||
|
|
||||||
|
override fun bind() {
|
||||||
|
titleView.setOnClickListener { listener?.invoke() }
|
||||||
|
titleView.text = title
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
package im.vector.riotredesign.features.home
|
||||||
|
|
||||||
|
import android.arch.lifecycle.Observer
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.Toast
|
||||||
|
import im.vector.matrix.android.api.Matrix
|
||||||
|
import im.vector.matrix.android.api.session.room.Room
|
||||||
|
import im.vector.riotredesign.R
|
||||||
|
import im.vector.riotredesign.core.platform.RiotFragment
|
||||||
|
import kotlinx.android.synthetic.main.fragment_room_list.*
|
||||||
|
import org.koin.android.ext.android.inject
|
||||||
|
|
||||||
|
class RoomListFragment : RiotFragment(), RoomController.Callback {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
fun newInstance(): RoomListFragment {
|
||||||
|
return RoomListFragment()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private val matrix by inject<Matrix>()
|
||||||
|
private val currentSession = matrix.currentSession!!
|
||||||
|
private val roomController = RoomController(this)
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
|
return inflater.inflate(R.layout.fragment_room_list, container, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||||
|
super.onActivityCreated(savedInstanceState)
|
||||||
|
epoxyRecyclerView.setController(roomController)
|
||||||
|
currentSession.rooms().observe(this, Observer<List<Room>> { renderRooms(it) })
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun renderRooms(rooms: List<Room>?) {
|
||||||
|
roomController.setData(rooms)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onRoomSelected(room: Room) {
|
||||||
|
Toast.makeText(context, "Room ${room.roomId} clicked", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -1,40 +1,15 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
tools:context=".features.login.LoginActivity">
|
tools:context=".features.login.LoginActivity">
|
||||||
|
|
||||||
<Button
|
<FrameLayout
|
||||||
android:id="@+id/startSyncButton"
|
android:id="@+id/homeFragmentContainer"
|
||||||
android:layout_width="0dp"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp"
|
android:layout_height="match_parent" />
|
||||||
android:layout_marginBottom="8dp"
|
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:layout_marginStart="8dp"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:text="Start sync"
|
|
||||||
app:layout_constraintBottom_toTopOf="@+id/stopSyncButton"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/stopSyncButton"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:layout_marginBottom="8dp"
|
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:layout_marginStart="8dp"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:text="stop sync"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintHorizontal_bias="0.5"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/startSyncButton" />
|
|
||||||
|
|
||||||
|
|
||||||
</android.support.constraint.ConstraintLayout>
|
</android.support.constraint.ConstraintLayout>
|
6
app/src/main/res/layout/fragment_room_detail.xml
Normal file
6
app/src/main/res/layout/fragment_room_detail.xml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<android.support.constraint.ConstraintLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
</android.support.constraint.ConstraintLayout>
|
11
app/src/main/res/layout/fragment_room_list.xml
Normal file
11
app/src/main/res/layout/fragment_room_list.xml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<com.airbnb.epoxy.EpoxyRecyclerView
|
||||||
|
android:id="@+id/epoxyRecyclerView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent" />
|
||||||
|
|
||||||
|
</FrameLayout>
|
6
app/src/main/res/layout/item_event.xml
Normal file
6
app/src/main/res/layout/item_event.xml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<android.support.constraint.ConstraintLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
</android.support.constraint.ConstraintLayout>
|
10
app/src/main/res/layout/item_room.xml
Normal file
10
app/src/main/res/layout/item_room.xml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/titleView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="80dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:padding="16dp"
|
||||||
|
android:textSize="14sp"
|
||||||
|
tools:text="Room name" />
|
Loading…
Reference in a new issue