Timeline : fix small issue, reduce animation time and reorganize a bit some files.

This commit is contained in:
ganfra 2019-03-05 18:31:03 +01:00
parent 4c3f7171e7
commit 1d4882e596
20 changed files with 124 additions and 65 deletions

View File

@ -19,16 +19,16 @@ package im.vector.riotredesign.features.home
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import im.vector.riotredesign.core.glide.GlideApp import im.vector.riotredesign.core.glide.GlideApp
import im.vector.riotredesign.features.home.group.GroupSummaryController import im.vector.riotredesign.features.home.group.GroupSummaryController
import im.vector.riotredesign.features.home.room.detail.timeline.CallItemFactory import im.vector.riotredesign.features.home.room.detail.timeline.factory.CallItemFactory
import im.vector.riotredesign.features.home.room.detail.timeline.DefaultItemFactory import im.vector.riotredesign.features.home.room.detail.timeline.factory.DefaultItemFactory
import im.vector.riotredesign.features.home.room.detail.timeline.MessageItemFactory import im.vector.riotredesign.features.home.room.detail.timeline.factory.MessageItemFactory
import im.vector.riotredesign.features.home.room.detail.timeline.RoomHistoryVisibilityItemFactory import im.vector.riotredesign.features.home.room.detail.timeline.factory.RoomHistoryVisibilityItemFactory
import im.vector.riotredesign.features.home.room.detail.timeline.RoomMemberItemFactory import im.vector.riotredesign.features.home.room.detail.timeline.factory.RoomMemberItemFactory
import im.vector.riotredesign.features.home.room.detail.timeline.RoomNameItemFactory import im.vector.riotredesign.features.home.room.detail.timeline.factory.RoomNameItemFactory
import im.vector.riotredesign.features.home.room.detail.timeline.RoomTopicItemFactory import im.vector.riotredesign.features.home.room.detail.timeline.factory.RoomTopicItemFactory
import im.vector.riotredesign.features.home.room.detail.timeline.TimelineDateFormatter import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineDateFormatter
import im.vector.riotredesign.features.home.room.detail.timeline.TimelineEventController import im.vector.riotredesign.features.home.room.detail.timeline.TimelineEventController
import im.vector.riotredesign.features.home.room.detail.timeline.TimelineItemFactory import im.vector.riotredesign.features.home.room.detail.timeline.factory.TimelineItemFactory
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider
import im.vector.riotredesign.features.home.room.list.RoomSummaryController import im.vector.riotredesign.features.home.room.list.RoomSummaryController
import im.vector.riotredesign.features.html.EventHtmlRenderer import im.vector.riotredesign.features.html.EventHtmlRenderer

View File

@ -35,6 +35,7 @@ import im.vector.riotredesign.features.home.AvatarRenderer
import im.vector.riotredesign.features.home.HomeModule import im.vector.riotredesign.features.home.HomeModule
import im.vector.riotredesign.features.home.HomePermalinkHandler import im.vector.riotredesign.features.home.HomePermalinkHandler
import im.vector.riotredesign.features.home.room.detail.timeline.TimelineEventController import im.vector.riotredesign.features.home.room.detail.timeline.TimelineEventController
import im.vector.riotredesign.features.home.room.detail.timeline.animation.TimelineItemAnimator
import kotlinx.android.parcel.Parcelize import kotlinx.android.parcel.Parcelize
import kotlinx.android.synthetic.main.fragment_room_detail.* import kotlinx.android.synthetic.main.fragment_room_detail.*
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
@ -76,6 +77,7 @@ class RoomDetailFragment : RiotFragment(), TimelineEventController.Callback {
setupRecyclerView() setupRecyclerView()
setupToolbar() setupToolbar()
setupSendButton() setupSendButton()
timelineEventController.requestModelBuild()
roomDetailViewModel.subscribe { renderState(it) } roomDetailViewModel.subscribe { renderState(it) }
} }


