mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2025-03-16 03:08:58 +03:00
Jitsi call: implement RemoveJitsiWidgetView
This commit is contained in:
parent
b7e5a6cf28
commit
8e2a1d3bcd
10 changed files with 345 additions and 214 deletions
|
@ -1,110 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020 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.app.core.ui.views
|
||||
|
||||
import android.content.Context
|
||||
import android.text.SpannableString
|
||||
import android.text.method.LinkMovementMethod
|
||||
import android.text.style.ClickableSpan
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import android.widget.RelativeLayout
|
||||
import androidx.core.view.isVisible
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.utils.tappableMatchingText
|
||||
import im.vector.app.databinding.ViewActiveConferenceViewBinding
|
||||
import im.vector.app.features.home.room.detail.RoomDetailViewState
|
||||
import im.vector.app.features.themes.ThemeUtils
|
||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||
import org.matrix.android.sdk.api.session.widgets.model.Widget
|
||||
import org.matrix.android.sdk.api.session.widgets.model.WidgetType
|
||||
|
||||
class ActiveConferenceView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : RelativeLayout(context, attrs, defStyleAttr) {
|
||||
|
||||
interface Callback {
|
||||
fun onTapJoinAudio(jitsiWidget: Widget)
|
||||
fun onTapJoinVideo(jitsiWidget: Widget)
|
||||
fun onDelete(jitsiWidget: Widget)
|
||||
}
|
||||
|
||||
var callback: Callback? = null
|
||||
private var jitsiWidget: Widget? = null
|
||||
|
||||
private lateinit var views: ViewActiveConferenceViewBinding
|
||||
|
||||
init {
|
||||
setupView()
|
||||
}
|
||||
|
||||
private fun setupView() {
|
||||
inflate(context, R.layout.view_active_conference_view, this)
|
||||
views = ViewActiveConferenceViewBinding.bind(this)
|
||||
setBackgroundColor(ThemeUtils.getColor(context, R.attr.colorPrimary))
|
||||
|
||||
// "voice" and "video" texts are underlined and clickable
|
||||
val voiceString = context.getString(R.string.ongoing_conference_call_voice)
|
||||
val videoString = context.getString(R.string.ongoing_conference_call_video)
|
||||
|
||||
val fullMessage = context.getString(R.string.ongoing_conference_call, voiceString, videoString)
|
||||
|
||||
val styledText = SpannableString(fullMessage)
|
||||
styledText.tappableMatchingText(voiceString, object : ClickableSpan() {
|
||||
override fun onClick(widget: View) {
|
||||
jitsiWidget?.let {
|
||||
callback?.onTapJoinAudio(it)
|
||||
}
|
||||
}
|
||||
})
|
||||
styledText.tappableMatchingText(videoString, object : ClickableSpan() {
|
||||
override fun onClick(widget: View) {
|
||||
jitsiWidget?.let {
|
||||
callback?.onTapJoinVideo(it)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
views.activeConferenceInfo.apply {
|
||||
text = styledText
|
||||
movementMethod = LinkMovementMethod.getInstance()
|
||||
}
|
||||
|
||||
views.deleteWidgetButton.setOnClickListener {
|
||||
jitsiWidget?.let { callback?.onDelete(it) }
|
||||
}
|
||||
}
|
||||
|
||||
fun render(state: RoomDetailViewState) {
|
||||
val summary = state.asyncRoomSummary()
|
||||
if (summary?.membership == Membership.JOIN) {
|
||||
// We only display banner for 'live' widgets
|
||||
jitsiWidget = state.activeRoomWidgets()?.firstOrNull {
|
||||
// for now only jitsi?
|
||||
it.type == WidgetType.Jitsi
|
||||
}
|
||||
|
||||
isVisible = jitsiWidget != null
|
||||
// if sent by me or if i can moderate?
|
||||
views.deleteWidgetButton.isVisible = state.isAllowedToManageWidgets
|
||||
} else {
|
||||
isVisible = false
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,159 @@
|
|||
/*
|
||||
* Copyright (c) 2021 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.app.features.call.conference
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.res.ColorStateList
|
||||
import android.util.AttributeSet
|
||||
import android.view.MotionEvent
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.widget.ImageViewCompat
|
||||
import im.vector.app.R
|
||||
import im.vector.app.databinding.ViewRemoveJitsiWidgetBinding
|
||||
import im.vector.app.features.home.room.detail.RoomDetailViewState
|
||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility") class RemoveJitsiWidgetView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : ConstraintLayout(context, attrs, defStyleAttr) {
|
||||
|
||||
private sealed class State {
|
||||
object Unmount : State()
|
||||
object Idle : State()
|
||||
data class Sliding(val initialX: Float, val translationX: Float, val hasReachedActivationThreshold: Boolean) : State()
|
||||
object Progress : State()
|
||||
}
|
||||
|
||||
private val views: ViewRemoveJitsiWidgetBinding
|
||||
private var state: State = State.Unmount
|
||||
var onCompleteSliding: (() -> Unit)? = null
|
||||
|
||||
init {
|
||||
inflate(context, R.layout.view_remove_jitsi_widget, this)
|
||||
views = ViewRemoveJitsiWidgetBinding.bind(this)
|
||||
views.removeJitsiSlidingContainer.setOnTouchListener { _, event ->
|
||||
val currentState = state
|
||||
return@setOnTouchListener when (event.action) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
if (currentState == State.Idle) {
|
||||
val initialX = views.removeJitsiSlidingContainer.x - event.rawX
|
||||
updateState(State.Sliding(initialX, 0f, false))
|
||||
}
|
||||
true
|
||||
}
|
||||
MotionEvent.ACTION_UP,
|
||||
MotionEvent.ACTION_CANCEL -> {
|
||||
if (currentState is State.Sliding) {
|
||||
if (currentState.hasReachedActivationThreshold) {
|
||||
updateState(State.Progress)
|
||||
} else {
|
||||
updateState(State.Idle)
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
MotionEvent.ACTION_MOVE -> {
|
||||
if (currentState is State.Sliding) {
|
||||
val translationX = (currentState.initialX + event.rawX).coerceAtLeast(0f)
|
||||
val hasReachedActivationThreshold = views.removeJitsiSlidingContainer.width + translationX >= views.removeJitsiHangupContainer.x
|
||||
updateState(State.Sliding(currentState.initialX, translationX, hasReachedActivationThreshold))
|
||||
}
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
renderInternalState(state)
|
||||
}
|
||||
|
||||
fun render(roomDetailViewState: RoomDetailViewState) {
|
||||
val summary = roomDetailViewState.asyncRoomSummary()
|
||||
val newState = if (summary?.membership != Membership.JOIN || !roomDetailViewState.isAllowedToManageWidgets || roomDetailViewState.jitsiState.widgetId == null) {
|
||||
State.Unmount
|
||||
} else if (roomDetailViewState.jitsiState.deleteWidgetInProgress) {
|
||||
State.Progress
|
||||
} else {
|
||||
State.Idle
|
||||
}
|
||||
// Don't force Idle if we are already sliding
|
||||
if (state is State.Sliding && newState is State.Idle) {
|
||||
return
|
||||
} else {
|
||||
updateState(newState)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateState(newState: State) {
|
||||
if (newState == state) {
|
||||
return
|
||||
}
|
||||
renderInternalState(newState)
|
||||
state = newState
|
||||
if (state == State.Progress) {
|
||||
onCompleteSliding?.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
private fun renderInternalState(state: State) {
|
||||
isVisible = state != State.Unmount
|
||||
when (state) {
|
||||
State.Progress -> {
|
||||
isVisible = true
|
||||
views.updateVisibilities(true)
|
||||
views.updateHangupColors(true)
|
||||
}
|
||||
State.Idle -> {
|
||||
isVisible = true
|
||||
views.updateVisibilities(false)
|
||||
views.removeJitsiSlidingContainer.translationX = 0f
|
||||
views.updateHangupColors(false)
|
||||
}
|
||||
is State.Sliding -> {
|
||||
isVisible = true
|
||||
views.updateVisibilities(false)
|
||||
views.removeJitsiSlidingContainer.translationX = state.translationX
|
||||
views.updateHangupColors(state.hasReachedActivationThreshold)
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
private fun ViewRemoveJitsiWidgetBinding.updateVisibilities(isProgress: Boolean) {
|
||||
removeJitsiProgressContainer.isVisible = isProgress
|
||||
removeJitsiHangupContainer.isVisible = !isProgress
|
||||
removeJitsiSlidingContainer.isVisible = !isProgress
|
||||
}
|
||||
|
||||
private fun ViewRemoveJitsiWidgetBinding.updateHangupColors(activated: Boolean) {
|
||||
val iconTintColor: Int
|
||||
val bgColor: Int
|
||||
if (activated) {
|
||||
bgColor = ContextCompat.getColor(context, R.color.palette_vermilion)
|
||||
iconTintColor = ContextCompat.getColor(context, R.color.palette_white)
|
||||
} else {
|
||||
bgColor = ContextCompat.getColor(context, android.R.color.transparent)
|
||||
iconTintColor = ContextCompat.getColor(context, R.color.palette_vermilion)
|
||||
}
|
||||
removeJitsiHangupContainer.setBackgroundColor(bgColor)
|
||||
ImageViewCompat.setImageTintList(removeJitsiHangupIcon, ColorStateList.valueOf(iconTintColor))
|
||||
}
|
||||
}
|
|
@ -67,7 +67,6 @@ import com.airbnb.mvrx.Success
|
|||
import com.airbnb.mvrx.args
|
||||
import com.airbnb.mvrx.fragmentViewModel
|
||||
import com.airbnb.mvrx.withState
|
||||
import com.facebook.react.bridge.JavaOnlyMap
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.jakewharton.rxbinding3.view.focusChanges
|
||||
import com.jakewharton.rxbinding3.widget.textChanges
|
||||
|
@ -90,7 +89,6 @@ import im.vector.app.core.intent.getMimeTypeFromUri
|
|||
import im.vector.app.core.platform.VectorBaseFragment
|
||||
import im.vector.app.core.platform.showOptimizedSnackbar
|
||||
import im.vector.app.core.resources.ColorProvider
|
||||
import im.vector.app.core.ui.views.ActiveConferenceView
|
||||
import im.vector.app.core.ui.views.CurrentCallsCardView
|
||||
import im.vector.app.core.ui.views.FailedMessagesWarningView
|
||||
import im.vector.app.core.ui.views.NotificationAreaView
|
||||
|
@ -124,7 +122,6 @@ import im.vector.app.features.call.VectorCallActivity
|
|||
import im.vector.app.features.call.conference.JitsiBroadcastEmitter
|
||||
import im.vector.app.features.call.conference.JitsiBroadcastEventObserver
|
||||
import im.vector.app.features.call.conference.JitsiCallViewModel
|
||||
import im.vector.app.features.call.conference.extractConferenceUrl
|
||||
import im.vector.app.features.call.webrtc.WebRtcCallManager
|
||||
import im.vector.app.features.command.Command
|
||||
import im.vector.app.features.crypto.keysbackup.restore.KeysBackupRestoreActivity
|
||||
|
@ -178,7 +175,6 @@ import nl.dionsegijn.konfetti.models.Shape
|
|||
import nl.dionsegijn.konfetti.models.Size
|
||||
import org.billcarsonfr.jsonviewer.JSonViewerDialog
|
||||
import org.commonmark.parser.Parser
|
||||
import org.jitsi.meet.sdk.BroadcastEmitter
|
||||
import org.jitsi.meet.sdk.BroadcastEvent
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.session.content.ContentAttachmentData
|
||||
|
@ -331,9 +327,10 @@ class RoomDetailFragment @Inject constructor(
|
|||
setupJumpToReadMarkerView()
|
||||
setupActiveCallView()
|
||||
setupJumpToBottomView()
|
||||
setupConfBannerView()
|
||||
setupEmojiPopup()
|
||||
setupFailedMessagesWarningView()
|
||||
setupRemoveJitsiWidgetView()
|
||||
|
||||
|
||||
views.roomToolbarContentView.debouncedClicks {
|
||||
navigator.openRoomProfile(requireActivity(), roomDetailArgs.roomId)
|
||||
|
@ -397,7 +394,7 @@ class RoomDetailFragment @Inject constructor(
|
|||
RoomDetailViewEvents.OpenActiveWidgetBottomSheet -> onViewWidgetsClicked()
|
||||
is RoomDetailViewEvents.ShowInfoOkDialog -> showDialogWithMessage(it.message)
|
||||
is RoomDetailViewEvents.JoinJitsiConference -> joinJitsiRoom(it.widget, it.withVideo)
|
||||
RoomDetailViewEvents.LeaveJitsiConference -> leaveJitsiConference()
|
||||
RoomDetailViewEvents.LeaveJitsiConference -> leaveJitsiConference()
|
||||
RoomDetailViewEvents.ShowWaitingView -> vectorBaseActivity.showWaitingView()
|
||||
RoomDetailViewEvents.HideWaitingView -> vectorBaseActivity.hideWaitingView()
|
||||
is RoomDetailViewEvents.RequestNativeWidgetPermission -> requestNativeWidgetPermission(it)
|
||||
|
@ -420,6 +417,18 @@ class RoomDetailFragment @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun setupRemoveJitsiWidgetView() {
|
||||
views.removeJitsiWidgetView.onCompleteSliding = {
|
||||
withState(roomDetailViewModel) {
|
||||
val jitsiWidgetId = it.jitsiState.widgetId ?: return@withState
|
||||
if (it.jitsiState.hasJoined) {
|
||||
leaveJitsiConference()
|
||||
}
|
||||
roomDetailViewModel.handle(RoomDetailAction.RemoveWidget(jitsiWidgetId))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun leaveJitsiConference() {
|
||||
JitsiBroadcastEmitter(vectorBaseActivity).emitConferenceEnded()
|
||||
}
|
||||
|
@ -530,31 +539,6 @@ class RoomDetailFragment @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
private fun setupConfBannerView() {
|
||||
views.activeConferenceView.callback = object : ActiveConferenceView.Callback {
|
||||
override fun onTapJoinAudio(jitsiWidget: Widget) {
|
||||
// need to check if allowed first
|
||||
roomDetailViewModel.handle(RoomDetailAction.EnsureNativeWidgetAllowed(
|
||||
widget = jitsiWidget,
|
||||
userJustAccepted = false,
|
||||
grantedEvents = RoomDetailViewEvents.JoinJitsiConference(jitsiWidget, false))
|
||||
)
|
||||
}
|
||||
|
||||
override fun onTapJoinVideo(jitsiWidget: Widget) {
|
||||
roomDetailViewModel.handle(RoomDetailAction.EnsureNativeWidgetAllowed(
|
||||
widget = jitsiWidget,
|
||||
userJustAccepted = false,
|
||||
grantedEvents = RoomDetailViewEvents.JoinJitsiConference(jitsiWidget, true))
|
||||
)
|
||||
}
|
||||
|
||||
override fun onDelete(jitsiWidget: Widget) {
|
||||
roomDetailViewModel.handle(RoomDetailAction.RemoveWidget(jitsiWidget.widgetId))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupEmojiPopup() {
|
||||
emojiPopup = EmojiPopup
|
||||
.Builder
|
||||
|
@ -1261,7 +1245,7 @@ class RoomDetailFragment @Inject constructor(
|
|||
invalidateOptionsMenu()
|
||||
val summary = state.asyncRoomSummary()
|
||||
renderToolbar(summary, state.typingMessage)
|
||||
views.activeConferenceView.render(state)
|
||||
views.removeJitsiWidgetView.render(state)
|
||||
views.failedMessagesWarningView.render(state.hasFailedSending)
|
||||
val inviter = state.asyncInviter()
|
||||
if (summary?.membership == Membership.JOIN) {
|
||||
|
|
|
@ -60,12 +60,12 @@ import io.reactivex.Observable
|
|||
import io.reactivex.rxkotlin.subscribeBy
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.commonmark.parser.Parser
|
||||
import org.commonmark.renderer.html.HtmlRenderer
|
||||
import org.jitsi.meet.sdk.BroadcastEvent
|
||||
import org.jitsi.meet.sdk.JitsiMeet
|
||||
import org.matrix.android.sdk.api.MatrixCallback
|
||||
import org.matrix.android.sdk.api.MatrixPatterns
|
||||
import org.matrix.android.sdk.api.extensions.orFalse
|
||||
|
@ -241,18 +241,25 @@ class RoomDetailViewModel @AssistedInject constructor(
|
|||
widgets.filter { it.isActive }
|
||||
}
|
||||
.execute { widgets ->
|
||||
val jitsiWidget = widgets()?.firstOrNull { it.type == WidgetType.Jitsi }
|
||||
val jitsiConfId = jitsiWidget?.let {
|
||||
jitsiService.extractProperties(it)?.confId
|
||||
}
|
||||
copy(
|
||||
activeRoomWidgets = widgets,
|
||||
jitsiState = jitsiState.copy(
|
||||
confId = jitsiConfId,
|
||||
widgetId = jitsiWidget?.widgetId
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
asyncSubscribe(RoomDetailViewState::activeRoomWidgets) { widgets ->
|
||||
setState {
|
||||
val jitsiWidget = widgets.firstOrNull { it.type == WidgetType.Jitsi }
|
||||
val jitsiConfId = jitsiWidget?.let {
|
||||
jitsiService.extractProperties(it)?.confId
|
||||
}
|
||||
copy(
|
||||
jitsiState = jitsiState.copy(
|
||||
confId = jitsiConfId,
|
||||
widgetId = jitsiWidget?.widgetId
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun observeMyRoomMember() {
|
||||
|
@ -318,8 +325,8 @@ class RoomDetailViewModel @AssistedInject constructor(
|
|||
is RoomDetailAction.ManageIntegrations -> handleManageIntegrations()
|
||||
is RoomDetailAction.AddJitsiWidget -> handleAddJitsiConference(action)
|
||||
is RoomDetailAction.UpdateJoinJitsiCallStatus -> handleJitsiCallJoinStatus(action)
|
||||
is RoomDetailAction.JoinJitsiCall -> handleJoinJitsiCall()
|
||||
is RoomDetailAction.LeaveJitsiCall -> handleLeaveJitsiCall()
|
||||
is RoomDetailAction.JoinJitsiCall -> handleJoinJitsiCall()
|
||||
is RoomDetailAction.LeaveJitsiCall -> handleLeaveJitsiCall()
|
||||
is RoomDetailAction.RemoveWidget -> handleDeleteWidget(action.widgetId)
|
||||
is RoomDetailAction.EnsureNativeWidgetAllowed -> handleCheckWidgetAllowed(action)
|
||||
is RoomDetailAction.CancelSend -> handleCancel(action)
|
||||
|
@ -363,8 +370,8 @@ class RoomDetailViewModel @AssistedInject constructor(
|
|||
_viewEvents.post(RoomDetailViewEvents.LeaveJitsiConference)
|
||||
}
|
||||
|
||||
private fun handleJoinJitsiCall() = withState{ state ->
|
||||
val jitsiWidget = state.activeRoomWidgets()?.firstOrNull { it.widgetId == state.jitsiState.widgetId} ?: return@withState
|
||||
private fun handleJoinJitsiCall() = withState { state ->
|
||||
val jitsiWidget = state.activeRoomWidgets()?.firstOrNull { it.widgetId == state.jitsiState.widgetId } ?: return@withState
|
||||
val action = RoomDetailAction.EnsureNativeWidgetAllowed(jitsiWidget, false, RoomDetailViewEvents.JoinJitsiConference(jitsiWidget, true))
|
||||
handleCheckWidgetAllowed(action)
|
||||
}
|
||||
|
@ -477,10 +484,15 @@ class RoomDetailViewModel @AssistedInject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun handleDeleteWidget(widgetId: String) {
|
||||
_viewEvents.post(RoomDetailViewEvents.ShowWaitingView)
|
||||
private fun handleDeleteWidget(widgetId: String) = withState { state ->
|
||||
val isJitsiWidget = state.jitsiState.widgetId == widgetId
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
if (isJitsiWidget) {
|
||||
setState { copy(jitsiState = jitsiState.copy(deleteWidgetInProgress = true)) }
|
||||
} else {
|
||||
_viewEvents.post(RoomDetailViewEvents.ShowWaitingView)
|
||||
}
|
||||
session.widgetService().destroyRoomWidget(room.roomId, widgetId)
|
||||
// local echo
|
||||
setState {
|
||||
|
@ -496,7 +508,11 @@ class RoomDetailViewModel @AssistedInject constructor(
|
|||
} catch (failure: Throwable) {
|
||||
_viewEvents.post(RoomDetailViewEvents.ShowMessage(stringProvider.getString(R.string.failed_to_remove_widget)))
|
||||
} finally {
|
||||
_viewEvents.post(RoomDetailViewEvents.HideWaitingView)
|
||||
if (isJitsiWidget) {
|
||||
setState { copy(jitsiState = jitsiState.copy(deleteWidgetInProgress = false)) }
|
||||
} else {
|
||||
_viewEvents.post(RoomDetailViewEvents.HideWaitingView)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,7 +59,8 @@ data class JitsiState(
|
|||
val hasJoined: Boolean = false,
|
||||
// Not null if we have an active jitsi widget on the room
|
||||
val confId: String? = null,
|
||||
val widgetId: String? = null
|
||||
val widgetId: String? = null,
|
||||
val deleteWidgetInProgress: Boolean = false
|
||||
)
|
||||
|
||||
data class RoomDetailViewState(
|
||||
|
|
|
@ -38,13 +38,8 @@ class WidgetItemFactory @Inject constructor(
|
|||
private val avatarSizeProvider: AvatarSizeProvider,
|
||||
private val messageColorProvider: MessageColorProvider,
|
||||
private val avatarRenderer: AvatarRenderer,
|
||||
private val activeSessionDataSource: ActiveSessionDataSource,
|
||||
private val roomSummariesHolder: RoomSummariesHolder
|
||||
) {
|
||||
private val currentUserId: String?
|
||||
get() = activeSessionDataSource.currentValue?.orNull()?.myUserId
|
||||
|
||||
private fun Event.isSentByCurrentUser() = senderId != null && senderId == currentUserId
|
||||
|
||||
fun create(params: TimelineItemFactoryParams): VectorEpoxyModel<*>? {
|
||||
val event = params.event
|
||||
|
|
|
@ -100,13 +100,14 @@
|
|||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/appBarLayout" />
|
||||
|
||||
<im.vector.app.core.ui.views.ActiveConferenceView
|
||||
android:id="@+id/activeConferenceView"
|
||||
<im.vector.app.features.call.conference.RemoveJitsiWidgetView
|
||||
android:id="@+id/removeJitsiWidgetView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintTop_toBottomOf="@id/syncStateView"
|
||||
tools:visibility="visible" />
|
||||
android:visibility="visible"
|
||||
android:background="?android:colorBackground"
|
||||
android:minHeight="54dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/syncStateView"/>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/timelineRecyclerView"
|
||||
|
@ -116,7 +117,7 @@
|
|||
app:layout_constraintBottom_toTopOf="@+id/timelineRecyclerViewBarrier"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/activeConferenceView"
|
||||
app:layout_constraintTop_toBottomOf="@id/removeJitsiWidgetView"
|
||||
tools:listitem="@layout/item_timeline_event_base" />
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
|
@ -132,7 +133,7 @@
|
|||
android:visibility="invisible"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/activeConferenceView"
|
||||
app:layout_constraintTop_toBottomOf="@id/removeJitsiWidgetView"
|
||||
tools:visibility="visible" />
|
||||
|
||||
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?colorPrimary"
|
||||
tools:parentTag="android.widget.RelativeLayout">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/activeConferenceInfo"
|
||||
style="@style/Widget.Vector.TextView.Body"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_toStartOf="@id/deleteWidgetButton"
|
||||
android:drawablePadding="10dp"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingTop="12dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingBottom="12dp"
|
||||
android:textColor="?colorOnPrimary"
|
||||
android:textColorLink="?colorOnPrimary"
|
||||
app:drawableStartCompat="@drawable/ic_call_answer"
|
||||
app:drawableTint="?colorOnPrimary"
|
||||
tools:text="@string/ongoing_conference_call" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/deleteWidgetButton"
|
||||
style="@style/Widget.Vector.Button.Text.OnPrimary"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignTop="@+id/activeConferenceInfo"
|
||||
android:layout_alignBottom="@+id/activeConferenceInfo"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:clickable="false"
|
||||
android:focusable="false"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:text="@string/action_close"
|
||||
android:textStyle="bold" />
|
||||
|
||||
</merge>
|
123
vector/src/main/res/layout/view_remove_jitsi_widget.xml
Normal file
123
vector/src/main/res/layout/view_remove_jitsi_widget.xml
Normal file
|
@ -0,0 +1,123 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="54dp"
|
||||
tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout">
|
||||
|
||||
<LinearLayout android:id="@+id/removeJitsiProgressContainer"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
android:orientation="horizontal"
|
||||
android:visibility="gone"
|
||||
android:gravity="center_vertical"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<ProgressBar
|
||||
android:layout_marginStart="@dimen/layout_horizontal_margin"
|
||||
android:indeterminateTintMode="src_atop"
|
||||
android:indeterminateTint="?vctr_content_primary"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp" />
|
||||
|
||||
<TextView
|
||||
android:text="@string/call_remove_jitsi_widget_progress"
|
||||
style="@style/Widget.Vector.TextView.Body"
|
||||
android:textColor="?vctr_content_primary"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/removeJitsiSlidingContainer"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="0dp"
|
||||
android:visibility="visible"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/removeJitsiSlidingTextView"
|
||||
style="@style/Widget.Vector.TextView.Body"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/layout_horizontal_margin"
|
||||
android:text="@string/call_slide_to_end_conference"
|
||||
android:textColor="?vctr_content_primary" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:src="@drawable/ic_arrow_right"
|
||||
android:tint="?vctr_content_quaternary" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:alpha="0.5"
|
||||
android:src="@drawable/ic_arrow_right"
|
||||
android:tint="?vctr_content_quaternary" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:alpha="0.2"
|
||||
android:src="@drawable/ic_arrow_right"
|
||||
android:tint="?vctr_content_quaternary" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/removeJitsiHangupContainer"
|
||||
android:layout_width="88dp"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:background="@color/vector_warning_color_2">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/removeJitsiHangupIcon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:src="@drawable/ic_call_hangup" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="1dp"
|
||||
android:background="?attr/vctr_system"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<View
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="1dp"
|
||||
android:background="?attr/vctr_system"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
|
||||
</merge>
|
|
@ -742,6 +742,8 @@
|
|||
<string name="call_error_camera_init_failed">Cannot initialize the camera</string>
|
||||
<string name="call_error_answered_elsewhere">call answered elsewhere</string>
|
||||
|
||||
<string name="call_remove_jitsi_widget_progress">Ending call…</string>
|
||||
|
||||
<!-- medias picker string -->
|
||||
<string name="media_picker_both_capture_title">Take a picture or a video"</string>
|
||||
<string name="media_picker_cannot_record_video">Cannot record video"</string>
|
||||
|
@ -3247,6 +3249,8 @@
|
|||
<string name="call_transfer_transfer_to_title">Transfer to %1$s</string>
|
||||
<string name="call_transfer_unknown_person">Unknown person</string>
|
||||
|
||||
<string name="call_slide_to_end_conference">Slide to end the call for everyone</string>
|
||||
|
||||
<string name="re_authentication_activity_title">Re-Authentication Needed</string>
|
||||
<!-- Note to translators: the translation MUST contain the string "${app_name}", which will be replaced by the application name -->
|
||||
<string name="template_re_authentication_default_confirm_text">${app_name} requires you to enter your credentials to perform this action.</string>
|
||||
|
@ -3409,4 +3413,5 @@
|
|||
<string name="teammate_spaces_arent_quite_ready">"Teammate spaces aren’t quite ready but you can still give them a try"</string>
|
||||
<string name="teammate_spaces_might_not_join">"At the moment people might not be able to join any private rooms you make.\n\nWe’ll be improving this as part of the beta, but just wanted to let you know."</string>
|
||||
<string name="error_failed_to_join_room">Sorry, an error occurred while trying to join: %s</string>
|
||||
|
||||
</resources>
|
||||
|
|
Loading…
Add table
Reference in a new issue