Timeline call tiles: start changing status handling

This commit is contained in:
ganfra 2021-08-11 12:09:59 +02:00
parent ec09532df6
commit 9027e94cfe
8 changed files with 110 additions and 63 deletions

View file

@ -24,7 +24,6 @@ import im.vector.app.features.home.room.detail.timeline.helper.AvatarSizeProvide
import im.vector.app.features.home.room.detail.timeline.helper.MessageInformationDataFactory
import im.vector.app.features.home.room.detail.timeline.helper.MessageItemAttributesFactory
import im.vector.app.features.home.room.detail.timeline.helper.RoomSummariesHolder
import im.vector.app.features.home.room.detail.timeline.helper.TimelineEventVisibilityHelper
import im.vector.app.features.home.room.detail.timeline.item.CallTileTimelineItem
import im.vector.app.features.home.room.detail.timeline.item.CallTileTimelineItem_
import im.vector.app.features.home.room.detail.timeline.item.MessageInformationData

View file

@ -358,7 +358,7 @@ class NoticeEventFormatter @Inject constructor(
}
EventType.CALL_REJECT ->
if (event.isSentByCurrentUser()) {
sp.getString(R.string.call_tile_you_declined, "")
sp.getString(R.string.call_tile_you_declined_call)
} else {
sp.getString(R.string.call_tile_other_declined, senderName)
}

View file