@ -100,6 +102,7 @@ class RoomDetailFragment : RiotFragment(), TimelineEventController.Callback {
val stateRestorer = LayoutManagerStateRestorer(layoutManager).register() val stateRestorer = LayoutManagerStateRestorer(layoutManager).register()
scrollOnNewMessageCallback = ScrollOnNewMessageCallback(layoutManager) scrollOnNewMessageCallback = ScrollOnNewMessageCallback(layoutManager)
recyclerView.layoutManager = layoutManager recyclerView.layoutManager = layoutManager
recyclerView.itemAnimator = TimelineItemAnimator()
recyclerView.setHasFixedSize(true) recyclerView.setHasFixedSize(true)
timelineEventController.addModelBuildListener { timelineEventController.addModelBuildListener {
it.dispatchTo(stateRestorer) it.dispatchTo(stateRestorer)

View File

@ -26,7 +26,10 @@ import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.riotredesign.core.epoxy.LoadingItemModel_ import im.vector.riotredesign.core.epoxy.LoadingItemModel_
import im.vector.riotredesign.core.epoxy.RiotEpoxyModel import im.vector.riotredesign.core.epoxy.RiotEpoxyModel
import im.vector.riotredesign.core.extensions.localDateTime import im.vector.riotredesign.core.extensions.localDateTime
import im.vector.riotredesign.features.home.room.detail.timeline.factory.TimelineItemFactory
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineDateFormatter
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider
import im.vector.riotredesign.features.home.room.detail.timeline.item.DaySeparatorItem_
import im.vector.riotredesign.features.home.room.detail.timeline.paging.PagedListEpoxyController import im.vector.riotredesign.features.home.room.detail.timeline.paging.PagedListEpoxyController


class TimelineEventController(private val dateFormatter: TimelineDateFormatter, class TimelineEventController(private val dateFormatter: TimelineDateFormatter,
@ -41,10 +44,6 @@ class TimelineEventController(private val dateFormatter: TimelineDateFormatter,
private var isLoadingBackward: Boolean = false private var isLoadingBackward: Boolean = false
private var hasReachedEnd: Boolean = true private var hasReachedEnd: Boolean = true


init {
requestModelBuild()
}

var callback: Callback? = null var callback: Callback? = null


fun update(timelineData: TimelineData?) { fun update(timelineData: TimelineData?) {

View File

@ -0,0 +1,32 @@
/*
* 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.features.home.room.detail.timeline.animation

import androidx.recyclerview.widget.DefaultItemAnimator

private const val ANIM_DURATION_IN_MILLIS = 100L

class TimelineItemAnimator : DefaultItemAnimator() {

init {
addDuration = ANIM_DURATION_IN_MILLIS
removeDuration = ANIM_DURATION_IN_MILLIS
moveDuration = ANIM_DURATION_IN_MILLIS
changeDuration = ANIM_DURATION_IN_MILLIS
}

}

View File

@ -1,22 +1,20 @@
/* /*
* Copyright 2019 New Vector Ltd
* *
* * 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.
* * Licensed under the Apache License, Version 2.0 (the "License"); * You may obtain a copy of the License at
* * 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.
* *
* 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.features.home.room.detail.timeline package im.vector.riotredesign.features.home.room.detail.timeline.factory


import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.events.model.EventType
@ -26,6 +24,8 @@ import im.vector.matrix.android.api.session.room.model.call.CallInviteContent
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.riotredesign.R import im.vector.riotredesign.R
import im.vector.riotredesign.core.resources.StringProvider import im.vector.riotredesign.core.resources.StringProvider
import im.vector.riotredesign.features.home.room.detail.timeline.item.NoticeItem
import im.vector.riotredesign.features.home.room.detail.timeline.item.NoticeItem_


class CallItemFactory(private val stringProvider: StringProvider) { class CallItemFactory(private val stringProvider: StringProvider) {


@ -40,18 +40,18 @@ class CallItemFactory(private val stringProvider: StringProvider) {


private fun buildNoticeText(event: Event, roomMember: RoomMember): CharSequence? { private fun buildNoticeText(event: Event, roomMember: RoomMember): CharSequence? {
return when { return when {
EventType.CALL_INVITE == event.type -> { EventType.CALL_INVITE == event.type -> {
val content = event.content.toModel<CallInviteContent>()?: return null val content = event.content.toModel<CallInviteContent>() ?: return null
val isVideoCall = content.offer.sdp == CallInviteContent.Offer.SDP_VIDEO val isVideoCall = content.offer.sdp == CallInviteContent.Offer.SDP_VIDEO
return if(isVideoCall){ return if (isVideoCall) {
stringProvider.getString(R.string.notice_placed_video_call, roomMember.displayName) stringProvider.getString(R.string.notice_placed_video_call, roomMember.displayName)
}else{ } else {
stringProvider.getString(R.string.notice_placed_voice_call, roomMember.displayName) stringProvider.getString(R.string.notice_placed_voice_call, roomMember.displayName)
} }
} }
EventType.CALL_ANSWER == event.type -> stringProvider.getString(R.string.notice_answered_call, roomMember.displayName) EventType.CALL_ANSWER == event.type -> stringProvider.getString(R.string.notice_answered_call, roomMember.displayName)
EventType.CALL_HANGUP == event.type -> stringProvider.getString(R.string.notice_ended_call, roomMember.displayName) EventType.CALL_HANGUP == event.type -> stringProvider.getString(R.string.notice_ended_call, roomMember.displayName)
else -> null else -> null
} }


} }

View File

@ -14,9 +14,11 @@
* limitations under the License. * limitations under the License.
*/ */


package im.vector.riotredesign.features.home.room.detail.timeline package im.vector.riotredesign.features.home.room.detail.timeline.factory


import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.riotredesign.features.home.room.detail.timeline.item.DefaultItem
import im.vector.riotredesign.features.home.room.detail.timeline.item.DefaultItem_


class DefaultItemFactory { class DefaultItemFactory {


View File

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */


package im.vector.riotredesign.features.home.room.detail.timeline package im.vector.riotredesign.features.home.room.detail.timeline.factory


import android.text.Spannable import android.text.Spannable
import android.text.SpannableStringBuilder import android.text.SpannableStringBuilder
@ -33,7 +33,16 @@ import im.vector.riotredesign.R
import im.vector.riotredesign.core.epoxy.RiotEpoxyModel import im.vector.riotredesign.core.epoxy.RiotEpoxyModel
import im.vector.riotredesign.core.extensions.localDateTime import im.vector.riotredesign.core.extensions.localDateTime
import im.vector.riotredesign.core.resources.ColorProvider import im.vector.riotredesign.core.resources.ColorProvider
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineDateFormatter
import im.vector.riotredesign.features.home.room.detail.timeline.TimelineEventController
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider
import im.vector.riotredesign.features.home.room.detail.timeline.item.DefaultItem
import im.vector.riotredesign.features.home.room.detail.timeline.item.DefaultItem_
import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageImageItem
import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageImageItem_
import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageInformationData
import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageTextItem
import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageTextItem_
import im.vector.riotredesign.features.html.EventHtmlRenderer import im.vector.riotredesign.features.html.EventHtmlRenderer
import im.vector.riotredesign.features.media.MediaContentRenderer import im.vector.riotredesign.features.media.MediaContentRenderer
import me.gujun.android.span.span import me.gujun.android.span.span

View File

@ -1,22 +1,20 @@
/* /*
* Copyright 2019 New Vector Ltd
* *
* * 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.
* * Licensed under the Apache License, Version 2.0 (the "License"); * You may obtain a copy of the License at
* * 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.
* *
* 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.features.home.room.detail.timeline package im.vector.riotredesign.features.home.room.detail.timeline.factory


import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.session.events.model.toModel import im.vector.matrix.android.api.session.events.model.toModel
@ -26,6 +24,8 @@ import im.vector.matrix.android.api.session.room.model.RoomMember
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.riotredesign.R import im.vector.riotredesign.R
import im.vector.riotredesign.core.resources.StringProvider import im.vector.riotredesign.core.resources.StringProvider
import im.vector.riotredesign.features.home.room.detail.timeline.item.NoticeItem
import im.vector.riotredesign.features.home.room.detail.timeline.item.NoticeItem_




class RoomHistoryVisibilityItemFactory(private val stringProvider: StringProvider) { class RoomHistoryVisibilityItemFactory(private val stringProvider: StringProvider) {

View File

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */


package im.vector.riotredesign.features.home.room.detail.timeline package im.vector.riotredesign.features.home.room.detail.timeline.factory


import android.text.TextUtils import android.text.TextUtils
import im.vector.matrix.android.api.session.events.model.toModel import im.vector.matrix.android.api.session.events.model.toModel
@ -23,9 +23,11 @@ import im.vector.matrix.android.api.session.room.model.RoomMember
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.riotredesign.R import im.vector.riotredesign.R
import im.vector.riotredesign.core.resources.StringProvider import im.vector.riotredesign.core.resources.StringProvider
import im.vector.riotredesign.features.home.room.detail.timeline.item.NoticeItem
import im.vector.riotredesign.features.home.room.detail.timeline.item.NoticeItem_




//TODO : complete with call membership events //TODO : complete with call membership events¬
class RoomMemberItemFactory(private val stringProvider: StringProvider) { class RoomMemberItemFactory(private val stringProvider: StringProvider) {


fun create(event: TimelineEvent): NoticeItem? { fun create(event: TimelineEvent): NoticeItem? {

View File

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */


package im.vector.riotredesign.features.home.room.detail.timeline package im.vector.riotredesign.features.home.room.detail.timeline.factory


import android.text.TextUtils import android.text.TextUtils
import im.vector.matrix.android.api.session.events.model.toModel import im.vector.matrix.android.api.session.events.model.toModel
@ -22,6 +22,8 @@ import im.vector.matrix.android.api.session.room.model.RoomNameContent
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.riotredesign.R import im.vector.riotredesign.R
import im.vector.riotredesign.core.resources.StringProvider import im.vector.riotredesign.core.resources.StringProvider
import im.vector.riotredesign.features.home.room.detail.timeline.item.NoticeItem
import im.vector.riotredesign.features.home.room.detail.timeline.item.NoticeItem_


class RoomNameItemFactory(private val stringProvider: StringProvider) { class RoomNameItemFactory(private val stringProvider: StringProvider) {


View File

@ -14,13 +14,15 @@
* limitations under the License. * limitations under the License.
*/ */


package im.vector.riotredesign.features.home.room.detail.timeline package im.vector.riotredesign.features.home.room.detail.timeline.factory


import im.vector.matrix.android.api.session.events.model.toModel import im.vector.matrix.android.api.session.events.model.toModel
import im.vector.matrix.android.api.session.room.model.RoomTopicContent import im.vector.matrix.android.api.session.room.model.RoomTopicContent
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.riotredesign.R import im.vector.riotredesign.R
import im.vector.riotredesign.core.resources.StringProvider import im.vector.riotredesign.core.resources.StringProvider
import im.vector.riotredesign.features.home.room.detail.timeline.item.NoticeItem
import im.vector.riotredesign.features.home.room.detail.timeline.item.NoticeItem_


class RoomTopicItemFactory(private val stringProvider: StringProvider) { class RoomTopicItemFactory(private val stringProvider: StringProvider) {


View File

@ -14,12 +14,13 @@
* limitations under the License. * limitations under the License.
*/ */


package im.vector.riotredesign.features.home.room.detail.timeline package im.vector.riotredesign.features.home.room.detail.timeline.factory


import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.riotredesign.core.epoxy.EmptyItem_ import im.vector.riotredesign.core.epoxy.EmptyItem_
import im.vector.riotredesign.core.epoxy.RiotEpoxyModel import im.vector.riotredesign.core.epoxy.RiotEpoxyModel
import im.vector.riotredesign.features.home.room.detail.timeline.TimelineEventController


class TimelineItemFactory(private val messageItemFactory: MessageItemFactory, class TimelineItemFactory(private val messageItemFactory: MessageItemFactory,
private val roomNameItemFactory: RoomNameItemFactory, private val roomNameItemFactory: RoomNameItemFactory,

View File

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */


package im.vector.riotredesign.features.home.room.detail.timeline package im.vector.riotredesign.features.home.room.detail.timeline.helper


import im.vector.riotredesign.core.resources.LocaleProvider import im.vector.riotredesign.core.resources.LocaleProvider
import org.threeten.bp.LocalDateTime import org.threeten.bp.LocalDateTime
@ -22,12 +22,19 @@ import org.threeten.bp.format.DateTimeFormatter


class TimelineDateFormatter(private val localeProvider: LocaleProvider) { class TimelineDateFormatter(private val localeProvider: LocaleProvider) {


private val messageHourFormatter by lazy {
DateTimeFormatter.ofPattern("H:mm", localeProvider.current())
}
private val messageDayFormatter by lazy {
DateTimeFormatter.ofPattern("EEE d MMM", localeProvider.current())
}

fun formatMessageHour(localDateTime: LocalDateTime): String { fun formatMessageHour(localDateTime: LocalDateTime): String {
return DateTimeFormatter.ofPattern("H:mm", localeProvider.current()).format(localDateTime) return messageHourFormatter.format(localDateTime)
} }


fun formatMessageDay(localDateTime: LocalDateTime): String { fun formatMessageDay(localDateTime: LocalDateTime): String {
return DateTimeFormatter.ofPattern("EEE d MMM", localeProvider.current()).format(localDateTime) return messageDayFormatter.format(localDateTime)
} }


} }

View File

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */


package im.vector.riotredesign.features.home.room.detail.timeline package im.vector.riotredesign.features.home.room.detail.timeline.item


import android.view.View import android.view.View
import android.widget.ImageView import android.widget.ImageView

View File

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */


package im.vector.riotredesign.features.home.room.detail.timeline package im.vector.riotredesign.features.home.room.detail.timeline.item


import android.widget.TextView import android.widget.TextView
import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyAttribute

View File

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */


package im.vector.riotredesign.features.home.room.detail.timeline package im.vector.riotredesign.features.home.room.detail.timeline.item


import android.widget.TextView import android.widget.TextView
import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyAttribute

View File

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */


package im.vector.riotredesign.features.home.room.detail.timeline package im.vector.riotredesign.features.home.room.detail.timeline.item


import android.widget.ImageView import android.widget.ImageView
import android.widget.TextView import android.widget.TextView

View File

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */


package im.vector.riotredesign.features.home.room.detail.timeline package im.vector.riotredesign.features.home.room.detail.timeline.item


data class MessageInformationData( data class MessageInformationData(
val time: CharSequence? = null, val time: CharSequence? = null,

View File

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */


package im.vector.riotredesign.features.home.room.detail.timeline package im.vector.riotredesign.features.home.room.detail.timeline.item


import android.text.Spannable import android.text.Spannable
import android.widget.ImageView import android.widget.ImageView

View File

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */


package im.vector.riotredesign.features.home.room.detail.timeline package im.vector.riotredesign.features.home.room.detail.timeline.item


import android.widget.ImageView import android.widget.ImageView
import android.widget.TextView import android.widget.TextView