diff --git a/app/build.gradle b/app/build.gradle index c5850ece1c..b569a18234 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -76,6 +76,7 @@ dependencies { implementation 'com.jakewharton.threetenabp:threetenabp:1.1.1' implementation 'com.jakewharton.timber:timber:4.7.1' + implementation 'com.facebook.stetho:stetho:1.5.0' // rx implementation 'io.reactivex.rxjava2:rxkotlin:2.3.0' diff --git a/app/src/main/java/im/vector/riotredesign/Riot.kt b/app/src/main/java/im/vector/riotredesign/Riot.kt index 8f589d2ff6..8385443ce8 100644 --- a/app/src/main/java/im/vector/riotredesign/Riot.kt +++ b/app/src/main/java/im/vector/riotredesign/Riot.kt @@ -19,6 +19,7 @@ package im.vector.riotredesign import android.app.Application import android.content.Context import androidx.multidex.MultiDex +import com.facebook.stetho.Stetho import com.jakewharton.threetenabp.AndroidThreeTen import im.vector.matrix.android.BuildConfig import im.vector.riotredesign.core.di.AppModule @@ -33,6 +34,7 @@ class Riot : Application() { super.onCreate() if (BuildConfig.DEBUG) { Timber.plant(Timber.DebugTree()) + Stetho.initializeWithDefaults(this) } AndroidThreeTen.init(this) startKoin(listOf(AppModule(this).definition), logger = EmptyLogger()) diff --git a/app/src/main/java/im/vector/riotredesign/core/epoxy/KotlinModel.kt b/app/src/main/java/im/vector/riotredesign/core/epoxy/KotlinModel.kt deleted file mode 100644 index dd651468cd..0000000000 --- a/app/src/main/java/im/vector/riotredesign/core/epoxy/KotlinModel.kt +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2019 New Vector Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package im.vector.riotredesign.core.epoxy - -import android.view.View -import androidx.annotation.IdRes -import androidx.annotation.LayoutRes -import com.airbnb.epoxy.EpoxyModel -import com.airbnb.epoxy.OnModelVisibilityStateChangedListener -import kotlin.properties.ReadOnlyProperty -import kotlin.reflect.KProperty - -abstract class KotlinModel( - @LayoutRes private val layoutRes: Int -) : EpoxyModel() { - - private var view: View? = null - private var onBindCallback: (() -> Unit)? = null - private var onModelVisibilityStateChangedListener: OnModelVisibilityStateChangedListener? = null - - abstract fun bind() - - override fun bind(view: View) { - this.view = view - onBindCallback?.invoke() - bind() - } - - override fun unbind(view: View) { - this.view = null - } - - fun onBind(lambda: (() -> Unit)?): KotlinModel { - onBindCallback = lambda - return this - } - - override fun onVisibilityStateChanged(visibilityState: Int, view: View) { - onModelVisibilityStateChangedListener?.onVisibilityStateChanged(this, view, visibilityState) - super.onVisibilityStateChanged(visibilityState, view) - } - - fun setOnVisibilityStateChanged(listener: OnModelVisibilityStateChangedListener): KotlinModel { - this.onModelVisibilityStateChangedListener = listener - return this - } - - override fun getDefaultLayout() = layoutRes - - protected fun bind(@IdRes id: Int) = object : ReadOnlyProperty { - 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.") - } - } -} \ No newline at end of file diff --git a/app/src/main/java/im/vector/riotredesign/core/epoxy/KotlinEpoxyHolder.kt b/app/src/main/java/im/vector/riotredesign/core/epoxy/RiotEpoxyHolder.kt similarity index 79% rename from app/src/main/java/im/vector/riotredesign/core/epoxy/KotlinEpoxyHolder.kt rename to app/src/main/java/im/vector/riotredesign/core/epoxy/RiotEpoxyHolder.kt index 8b9cc92a04..f698394026 100644 --- a/app/src/main/java/im/vector/riotredesign/core/epoxy/KotlinEpoxyHolder.kt +++ b/app/src/main/java/im/vector/riotredesign/core/epoxy/RiotEpoxyHolder.kt @@ -26,15 +26,15 @@ import kotlin.reflect.KProperty * * See [SampleKotlinModelWithHolder] for a usage example. */ -abstract class KotlinEpoxyHolder : EpoxyHolder() { +abstract class RiotEpoxyHolder : EpoxyHolder() { private lateinit var view: View override fun bindView(itemView: View) { view = itemView } - protected fun bind(id: Int): ReadOnlyProperty = - Lazy { holder: KotlinEpoxyHolder, prop -> + protected fun bind(id: Int): ReadOnlyProperty = + Lazy { holder: RiotEpoxyHolder, prop -> holder.view.findViewById(id) as V? ?: throw IllegalStateException("View ID $id for '${prop.name}' not found.") } @@ -44,13 +44,13 @@ abstract class KotlinEpoxyHolder : EpoxyHolder() { * https://github.com/JakeWharton/kotterknife */ private class Lazy( - private val initializer: (KotlinEpoxyHolder, KProperty<*>) -> V - ) : ReadOnlyProperty { + private val initializer: (RiotEpoxyHolder, KProperty<*>) -> V + ) : ReadOnlyProperty { private object EMPTY private var value: Any? = EMPTY - override fun getValue(thisRef: KotlinEpoxyHolder, property: KProperty<*>): V { + override fun getValue(thisRef: RiotEpoxyHolder, property: KProperty<*>): V { if (value == EMPTY) { value = initializer(thisRef, property) } diff --git a/app/src/main/java/im/vector/riotredesign/core/epoxy/RiotEpoxyModel.kt b/app/src/main/java/im/vector/riotredesign/core/epoxy/RiotEpoxyModel.kt new file mode 100644 index 0000000000..e93fc3cb22 --- /dev/null +++ b/app/src/main/java/im/vector/riotredesign/core/epoxy/RiotEpoxyModel.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.riotredesign.core.epoxy + +import com.airbnb.epoxy.EpoxyModelWithHolder +import com.airbnb.epoxy.VisibilityState + +abstract class RiotEpoxyModel : EpoxyModelWithHolder() { + + private var onModelVisibilityStateChangedListener: OnVisibilityStateChangedListener? = null + + override fun onVisibilityStateChanged(visibilityState: Int, view: H) { + onModelVisibilityStateChangedListener?.onVisibilityStateChanged(visibilityState) + super.onVisibilityStateChanged(visibilityState, view) + } + + fun setOnVisibilityStateChanged(listener: OnVisibilityStateChangedListener): RiotEpoxyModel { + this.onModelVisibilityStateChangedListener = listener + return this + } + + interface OnVisibilityStateChangedListener { + fun onVisibilityStateChanged(@VisibilityState.Visibility visibilityState: Int) + } + + +} \ No newline at end of file diff --git a/app/src/main/java/im/vector/riotredesign/features/home/group/SelectedGroupHolder.kt b/app/src/main/java/im/vector/riotredesign/core/utils/RxStore.kt similarity index 56% rename from app/src/main/java/im/vector/riotredesign/features/home/group/SelectedGroupHolder.kt rename to app/src/main/java/im/vector/riotredesign/core/utils/RxStore.kt index 78af1e77ef..cbf96a3241 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/group/SelectedGroupHolder.kt +++ b/app/src/main/java/im/vector/riotredesign/core/utils/RxStore.kt @@ -14,25 +14,24 @@ * limitations under the License. */ -package im.vector.riotredesign.features.home.group +package im.vector.riotredesign.core.utils -import arrow.core.Option import com.jakewharton.rxrelay2.BehaviorRelay -import im.vector.matrix.android.api.session.group.model.GroupSummary import io.reactivex.Observable -class SelectedGroupHolder { +open class RxStore(defaultValue: T? = null) { - private val selectedGroupStream = BehaviorRelay.createDefault>(Option.empty()) - - fun setSelectedGroup(group: GroupSummary?) { - val optionValue = Option.fromNullable(group) - selectedGroupStream.accept(optionValue) + private val storeSubject: BehaviorRelay = if (defaultValue == null) { + BehaviorRelay.create() + } else { + BehaviorRelay.createDefault(defaultValue) } - fun selectedGroup(): Observable> { - return selectedGroupStream.hide() + fun observe(): Observable { + return storeSubject.hide().distinctUntilChanged() } - -} \ No newline at end of file + fun post(value: T) { + storeSubject.accept(value) + } +} diff --git a/app/src/main/java/im/vector/riotredesign/features/home/HomeModule.kt b/app/src/main/java/im/vector/riotredesign/features/home/HomeModule.kt index 3b3bd51df5..b63f7142f2 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/HomeModule.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/HomeModule.kt @@ -16,8 +16,8 @@ package im.vector.riotredesign.features.home -import im.vector.riotredesign.features.home.group.SelectedGroupHolder -import im.vector.riotredesign.features.home.room.VisibleRoomHolder +import im.vector.riotredesign.features.home.group.SelectedGroupStore +import im.vector.riotredesign.features.home.room.VisibleRoomStore import im.vector.riotredesign.features.home.room.detail.timeline.DefaultItemFactory import im.vector.riotredesign.features.home.room.detail.timeline.MessageItemFactory import im.vector.riotredesign.features.home.room.detail.timeline.RoomMemberItemFactory @@ -80,11 +80,11 @@ class HomeModule { } single { - SelectedGroupHolder() + SelectedGroupStore() } single { - VisibleRoomHolder() + VisibleRoomStore() } single { diff --git a/app/src/main/java/im/vector/riotredesign/features/home/group/GroupListViewModel.kt b/app/src/main/java/im/vector/riotredesign/features/home/group/GroupListViewModel.kt index 07e9b4ef76..f35c46fc10 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/group/GroupListViewModel.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/group/GroupListViewModel.kt @@ -16,6 +16,7 @@ package im.vector.riotredesign.features.home.group +import arrow.core.Option import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.ViewModelContext import im.vector.matrix.android.api.Matrix @@ -25,7 +26,7 @@ import im.vector.riotredesign.core.platform.RiotViewModel import org.koin.android.ext.android.get class GroupListViewModel(initialState: GroupListViewState, - private val selectedGroupHolder: SelectedGroupHolder, + private val selectedGroupHolder: SelectedGroupStore, private val session: Session ) : RiotViewModel(initialState) { @@ -34,7 +35,7 @@ class GroupListViewModel(initialState: GroupListViewState, @JvmStatic override fun create(viewModelContext: ViewModelContext, state: GroupListViewState): GroupListViewModel? { val currentSession = Matrix.getInstance().currentSession - val selectedGroupHolder = viewModelContext.activity.get() + val selectedGroupHolder = viewModelContext.activity.get() return GroupListViewModel(state, selectedGroupHolder, currentSession) } } @@ -46,7 +47,8 @@ class GroupListViewModel(initialState: GroupListViewState, private fun observeState() { subscribe { - selectedGroupHolder.setSelectedGroup(it.selectedGroup) + val selectedGroup = Option.fromNullable(it.selectedGroup) + selectedGroupHolder.post(selectedGroup) } } diff --git a/app/src/main/java/im/vector/riotredesign/features/home/group/GroupSummaryController.kt b/app/src/main/java/im/vector/riotredesign/features/home/group/GroupSummaryController.kt index d2bc0b958d..3f24f9e0f4 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/group/GroupSummaryController.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/group/GroupSummaryController.kt @@ -32,14 +32,13 @@ class GroupSummaryController(private val callback: Callback? = null } summaries.forEach { groupSummary -> val isSelected = groupSummary.groupId == selected?.groupId - GroupSummaryItem( - groupName = groupSummary.displayName, - avatarUrl = groupSummary.avatarUrl, - isSelected = isSelected, - listener = { callback?.onGroupSelected(groupSummary) } - ) - .id(groupSummary.groupId) - .addTo(this) + groupSummaryItem { + id(groupSummary.groupId) + groupName(groupSummary.displayName) + selected(isSelected) + avatarUrl(groupSummary.avatarUrl) + listener { callback?.onGroupSelected(groupSummary) } + } } } diff --git a/app/src/main/java/im/vector/riotredesign/features/home/group/GroupSummaryItem.kt b/app/src/main/java/im/vector/riotredesign/features/home/group/GroupSummaryItem.kt index 664e7453d0..5271780118 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/group/GroupSummaryItem.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/group/GroupSummaryItem.kt @@ -17,25 +17,32 @@ package im.vector.riotredesign.features.home.group import android.widget.ImageView +import com.airbnb.epoxy.EpoxyAttribute +import com.airbnb.epoxy.EpoxyModelClass import im.vector.riotredesign.R -import im.vector.riotredesign.core.epoxy.KotlinModel +import im.vector.riotredesign.core.epoxy.RiotEpoxyHolder +import im.vector.riotredesign.core.epoxy.RiotEpoxyModel import im.vector.riotredesign.core.platform.CheckableFrameLayout import im.vector.riotredesign.features.home.AvatarRenderer +@EpoxyModelClass(layout = R.layout.item_group) +abstract class GroupSummaryItem : RiotEpoxyModel() { -data class GroupSummaryItem( - val groupName: CharSequence, - val avatarUrl: String?, - val isSelected: Boolean, - val listener: (() -> Unit)? = null -) : KotlinModel(R.layout.item_group) { + @EpoxyAttribute lateinit var groupName: CharSequence + @EpoxyAttribute var avatarUrl: String? = null + @EpoxyAttribute var selected: Boolean = false + @EpoxyAttribute var listener: (() -> Unit)? = null - private val avatarImageView by bind(R.id.groupAvatarImageView) - private val rootView by bind(R.id.itemGroupLayout) - - override fun bind() { - rootView.isSelected = isSelected - rootView.setOnClickListener { listener?.invoke() } - AvatarRenderer.render(avatarUrl, groupName.toString(), avatarImageView) + override fun bind(holder: Holder) { + super.bind(holder) + holder.rootView.isSelected = selected + holder.rootView.setOnClickListener { listener?.invoke() } + AvatarRenderer.render(avatarUrl, groupName.toString(), holder.avatarImageView) } + + class Holder : RiotEpoxyHolder() { + val avatarImageView by bind(R.id.groupAvatarImageView) + val rootView by bind(R.id.itemGroupLayout) + } + } \ No newline at end of file diff --git a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/BlankItem.kt b/app/src/main/java/im/vector/riotredesign/features/home/group/SelectedGroupStore.kt similarity index 68% rename from app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/BlankItem.kt rename to app/src/main/java/im/vector/riotredesign/features/home/group/SelectedGroupStore.kt index 156a5281b4..5ee3cc4b1c 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/BlankItem.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/group/SelectedGroupStore.kt @@ -14,16 +14,10 @@ * limitations under the License. */ -package im.vector.riotredesign.features.home.room.detail.timeline +package im.vector.riotredesign.features.home.group -import im.vector.riotredesign.R -import im.vector.riotredesign.core.epoxy.KotlinModel +import arrow.core.Option +import im.vector.matrix.android.api.session.group.model.GroupSummary +import im.vector.riotredesign.core.utils.RxStore -class BlankItem - : KotlinModel(R.layout.item_timeline_event_blank) { - - override fun bind() { - //no-op - } - -} \ No newline at end of file +class SelectedGroupStore : RxStore>(Option.empty()) diff --git a/app/src/main/java/im/vector/riotredesign/features/home/room/VisibleRoomHolder.kt b/app/src/main/java/im/vector/riotredesign/features/home/room/VisibleRoomStore.kt similarity index 61% rename from app/src/main/java/im/vector/riotredesign/features/home/room/VisibleRoomHolder.kt rename to app/src/main/java/im/vector/riotredesign/features/home/room/VisibleRoomStore.kt index 0ef1df4712..6b2d60f69f 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/room/VisibleRoomHolder.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/room/VisibleRoomStore.kt @@ -16,21 +16,6 @@ package im.vector.riotredesign.features.home.room -import com.jakewharton.rxrelay2.BehaviorRelay -import io.reactivex.Observable -import io.reactivex.subjects.BehaviorSubject +import im.vector.riotredesign.core.utils.RxStore -class VisibleRoomHolder { - - private val visibleRoomStream = BehaviorRelay.create() - - fun setVisibleRoom(roomId: String) { - visibleRoomStream.accept(roomId) - } - - fun visibleRoom(): Observable { - return visibleRoomStream.hide() - } - - -} \ No newline at end of file +class VisibleRoomStore : RxStore() diff --git a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailViewModel.kt b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailViewModel.kt index 3d1d4cf104..3ce30c0e59 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailViewModel.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailViewModel.kt @@ -26,14 +26,14 @@ import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.rx.rx import im.vector.riotredesign.core.extensions.lastMinBy import im.vector.riotredesign.core.platform.RiotViewModel -import im.vector.riotredesign.features.home.room.VisibleRoomHolder +import im.vector.riotredesign.features.home.room.VisibleRoomStore import io.reactivex.rxkotlin.subscribeBy import org.koin.android.ext.android.get import java.util.concurrent.TimeUnit class RoomDetailViewModel(initialState: RoomDetailViewState, private val session: Session, - private val visibleRoomHolder: VisibleRoomHolder + private val visibleRoomHolder: VisibleRoomStore ) : RiotViewModel(initialState) { private val room = session.getRoom(initialState.roomId)!! @@ -47,7 +47,7 @@ class RoomDetailViewModel(initialState: RoomDetailViewState, @JvmStatic override fun create(viewModelContext: ViewModelContext, state: RoomDetailViewState): RoomDetailViewModel? { val currentSession = Matrix.getInstance().currentSession - val visibleRoomHolder = viewModelContext.activity.get() + val visibleRoomHolder = viewModelContext.activity.get() return RoomDetailViewModel(state, currentSession, visibleRoomHolder) } } @@ -78,7 +78,7 @@ class RoomDetailViewModel(initialState: RoomDetailViewState, } private fun handleIsDisplayed() { - visibleRoomHolder.setVisibleRoom(roomId) + visibleRoomHolder.post(roomId) } private fun observeDisplayedEvents() { diff --git a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/AbsMessageItem.kt b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/AbsMessageItem.kt index fb10badbbe..8a598d0f35 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/AbsMessageItem.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/AbsMessageItem.kt @@ -19,31 +19,33 @@ package im.vector.riotredesign.features.home.room.detail.timeline import android.view.View import android.widget.ImageView import android.widget.TextView -import androidx.annotation.LayoutRes -import im.vector.riotredesign.core.epoxy.KotlinModel +import im.vector.riotredesign.core.epoxy.RiotEpoxyHolder +import im.vector.riotredesign.core.epoxy.RiotEpoxyModel import im.vector.riotredesign.features.home.AvatarRenderer -abstract class AbsMessageItem(private val informationData: MessageInformationData, - @LayoutRes layoutRes: Int -) : KotlinModel(layoutRes) { +abstract class AbsMessageItem : RiotEpoxyModel() { - protected abstract val avatarImageView: ImageView - protected abstract val memberNameView: TextView - protected abstract val timeView: TextView + abstract val informationData: MessageInformationData - override fun bind() { + override fun bind(holder: H) { if (informationData.showInformation) { - avatarImageView.visibility = View.VISIBLE - memberNameView.visibility = View.VISIBLE - timeView.visibility = View.VISIBLE - timeView.text = informationData.time - memberNameView.text = informationData.memberName - AvatarRenderer.render(informationData.avatarUrl, informationData.memberName?.toString(), avatarImageView) + holder.avatarImageView.visibility = View.VISIBLE + holder.memberNameView.visibility = View.VISIBLE + holder.timeView.visibility = View.VISIBLE + holder.timeView.text = informationData.time + holder.memberNameView.text = informationData.memberName + AvatarRenderer.render(informationData.avatarUrl, informationData.memberName?.toString(), holder.avatarImageView) } else { - avatarImageView.visibility = View.GONE - memberNameView.visibility = View.GONE - timeView.visibility = View.GONE + holder.avatarImageView.visibility = View.GONE + holder.memberNameView.visibility = View.GONE + holder.timeView.visibility = View.GONE } } + abstract class Holder : RiotEpoxyHolder() { + abstract val avatarImageView: ImageView + abstract val memberNameView: TextView + abstract val timeView: TextView + } + } \ No newline at end of file diff --git a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/DaySeparatorItem.kt b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/DaySeparatorItem.kt index e80ec2bacd..6f1bcc7c36 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/DaySeparatorItem.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/DaySeparatorItem.kt @@ -17,16 +17,22 @@ package im.vector.riotredesign.features.home.room.detail.timeline import android.widget.TextView +import com.airbnb.epoxy.EpoxyAttribute +import com.airbnb.epoxy.EpoxyModelClass +import com.airbnb.epoxy.EpoxyModelWithHolder import im.vector.riotredesign.R -import im.vector.riotredesign.core.epoxy.KotlinModel +import im.vector.riotredesign.core.epoxy.RiotEpoxyHolder -data class DaySeparatorItem( - val formattedDay: CharSequence -) : KotlinModel(R.layout.item_timeline_event_day_separator) { +@EpoxyModelClass(layout = R.layout.item_timeline_event_day_separator) +abstract class DaySeparatorItem : EpoxyModelWithHolder() { - private val dayTextView by bind(R.id.itemDayTextView) + @EpoxyAttribute lateinit var formattedDay: CharSequence - override fun bind() { - dayTextView.text = formattedDay + override fun bind(holder: Holder) { + holder.dayTextView.text = formattedDay + } + + class Holder : RiotEpoxyHolder() { + val dayTextView by bind(R.id.itemDayTextView) } } \ No newline at end of file diff --git a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/DefaultItem.kt b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/DefaultItem.kt index b9db438484..3d399e2f45 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/DefaultItem.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/DefaultItem.kt @@ -17,16 +17,22 @@ package im.vector.riotredesign.features.home.room.detail.timeline import android.widget.TextView +import com.airbnb.epoxy.EpoxyAttribute +import com.airbnb.epoxy.EpoxyModelClass import im.vector.riotredesign.R -import im.vector.riotredesign.core.epoxy.KotlinModel +import im.vector.riotredesign.core.epoxy.RiotEpoxyHolder +import im.vector.riotredesign.core.epoxy.RiotEpoxyModel -class DefaultItem( - val text: CharSequence? = null -) : KotlinModel(R.layout.item_timeline_event_default) { +@EpoxyModelClass(layout = R.layout.item_timeline_event_default) +abstract class DefaultItem : RiotEpoxyModel() { - private val messageView by bind(R.id.stateMessageView) + @EpoxyAttribute var text: CharSequence? = null - override fun bind() { - messageView.text = text + override fun bind(holder: Holder) { + holder.messageView.text = text + } + + class Holder : RiotEpoxyHolder() { + val messageView by bind(R.id.stateMessageView) } } \ No newline at end of file diff --git a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/DefaultItemFactory.kt b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/DefaultItemFactory.kt index 03a631eaf1..c168d89e17 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/DefaultItemFactory.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/DefaultItemFactory.kt @@ -26,7 +26,7 @@ class DefaultItemFactory { } else { "an exception occurred when rendering the event ${event.root.eventId}" } - return DefaultItem(text = text) + return DefaultItem_().text(text) } } \ No newline at end of file diff --git a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/MessageImageItem.kt b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/MessageImageItem.kt index dc93c89dd0..310ffd4613 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/MessageImageItem.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/MessageImageItem.kt @@ -18,23 +18,27 @@ package im.vector.riotredesign.features.home.room.detail.timeline import android.widget.ImageView import android.widget.TextView +import com.airbnb.epoxy.EpoxyAttribute +import com.airbnb.epoxy.EpoxyModelClass import im.vector.riotredesign.R import im.vector.riotredesign.features.media.MediaContentRenderer -class MessageImageItem( - private val mediaData: MediaContentRenderer.Data, - informationData: MessageInformationData -) : AbsMessageItem(informationData, R.layout.item_timeline_event_image_message) { +@EpoxyModelClass(layout = R.layout.item_timeline_event_image_message) +abstract class MessageImageItem : AbsMessageItem() { - override val avatarImageView by bind(R.id.messageAvatarImageView) - override val memberNameView by bind(R.id.messageMemberNameView) - override val timeView by bind(R.id.messageTimeView) - private val imageView by bind(R.id.messageImageView) + @EpoxyAttribute lateinit var mediaData: MediaContentRenderer.Data + @EpoxyAttribute override lateinit var informationData: MessageInformationData - override fun bind() { - super.bind() - MediaContentRenderer.render(mediaData, MediaContentRenderer.Mode.THUMBNAIL, imageView) + override fun bind(holder: Holder) { + super.bind(holder) + MediaContentRenderer.render(mediaData, MediaContentRenderer.Mode.THUMBNAIL, holder.imageView) } + class Holder : AbsMessageItem.Holder() { + override val avatarImageView by bind(R.id.messageAvatarImageView) + override val memberNameView by bind(R.id.messageMemberNameView) + override val timeView by bind(R.id.messageTimeView) + val imageView by bind(R.id.messageImageView) + } } \ No newline at end of file diff --git a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/MessageItemFactory.kt b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/MessageItemFactory.kt index 314bf77487..a52659de62 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/MessageItemFactory.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/MessageItemFactory.kt @@ -26,7 +26,7 @@ import im.vector.matrix.android.api.session.room.model.message.MessageContent import im.vector.matrix.android.api.session.room.model.message.MessageImageContent import im.vector.matrix.android.api.session.room.model.message.MessageTextContent import im.vector.matrix.android.api.session.room.timeline.TimelineEvent -import im.vector.riotredesign.core.epoxy.KotlinModel +import im.vector.riotredesign.core.epoxy.RiotEpoxyModel import im.vector.riotredesign.core.extensions.localDateTime import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider import im.vector.riotredesign.features.media.MediaContentRenderer @@ -39,7 +39,7 @@ class MessageItemFactory(private val timelineMediaSizeProvider: TimelineMediaSiz fun create(event: TimelineEvent, nextEvent: TimelineEvent?, callback: TimelineEventController.Callback? - ): KotlinModel? { + ): RiotEpoxyModel<*>? { val roomMember = event.roomMember val nextRoomMember = nextEvent?.roomMember @@ -48,12 +48,12 @@ class MessageItemFactory(private val timelineMediaSizeProvider: TimelineMediaSiz val nextDate = nextEvent?.root?.localDateTime() val addDaySeparator = date.toLocalDate() != nextDate?.toLocalDate() val isNextMessageReceivedMoreThanOneHourAgo = nextDate?.isBefore(date.minusMinutes(60)) - ?: false + ?: false if (addDaySeparator - || nextRoomMember != roomMember - || nextEvent?.root?.type != EventType.MESSAGE - || isNextMessageReceivedMoreThanOneHourAgo) { + || nextRoomMember != roomMember + || nextEvent?.root?.type != EventType.MESSAGE + || isNextMessageReceivedMoreThanOneHourAgo) { messagesDisplayedWithInformation.add(event.root.eventId) } @@ -71,9 +71,9 @@ class MessageItemFactory(private val timelineMediaSizeProvider: TimelineMediaSiz } } - private fun buildNotHandledMessageItem(messageContent: MessageContent): KotlinModel? { + private fun buildNotHandledMessageItem(messageContent: MessageContent): DefaultItem? { val text = "${messageContent.type} message events are not yet handled" - return DefaultItem(text = text) + return DefaultItem_().text(text) } private fun buildImageMessageItem(messageContent: MessageImageContent, @@ -89,7 +89,9 @@ class MessageItemFactory(private val timelineMediaSizeProvider: TimelineMediaSiz rotation = messageContent.info?.rotation, orientation = messageContent.info?.orientation ) - return MessageImageItem(data, informationData) + return MessageImageItem_() + .informationData(informationData) + .mediaData(data) } private fun buildTextMessageItem(messageContent: MessageTextContent, @@ -106,10 +108,9 @@ class MessageItemFactory(private val timelineMediaSizeProvider: TimelineMediaSiz Linkify.addLinks(spannable, Linkify.ALL) spannable } - return MessageTextItem( - message = message, - informationData = informationData - ) + return MessageTextItem_() + .message(message) + .informationData(informationData) } diff --git a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/MessageTextItem.kt b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/MessageTextItem.kt index 2044632943..8265313dc7 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/MessageTextItem.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/MessageTextItem.kt @@ -18,22 +18,29 @@ package im.vector.riotredesign.features.home.room.detail.timeline import android.widget.ImageView import android.widget.TextView +import com.airbnb.epoxy.EpoxyAttribute +import com.airbnb.epoxy.EpoxyModelClass import im.vector.matrix.android.api.permalinks.MatrixLinkify import im.vector.riotredesign.R -class MessageTextItem( - val message: CharSequence? = null, - informationData: MessageInformationData -) : AbsMessageItem(informationData, R.layout.item_timeline_event_text_message) { +@EpoxyModelClass(layout = R.layout.item_timeline_event_text_message) +abstract class MessageTextItem : AbsMessageItem() { - override val avatarImageView by bind(R.id.messageAvatarImageView) - override val memberNameView by bind(R.id.messageMemberNameView) - override val timeView by bind(R.id.messageTimeView) - private val messageView by bind(R.id.messageTextView) + @EpoxyAttribute var message: CharSequence? = null + @EpoxyAttribute override lateinit var informationData: MessageInformationData - override fun bind() { - super.bind() - messageView.text = message - MatrixLinkify.addLinkMovementMethod(messageView) + override fun bind(holder: Holder) { + super.bind(holder) + holder.messageView.text = message + MatrixLinkify.addLinkMovementMethod(holder.messageView) } + + class Holder : AbsMessageItem.Holder() { + override val avatarImageView by bind(R.id.messageAvatarImageView) + override val memberNameView by bind(R.id.messageMemberNameView) + override val timeView by bind(R.id.messageTimeView) + val messageView by bind(R.id.messageTextView) + } + + } \ No newline at end of file diff --git a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/NoticeItem.kt b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/NoticeItem.kt index 14f9159c33..63c4ae1758 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/NoticeItem.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/NoticeItem.kt @@ -18,20 +18,27 @@ package im.vector.riotredesign.features.home.room.detail.timeline import android.widget.ImageView import android.widget.TextView +import com.airbnb.epoxy.EpoxyAttribute +import com.airbnb.epoxy.EpoxyModelClass import im.vector.riotredesign.R -import im.vector.riotredesign.core.epoxy.KotlinModel +import im.vector.riotredesign.core.epoxy.RiotEpoxyHolder +import im.vector.riotredesign.core.epoxy.RiotEpoxyModel import im.vector.riotredesign.features.home.AvatarRenderer -class NoticeItem(private val noticeText: CharSequence? = null, - private val avatarUrl: String?, - private val memberName: CharSequence? = null) - : KotlinModel(R.layout.item_timeline_event_notice) { +@EpoxyModelClass(layout = R.layout.item_timeline_event_notice) +abstract class NoticeItem : RiotEpoxyModel() { - private val avatarImageView by bind(R.id.itemNoticeAvatarView) - private val noticeTextView by bind(R.id.itemNoticeTextView) + @EpoxyAttribute var noticeText: CharSequence? = null + @EpoxyAttribute var avatarUrl: String? = null + @EpoxyAttribute var memberName: CharSequence? = null - override fun bind() { - noticeTextView.text = noticeText - AvatarRenderer.render(avatarUrl, memberName?.toString(), avatarImageView) + override fun bind(holder: Holder) { + holder.noticeTextView.text = noticeText + AvatarRenderer.render(avatarUrl, memberName?.toString(), holder.avatarImageView) + } + + class Holder : RiotEpoxyHolder() { + val avatarImageView by bind(R.id.itemNoticeAvatarView) + val noticeTextView by bind(R.id.itemNoticeTextView) } } \ No newline at end of file diff --git a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/RoomMemberItemFactory.kt b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/RoomMemberItemFactory.kt index ab2347fd0b..e2cf8cbd4f 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/RoomMemberItemFactory.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/RoomMemberItemFactory.kt @@ -17,10 +17,10 @@ package im.vector.riotredesign.features.home.room.detail.timeline import android.text.TextUtils -import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.matrix.android.api.session.events.model.toModel import im.vector.matrix.android.api.session.room.model.Membership import im.vector.matrix.android.api.session.room.model.RoomMember +import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.riotredesign.R import im.vector.riotredesign.core.resources.StringProvider @@ -31,7 +31,10 @@ class RoomMemberItemFactory(private val stringProvider: StringProvider) { fun create(event: TimelineEvent): NoticeItem? { val roomMember = event.roomMember ?: return null val noticeText = buildRoomMemberNotice(event) ?: return null - return NoticeItem(noticeText, roomMember.avatarUrl, roomMember.displayName) + return NoticeItem_() + .noticeText(noticeText) + .avatarUrl(roomMember.avatarUrl) + .memberName(roomMember.displayName) } private fun buildRoomMemberNotice(event: TimelineEvent): String? { diff --git a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/RoomNameItemFactory.kt b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/RoomNameItemFactory.kt index 8732a926ca..db59e58a72 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/RoomNameItemFactory.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/RoomNameItemFactory.kt @@ -17,9 +17,9 @@ package im.vector.riotredesign.features.home.room.detail.timeline import android.text.TextUtils -import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.matrix.android.api.session.events.model.toModel import im.vector.matrix.android.api.session.room.model.RoomNameContent +import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.riotredesign.R import im.vector.riotredesign.core.resources.StringProvider @@ -37,7 +37,10 @@ class RoomNameItemFactory(private val stringProvider: StringProvider) { } else { stringProvider.getString(R.string.notice_room_name_removed, roomMember.displayName) } - return NoticeItem(text, roomMember.avatarUrl, roomMember.displayName) + return NoticeItem_() + .noticeText(text) + .avatarUrl(roomMember.avatarUrl) + .memberName(roomMember.displayName) } diff --git a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/RoomTopicItemFactory.kt b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/RoomTopicItemFactory.kt index 90c7c83f42..f4fe72c9ed 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/RoomTopicItemFactory.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/RoomTopicItemFactory.kt @@ -36,7 +36,10 @@ class RoomTopicItemFactory(private val stringProvider: StringProvider) { } else { stringProvider.getString(R.string.notice_room_topic_changed, roomMember.displayName, content.topic) } - return NoticeItem(text, roomMember.avatarUrl, roomMember.displayName) + return NoticeItem_() + .noticeText(text) + .avatarUrl(roomMember.avatarUrl) + .memberName(roomMember.displayName) } diff --git a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/TimelineEventController.kt b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/TimelineEventController.kt index 8fc268c3be..75b3f26580 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/TimelineEventController.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/TimelineEventController.kt @@ -16,16 +16,14 @@ package im.vector.riotredesign.features.home.room.detail.timeline -import android.view.View import androidx.recyclerview.widget.RecyclerView import com.airbnb.epoxy.EpoxyAsyncUtil import com.airbnb.epoxy.EpoxyModel -import com.airbnb.epoxy.OnModelVisibilityStateChangedListener import com.airbnb.epoxy.VisibilityState import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.room.timeline.TimelineData import im.vector.matrix.android.api.session.room.timeline.TimelineEvent -import im.vector.riotredesign.core.epoxy.KotlinModel +import im.vector.riotredesign.core.epoxy.RiotEpoxyModel import im.vector.riotredesign.core.extensions.localDateTime import im.vector.riotredesign.features.home.LoadingItemModel_ import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider @@ -78,16 +76,12 @@ class TimelineEventController(private val roomId: String, timelineItemFactory.create(event, nextEvent, callback)?.also { it.id(event.localId) - it.setOnVisibilityStateChanged(OnModelVisibilityStateChangedListener { model, view, visibilityState -> - if (visibilityState == VisibilityState.VISIBLE) { - callback?.onEventVisible(event, currentPosition) - } - }) + it.setOnVisibilityStateChanged(TimelineEventVisibilityStateChangedListener(callback, event, currentPosition)) epoxyModels.add(it) } if (addDaySeparator) { val formattedDay = dateFormatter.formatMessageDay(date) - val daySeparatorItem = DaySeparatorItem(formattedDay).id(roomId + formattedDay) + val daySeparatorItem = DaySeparatorItem_().formattedDay(formattedDay).id(roomId + formattedDay) epoxyModels.add(daySeparatorItem) } return epoxyModels @@ -111,4 +105,18 @@ class TimelineEventController(private val roomId: String, fun onUrlClicked(url: String) } +} + +private class TimelineEventVisibilityStateChangedListener(private val callback: TimelineEventController.Callback?, + private val event: TimelineEvent, + private val currentPosition: Int) + : RiotEpoxyModel.OnVisibilityStateChangedListener { + + override fun onVisibilityStateChanged(visibilityState: Int) { + if (visibilityState == VisibilityState.VISIBLE) { + callback?.onEventVisible(event, currentPosition) + } + } + + } \ No newline at end of file diff --git a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/TimelineItemFactory.kt b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/TimelineItemFactory.kt index 83a41cf65e..8fca5301b0 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/TimelineItemFactory.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/TimelineItemFactory.kt @@ -18,7 +18,7 @@ package im.vector.riotredesign.features.home.room.detail.timeline import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.room.timeline.TimelineEvent -import im.vector.riotredesign.core.epoxy.KotlinModel +import im.vector.riotredesign.core.epoxy.RiotEpoxyModel class TimelineItemFactory(private val messageItemFactory: MessageItemFactory, private val roomNameItemFactory: RoomNameItemFactory, @@ -28,7 +28,7 @@ class TimelineItemFactory(private val messageItemFactory: MessageItemFactory, fun create(event: TimelineEvent, nextEvent: TimelineEvent?, - callback: TimelineEventController.Callback?): KotlinModel? { + callback: TimelineEventController.Callback?): RiotEpoxyModel<*>? { return try { when (event.root.type) { diff --git a/app/src/main/java/im/vector/riotredesign/features/home/room/list/RoomCategoryItem.kt b/app/src/main/java/im/vector/riotredesign/features/home/room/list/RoomCategoryItem.kt index 7fcc0210cb..656593b419 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/room/list/RoomCategoryItem.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/room/list/RoomCategoryItem.kt @@ -20,33 +20,39 @@ import android.view.ViewGroup import android.widget.TextView import androidx.core.content.ContextCompat import androidx.core.graphics.drawable.DrawableCompat +import com.airbnb.epoxy.EpoxyAttribute +import com.airbnb.epoxy.EpoxyModelClass import im.vector.riotredesign.R -import im.vector.riotredesign.core.epoxy.KotlinModel +import im.vector.riotredesign.core.epoxy.RiotEpoxyHolder +import im.vector.riotredesign.core.epoxy.RiotEpoxyModel -data class RoomCategoryItem( - val title: CharSequence, - val isExpanded: Boolean, - val unreadCount: Int, - val showHighlighted: Boolean, - val listener: (() -> Unit)? = null -) : KotlinModel(R.layout.item_room_category) { +@EpoxyModelClass(layout = R.layout.item_room_category) +abstract class RoomCategoryItem : RiotEpoxyModel() { - private val unreadCounterBadgeView by bind(R.id.roomCategoryUnreadCounterBadgeView) - private val titleView by bind(R.id.roomCategoryTitleView) - private val rootView by bind(R.id.roomCategoryRootView) + @EpoxyAttribute lateinit var title: CharSequence + @EpoxyAttribute var expanded: Boolean = false + @EpoxyAttribute var unreadCount: Int = 0 + @EpoxyAttribute var showHighlighted: Boolean = false + @EpoxyAttribute var listener: (() -> Unit)? = null - private val tintColor by lazy { - ContextCompat.getColor(rootView.context, R.color.bluey_grey_two) - } - - override fun bind() { - val expandedArrowDrawableRes = if (isExpanded) R.drawable.ic_expand_more_white else R.drawable.ic_expand_less_white - val expandedArrowDrawable = ContextCompat.getDrawable(rootView.context, expandedArrowDrawableRes)?.also { + override fun bind(holder: Holder) { + val tintColor = ContextCompat.getColor(holder.rootView.context, R.color.bluey_grey_two) + val expandedArrowDrawableRes = if (expanded) R.drawable.ic_expand_more_white else R.drawable.ic_expand_less_white + val expandedArrowDrawable = ContextCompat.getDrawable(holder.rootView.context, expandedArrowDrawableRes)?.also { DrawableCompat.setTint(it, tintColor) } - unreadCounterBadgeView.render(unreadCount, showHighlighted) - titleView.setCompoundDrawablesWithIntrinsicBounds(expandedArrowDrawable, null, null, null) - titleView.text = title - rootView.setOnClickListener { listener?.invoke() } + holder.unreadCounterBadgeView.render(unreadCount, showHighlighted) + holder.titleView.setCompoundDrawablesWithIntrinsicBounds(expandedArrowDrawable, null, null, null) + holder.titleView.text = title + holder.rootView.setOnClickListener { listener?.invoke() } } + + + class Holder : RiotEpoxyHolder() { + val unreadCounterBadgeView by bind(R.id.roomCategoryUnreadCounterBadgeView) + val titleView by bind(R.id.roomCategoryTitleView) + val rootView by bind(R.id.roomCategoryRootView) + } + } + diff --git a/app/src/main/java/im/vector/riotredesign/features/home/room/list/RoomListViewModel.kt b/app/src/main/java/im/vector/riotredesign/features/home/room/list/RoomListViewModel.kt index 68d41bae82..0612016c06 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/room/list/RoomListViewModel.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/room/list/RoomListViewModel.kt @@ -27,8 +27,8 @@ import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.matrix.android.api.session.room.model.tag.RoomTag import im.vector.matrix.rx.rx import im.vector.riotredesign.core.platform.RiotViewModel -import im.vector.riotredesign.features.home.group.SelectedGroupHolder -import im.vector.riotredesign.features.home.room.VisibleRoomHolder +import im.vector.riotredesign.features.home.group.SelectedGroupStore +import im.vector.riotredesign.features.home.room.VisibleRoomStore import io.reactivex.Observable import io.reactivex.functions.Function3 import io.reactivex.rxkotlin.subscribeBy @@ -39,8 +39,8 @@ typealias RoomListFilterName = CharSequence class RoomListViewModel(initialState: RoomListViewState, private val session: Session, - private val selectedGroupHolder: SelectedGroupHolder, - private val visibleRoomHolder: VisibleRoomHolder, + private val selectedGroupHolder: SelectedGroupStore, + private val visibleRoomHolder: VisibleRoomStore, private val roomSelectionRepository: RoomSelectionRepository, private val roomSummaryComparator: RoomSummaryComparator) : RiotViewModel(initialState) { @@ -51,8 +51,8 @@ class RoomListViewModel(initialState: RoomListViewState, override fun create(viewModelContext: ViewModelContext, state: RoomListViewState): RoomListViewModel? { val currentSession = Matrix.getInstance().currentSession val roomSelectionRepository = viewModelContext.activity.get() - val selectedGroupHolder = viewModelContext.activity.get() - val visibleRoomHolder = viewModelContext.activity.get() + val selectedGroupHolder = viewModelContext.activity.get() + val visibleRoomHolder = viewModelContext.activity.get() val roomSummaryComparator = viewModelContext.activity.get() return RoomListViewModel(state, currentSession, selectedGroupHolder, visibleRoomHolder, roomSelectionRepository, roomSummaryComparator) } @@ -87,17 +87,18 @@ class RoomListViewModel(initialState: RoomListViewState, } private fun observeVisibleRoom() { - visibleRoomHolder.visibleRoom() - .subscribeBy { + visibleRoomHolder.observe() + .doOnNext { setState { copy(selectedRoomId = it) } } + .subscribe() .disposeOnClear() } private fun observeRoomSummaries() { Observable.combineLatest, Option, Option, RoomSummaries>( session.rx().liveRoomSummaries().throttleLast(300, TimeUnit.MILLISECONDS), - selectedGroupHolder.selectedGroup(), + selectedGroupHolder.observe(), roomListFilter.throttleLast(300, TimeUnit.MILLISECONDS), Function3 { rooms, selectedGroupOption, filterRoomOption -> val filteredRooms = filterRooms(rooms, filterRoomOption) diff --git a/app/src/main/java/im/vector/riotredesign/features/home/room/list/RoomSummaryController.kt b/app/src/main/java/im/vector/riotredesign/features/home/room/list/RoomSummaryController.kt index cc69d37573..8178e2b862 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/room/list/RoomSummaryController.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/room/list/RoomSummaryController.kt @@ -85,18 +85,17 @@ class RoomSummaryController(private val stringProvider: StringProvider summaries.map { it.notificationCount }.reduce { acc, i -> acc + i } } val showHighlighted = summaries.any { it.highlightCount > 0 } - RoomCategoryItem( - title = stringProvider.getString(titleRes).toUpperCase(), - isExpanded = isExpanded, - unreadCount = unreadCount, - showHighlighted = showHighlighted, - listener = { - mutateExpandedState() - setData(viewState) - } - ) - .id(titleRes) - .addTo(this) + roomCategoryItem { + id(titleRes) + title(stringProvider.getString(titleRes).toUpperCase()) + expanded(isExpanded) + unreadCount(unreadCount) + showHighlighted(showHighlighted) + listener { + mutateExpandedState() + setData(viewState) + } + } } private fun buildRoomModels(summaries: List, selectedRoomId: String?) { @@ -104,16 +103,16 @@ class RoomSummaryController(private val stringProvider: StringProvider val unreadCount = roomSummary.notificationCount val showHighlighted = roomSummary.highlightCount > 0 val isSelected = roomSummary.roomId == selectedRoomId - RoomSummaryItem( - roomName = roomSummary.displayName, - avatarUrl = roomSummary.avatarUrl, - isSelected = isSelected, - showHighlighted = showHighlighted, - unreadCount = unreadCount, - listener = { callback?.onRoomSelected(roomSummary) } - ) - .id(roomSummary.roomId) - .addTo(this) + + roomSummaryItem { + id(roomSummary.roomId) + roomName(roomSummary.displayName) + avatarUrl(roomSummary.avatarUrl) + selected(isSelected) + showHighlighted(showHighlighted) + unreadCount(unreadCount) + listener { callback?.onRoomSelected(roomSummary) } + } } } diff --git a/app/src/main/java/im/vector/riotredesign/features/home/room/list/RoomSummaryItem.kt b/app/src/main/java/im/vector/riotredesign/features/home/room/list/RoomSummaryItem.kt index 6b4bd8a39a..fbe9f4f4a6 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/room/list/RoomSummaryItem.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/room/list/RoomSummaryItem.kt @@ -18,31 +18,40 @@ package im.vector.riotredesign.features.home.room.list import android.widget.ImageView import android.widget.TextView +import com.airbnb.epoxy.EpoxyAttribute +import com.airbnb.epoxy.EpoxyModelClass import im.vector.riotredesign.R -import im.vector.riotredesign.core.epoxy.KotlinModel +import im.vector.riotredesign.core.epoxy.RiotEpoxyHolder +import im.vector.riotredesign.core.epoxy.RiotEpoxyModel import im.vector.riotredesign.core.platform.CheckableFrameLayout import im.vector.riotredesign.features.home.AvatarRenderer -data class RoomSummaryItem( - val roomName: CharSequence, - val avatarUrl: String?, - val isSelected: Boolean, - val unreadCount: Int, - val showHighlighted: Boolean, - val listener: (() -> Unit)? = null -) : KotlinModel(R.layout.item_room) { +@EpoxyModelClass(layout = R.layout.item_room) +abstract class RoomSummaryItem : RiotEpoxyModel() { - private val unreadCounterBadgeView by bind(R.id.roomUnreadCounterBadgeView) - private val titleView by bind(R.id.roomNameView) - private val avatarImageView by bind(R.id.roomAvatarImageView) - private val rootView by bind(R.id.itemRoomLayout) + @EpoxyAttribute lateinit var roomName: CharSequence + @EpoxyAttribute var avatarUrl: String? = null + @EpoxyAttribute var selected: Boolean = false + @EpoxyAttribute var unreadCount: Int = 0 + @EpoxyAttribute var showHighlighted: Boolean = false + @EpoxyAttribute var listener: (() -> Unit)? = null - override fun bind() { - unreadCounterBadgeView.render(unreadCount, showHighlighted) - rootView.isChecked = isSelected - rootView.setOnClickListener { listener?.invoke() } - titleView.text = roomName - AvatarRenderer.render(avatarUrl, roomName.toString(), avatarImageView) + + override fun bind(holder: Holder) { + super.bind(holder) + holder.unreadCounterBadgeView.render(unreadCount, showHighlighted) + holder.rootView.isChecked = selected + holder.rootView.setOnClickListener { listener?.invoke() } + holder.titleView.text = roomName + AvatarRenderer.render(avatarUrl, roomName.toString(), holder.avatarImageView) } + + class Holder : RiotEpoxyHolder() { + val unreadCounterBadgeView by bind(R.id.roomUnreadCounterBadgeView) + val titleView by bind(R.id.roomNameView) + val avatarImageView by bind(R.id.roomAvatarImageView) + val rootView by bind(R.id.itemRoomLayout) + } + } \ No newline at end of file diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle index 612f0f7bfb..5d87130fd7 100644 --- a/matrix-sdk-android/build.gradle +++ b/matrix-sdk-android/build.gradle @@ -95,6 +95,7 @@ dependencies { // Logging implementation 'com.jakewharton.timber:timber:4.7.1' + implementation 'com.facebook.stetho:stetho-okhttp3:1.5.0' debugImplementation 'com.airbnb.okreplay:okreplay:1.4.0' releaseImplementation 'com.airbnb.okreplay:noop:1.4.0' diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/NetworkModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/NetworkModule.kt index d9abf7910d..e19c15daca 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/NetworkModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/NetworkModule.kt @@ -16,6 +16,7 @@ package im.vector.matrix.android.internal.di +import com.facebook.stetho.okhttp3.StethoInterceptor import im.vector.matrix.android.internal.network.AccessTokenInterceptor import im.vector.matrix.android.internal.network.NetworkConnectivityChecker import im.vector.matrix.android.internal.network.UnitConverterFactory @@ -47,11 +48,16 @@ class NetworkModule { OkReplayInterceptor() } + single { + StethoInterceptor() + } + single { OkHttpClient.Builder() .connectTimeout(30, TimeUnit.SECONDS) .readTimeout(30, TimeUnit.SECONDS) .writeTimeout(30, TimeUnit.SECONDS) + .addNetworkInterceptor(get()) .addInterceptor(get()) .addInterceptor(get()) .addInterceptor(get())