@ -44,10 +44,10 @@ class CallEventGrouper(private val myUserId: String, val callId: String) {
}
/**
* Returns true if there are only events from the other side - we missed the call
* Returns true if there are only events from one side.
*/
fun callWasMissed(): Boolean {
return events.none { it.senderInfo.userId == myUserId }
return events.distinctBy { it.senderInfo.userId }.size == 1
}
private fun getAnswer(): TimelineEvent? {

View file

@ -32,7 +32,6 @@ import im.vector.app.R
import im.vector.app.core.epoxy.ClickListener
import im.vector.app.core.epoxy.onClick
import im.vector.app.core.extensions.setLeftDrawable
import im.vector.app.core.extensions.setTextWithColoredPart
import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.home.room.detail.RoomDetailAction
import im.vector.app.features.home.room.detail.timeline.MessageColorProvider
@ -45,9 +44,7 @@ abstract class CallTileTimelineItem : AbsBaseMessageItem<CallTileTimelineItem.Ho
override val baseAttributes: AbsBaseMessageItem.Attributes
get() = attributes
override fun isCacheable(): Boolean {
return attributes.callKind == CallKind.CONFERENCE
}
override fun isCacheable() = false
@EpoxyAttribute
lateinit var attributes: Attributes
@ -61,13 +58,6 @@ abstract class CallTileTimelineItem : AbsBaseMessageItem<CallTileTimelineItem.Ho
}
holder.creatorNameView.text = attributes.userOfInterest.getBestName()
attributes.avatarRenderer.render(attributes.userOfInterest, holder.creatorAvatarView)
if (attributes.callKind != CallKind.UNKNOWN) {
holder.callKindView.isVisible = true
holder.callKindView.setText(attributes.callKind.title)
holder.callKindView.setLeftDrawable(attributes.callKind.icon)
} else {
holder.callKindView.isVisible = false
}
when (attributes.callStatus) {
CallStatus.INVITED -> renderInvitedStatus(holder)
CallStatus.IN_CALL -> renderInCallStatus(holder)
@ -79,32 +69,59 @@ abstract class CallTileTimelineItem : AbsBaseMessageItem<CallTileTimelineItem.Ho
}
private fun renderMissedStatus(holder: Holder) {
holder.acceptRejectViewGroup.isVisible = false
holder.statusView.isVisible = true
val status = if (attributes.callKind == CallKind.VIDEO) {
holder.resources.getQuantityString(R.plurals.missed_video_call, 1)
// Sent by me means I made the call and opponent missed it.
if (attributes.informationData.sentByMe) {
if (attributes.callKind.isVoiceCall) {
holder.statusView.setStatus(R.string.call_tile_no_answer, R.drawable.ic_missed_voice_call_small)
} else {
holder.statusView.setStatus(R.string.call_tile_no_answer, R.drawable.ic_missed_video_call_small)
}
} else {
holder.resources.getQuantityString(R.plurals.missed_audio_call, 1)
if (attributes.callKind.isVoiceCall) {
holder.statusView.setStatus(R.string.call_tile_voice_missed, R.drawable.ic_missed_voice_call_small)
} else {
holder.statusView.setStatus(R.string.call_tile_video_missed, R.drawable.ic_missed_video_call_small)
}
}
holder.statusView.text = status
holder.acceptRejectViewGroup.isVisible = true
holder.acceptView.setText(R.string.call_tile_call_back)
holder.acceptView.setLeftDrawable(attributes.callKind.icon, R.attr.colorOnPrimary)
holder.acceptView.onClick {
val callbackAction = RoomDetailAction.StartCall(attributes.callKind == CallKind.VIDEO)
attributes.callback?.onTimelineItemAction(callbackAction)
}
holder.rejectView.isVisible = false
}
private fun renderEndedStatus(holder: Holder) {
holder.acceptRejectViewGroup.isVisible = false
holder.statusView.isVisible = true
holder.statusView.setText(R.string.call_tile_ended)
holder.statusView.setStatus(R.string.call_tile_ended)
}
private fun renderRejectedStatus(holder: Holder) {
holder.acceptRejectViewGroup.isVisible = false
holder.statusView.isVisible = true
// Sent by me means I rejected the call made by opponent.
if (attributes.informationData.sentByMe) {
holder.statusView.setTextWithColoredPart(R.string.call_tile_you_declined, R.string.call_tile_call_back) {
if (attributes.callKind.isVoiceCall) {
holder.statusView.setStatus(R.string.call_tile_voice_declined, R.drawable.ic_voice_call_declined)
} else {
holder.statusView.setStatus(R.string.call_tile_video_declined, R.drawable.ic_video_call_declined)
}
holder.acceptRejectViewGroup.isVisible = true
holder.acceptView.setText(R.string.call_tile_call_back)
holder.acceptView.setLeftDrawable(attributes.callKind.icon, R.attr.colorOnPrimary)
holder.acceptView.onClick {
val callbackAction = RoomDetailAction.StartCall(attributes.callKind == CallKind.VIDEO)
attributes.callback?.onTimelineItemAction(callbackAction)
}
holder.rejectView.isVisible = false
} else {
holder.statusView.text = holder.resources.getString(R.string.call_tile_other_declined, attributes.userOfInterest.getBestName())
holder.acceptRejectViewGroup.isVisible = false
if (attributes.callKind.isVoiceCall) {
holder.statusView.setStatus(R.string.call_tile_no_answer, R.drawable.ic_voice_call_declined)
} else {
holder.statusView.setStatus(R.string.call_tile_no_answer, R.drawable.ic_video_call_declined)
}
}
}
@ -113,7 +130,6 @@ abstract class CallTileTimelineItem : AbsBaseMessageItem<CallTileTimelineItem.Ho
holder.acceptView.isVisible = false
when {
attributes.callKind == CallKind.CONFERENCE -> {
holder.statusView.isVisible = false
holder.rejectView.isVisible = true
holder.rejectView.setText(R.string.leave)
holder.rejectView.setLeftDrawable(R.drawable.ic_call_hangup, R.attr.colorOnPrimary)
@ -122,7 +138,6 @@ abstract class CallTileTimelineItem : AbsBaseMessageItem<CallTileTimelineItem.Ho
}
}
attributes.isStillActive -> {
holder.statusView.isVisible = false
holder.rejectView.isVisible = true
holder.rejectView.setText(R.string.call_notification_hangup)
holder.rejectView.setLeftDrawable(R.drawable.ic_call_hangup, R.attr.colorOnPrimary)
@ -131,17 +146,19 @@ abstract class CallTileTimelineItem : AbsBaseMessageItem<CallTileTimelineItem.Ho
}
}
else -> {
holder.statusView.isVisible = true
holder.statusView.setText(R.string.call_tile_in_call)
holder.acceptRejectViewGroup.isVisible = false
}
}
if (attributes.callKind.isVoiceCall) {
holder.statusView.setStatus(R.string.call_tile_voice_active)
} else {
holder.statusView.setStatus(R.string.call_tile_video_active)
}
}
private fun renderInvitedStatus(holder: Holder) {
when {
attributes.callKind == CallKind.CONFERENCE -> {
holder.statusView.isVisible = false
holder.acceptRejectViewGroup.isVisible = true
holder.acceptView.onClick {
attributes.callback?.onTimelineItemAction(RoomDetailAction.JoinJitsiCall)
@ -162,7 +179,6 @@ abstract class CallTileTimelineItem : AbsBaseMessageItem<CallTileTimelineItem.Ho
holder.rejectView.onClick {
attributes.callback?.onTimelineItemAction(RoomDetailAction.EndCall)
}
holder.statusView.isVisible = false
if (attributes.callKind == CallKind.AUDIO) {
holder.rejectView.setText(R.string.call_notification_reject)
holder.acceptView.setText(R.string.call_notification_answer)
@ -175,21 +191,30 @@ abstract class CallTileTimelineItem : AbsBaseMessageItem<CallTileTimelineItem.Ho
}
else -> {
holder.acceptRejectViewGroup.isVisible = false
holder.statusView.isVisible = true
if (attributes.informationData.sentByMe) {
holder.statusView.setText(R.string.call_tile_you_started_call)
} else {
holder.statusView.text = holder.resources.getString(R.string.call_tile_other_started_call, attributes.userOfInterest.getBestName())
}
}
}
when {
attributes.informationData.sentByMe -> {
holder.statusView.setStatus(R.string.call_ringing)
}
attributes.callKind.isVoiceCall -> {
holder.statusView.setStatus(R.string.call_tile_voice_incoming)
}
else -> {
holder.statusView.setStatus(R.string.call_tile_video_incoming)
}
}
}
private fun TextView.setStatus(@StringRes statusRes: Int, @DrawableRes drawableRes: Int? = null) {
setLeftDrawable(drawableRes ?: attributes.callKind.icon)
setText(statusRes)
}
class Holder : AbsBaseMessageItem.Holder(STUB_ID) {
val acceptView by bind<Button>(R.id.itemCallAcceptView)
val rejectView by bind<Button>(R.id.itemCallRejectView)
val acceptRejectViewGroup by bind<ViewGroup>(R.id.itemCallAcceptRejectViewGroup)
val callKindView by bind<TextView>(R.id.itemCallKindTextView)
val creatorAvatarView by bind<ImageView>(R.id.itemCallCreatorAvatar)
val creatorNameView by bind<TextView>(R.id.itemCallCreatorNameTextView)
val statusView by bind<TextView>(R.id.itemCallStatusTextView)
@ -223,8 +248,10 @@ abstract class CallTileTimelineItem : AbsBaseMessageItem<CallTileTimelineItem.Ho
enum class CallKind(@DrawableRes val icon: Int, @StringRes val title: Int) {
VIDEO(R.drawable.ic_call_video_small, R.string.action_video_call),
AUDIO(R.drawable.ic_call_audio_small, R.string.action_voice_call),
CONFERENCE(R.drawable.ic_call_video_small, R.string.action_video_call),
UNKNOWN(0, 0)
CONFERENCE(R.drawable.ic_call_video_small, R.string.action_video_call);
val isVoiceCall
get() = this == AUDIO
}
enum class CallStatus {

View file

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="16dp"
android:height="12dp"
android:viewportWidth="16"
android:viewportHeight="12">
<path
android:pathData="M0,2.8182C0,1.7638 0.8954,0.9091 2,0.9091H9.3333C10.4379,0.9091 11.3333,1.7638 11.3333,2.8182V9.1818C11.3333,10.2361 10.4379,11.0909 9.3333,11.0909H2C0.8954,11.0909 0,10.2361 0,9.1818V2.8182ZM12.6667,4.0909L14.9169,2.3726C15.3534,2.0392 16,2.3359 16,2.8695V9.1305C16,9.6641 15.3534,9.9607 14.9169,9.6274L12.6667,7.9091V4.0909ZM7.8233,3.8154C7.965,3.9571 7.965,4.1869 7.8233,4.3285L6.1798,5.972L7.8937,7.6859C8.0354,7.8276 8.0354,8.0574 7.8937,8.1991C7.752,8.3408 7.5223,8.3408 7.3805,8.1991L5.6667,6.4852L3.9528,8.1991C3.8111,8.3408 3.5813,8.3408 3.4396,8.1991C3.2979,8.0574 3.2979,7.8276 3.4396,7.6859L5.1535,5.972L3.51,4.3285C3.3683,4.1869 3.3683,3.9571 3.51,3.8154C3.6517,3.6737 3.8815,3.6737 4.0232,3.8154L5.6667,5.4589L7.3102,3.8154C7.4519,3.6737 7.6816,3.6737 7.8233,3.8154Z"
android:fillColor="#737D8C"
android:fillType="evenOdd"/>
</vector>

View file

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="17dp"
android:height="16dp"
android:viewportWidth="17"
android:viewportHeight="16">
<path
android:pathData="M5.8514,10.6408C6.6121,11.4621 8.4433,12.8842 8.9424,13.176C8.9719,13.1933 9.0057,13.2134 9.0435,13.2358C9.8051,13.6886 12.1916,15.1072 13.9304,13.7796C15.2775,12.751 14.8395,11.5939 14.386,11.25C14.0756,11.0085 13.161,10.3429 12.3005,9.7434C11.4555,9.1548 10.9846,9.6264 10.6662,9.9453C10.6603,9.9512 10.6545,9.957 10.6488,9.9627L10.0081,10.6034C9.845,10.7665 9.5968,10.707 9.3591,10.5203C8.5062,9.8707 7.8788,9.2439 7.5649,8.93L7.5623,8.9273C7.2484,8.6135 6.6293,7.9938 5.9798,7.1409C5.7931,6.9032 5.7335,6.655 5.8967,6.4919L6.5373,5.8512C6.5431,5.8455 6.5489,5.8397 6.5547,5.8338C6.8736,5.5154 7.3453,5.0445 6.7566,4.1995C6.1571,3.339 5.4915,2.4244 5.2501,2.114C4.9061,1.6606 3.749,1.2226 2.7204,2.5697C1.3928,4.3085 2.8115,6.6949 3.2642,7.4565C3.2867,7.4943 3.3068,7.5281 3.324,7.5576C3.6159,8.0567 5.0301,9.8801 5.8514,10.6408Z"
android:fillColor="#737D8C"/>
<path
android:pathData="M14.2982,2.0522C14.4601,1.8877 14.4601,1.6211 14.2982,1.4567C14.1362,1.2923 13.8736,1.2923 13.7117,1.4567L11.8334,3.3637L9.9551,1.4567C9.7932,1.2923 9.5306,1.2923 9.3687,1.4567C9.2067,1.6211 9.2067,1.8877 9.3687,2.0522L11.2469,3.9592L9.2882,5.9479C9.1263,6.1124 9.1263,6.379 9.2882,6.5434C9.4502,6.7078 9.7127,6.7078 9.8747,6.5434L11.8334,4.5546L13.7921,6.5434C13.9541,6.7078 14.2167,6.7078 14.3786,6.5434C14.5406,6.379 14.5406,6.1124 14.3786,5.9479L12.4199,3.9592L14.2982,2.0522Z"
android:fillColor="#737D8C"/>
</vector>

View file

@ -27,34 +27,22 @@
android:textStyle="bold"
tools:text="@sample/users.json/data/displayName" />
<TextView
android:id="@+id/itemCallKindTextView"
style="@style/Widget.Vector.TextView.Caption"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginStart="8dp"
android:layout_marginTop="4dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="12dp"
android:drawablePadding="4dp"
android:gravity="center"
android:textColor="?vctr_content_primary"
tools:text="@string/action_video_call" />
<TextView
android:id="@+id/itemCallStatusTextView"
style="@style/Widget.Vector.TextView.Body"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="12dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="12dp"
android:layout_marginBottom="16dp"
android:gravity="center"
android:textColor="?vctr_content_secondary"
tools:text="@string/video_call_in_progress" />
android:maxLines="3"
android:drawablePadding="8dp"
tools:drawableLeft="@drawable/ic_missed_video_call"
tools:text="@string/call_tile_video_incoming" />
<androidx.constraintlayout.widget.ConstraintLayout
@ -80,7 +68,6 @@
style="@style/Widget.Vector.Button.Destructive"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginEnd="4dp"
android:minWidth="120dp"
app:layout_constraintEnd_toStartOf="@+id/itemCallAcceptView"

View file

@ -3242,12 +3242,24 @@
<string name="call_tile_you_started_call">You started a call</string>
<string name="call_tile_other_started_call">%1$s started a call</string>
<string name="call_tile_in_call">You\'re currently in this call</string>
<!-- Pattern can be replaced by the value of string call_tile_call_back -->
<string name="call_tile_you_declined">You declined this call %1$s</string>
<string name="call_tile_you_declined_call">You declined this call</string>
<string name="call_tile_other_declined">%1$s declined this call</string>
<string name="call_tile_ended">This call has ended</string>
<string name="call_tile_call_back">Call back</string>
<string name="call_tile_voice_incoming">Incoming voice call</string>
<string name="call_tile_video_incoming">Incoming video call</string>
<string name="call_tile_voice_active">Active voice call</string>
<string name="call_tile_video_active">Active video call</string>
<string name="call_tile_voice_call_has_ended">Voice call ended • %1$s</string>
<string name="call_tile_video_call_has_ended">Video call ended • %1$s</string>
<string name="call_tile_voice_declined">Voice call declined</string>
<string name="call_tile_video_declined">Video call declined</string>
<string name="call_tile_voice_missed">Missed voice call</string>
<string name="call_tile_video_missed">Missed video call</string>
<string name="call_tile_no_answer">No answer</string>
<string name="call_tile_connection_failed">Connection failed</string>
<string name="call_dial_pad_title">Dial pad</string>
<string name="call_dial_pad_lookup_error">"There was an error looking up the phone number"</string>