Merge branch 'develop' into feature/event_type_filtering

This commit is contained in:
ganfra 2020-09-16 15:49:16 +02:00
commit 0ada12e646
12 changed files with 286 additions and 124 deletions

View file

@ -15,6 +15,7 @@ Bugfix 🐛:
- Various report of people that cannot play video (#2107) - Various report of people that cannot play video (#2107)
- Rooms incorrectly marked as unread (#588) - Rooms incorrectly marked as unread (#588)
- Allow users to show/hide room member state events (#1231) - Allow users to show/hide room member state events (#1231)
- Fix stuck on loader when launching home
Translations 🗣: Translations 🗣:
- -

View file

@ -22,36 +22,63 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentTransaction import androidx.fragment.app.FragmentTransaction
import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.platform.VectorBaseActivity
fun VectorBaseActivity.addFragment(frameId: Int, fragment: Fragment) { fun VectorBaseActivity.addFragment(
supportFragmentManager.commitTransaction { add(frameId, fragment) } frameId: Int,
fragment: Fragment,
allowStateLoss: Boolean = false
) {
supportFragmentManager.commitTransaction(allowStateLoss) { add(frameId, fragment) }
} }
fun <T : Fragment> VectorBaseActivity.addFragment(frameId: Int, fragmentClass: Class<T>, params: Parcelable? = null, tag: String? = null) { fun <T : Fragment> VectorBaseActivity.addFragment(
supportFragmentManager.commitTransaction { frameId: Int,
fragmentClass: Class<T>,
params: Parcelable? = null,
tag: String? = null,
allowStateLoss: Boolean = false
) {
supportFragmentManager.commitTransaction(allowStateLoss) {
add(frameId, fragmentClass, params.toMvRxBundle(), tag) add(frameId, fragmentClass, params.toMvRxBundle(), tag)
} }
} }
fun VectorBaseActivity.replaceFragment(frameId: Int, fragment: Fragment, tag: String? = null) { fun VectorBaseActivity.replaceFragment(
supportFragmentManager.commitTransaction { replace(frameId, fragment, tag) } frameId: Int,
fragment: Fragment,
tag: String? = null,
allowStateLoss: Boolean = false
) {
supportFragmentManager.commitTransaction(allowStateLoss) { replace(frameId, fragment, tag) }
} }
fun <T : Fragment> VectorBaseActivity.replaceFragment(frameId: Int, fragmentClass: Class<T>, params: Parcelable? = null, tag: String? = null) { fun <T : Fragment> VectorBaseActivity.replaceFragment(
supportFragmentManager.commitTransaction { frameId: Int,
fragmentClass: Class<T>,
params: Parcelable? = null,
tag: String? = null,
allowStateLoss: Boolean = false
) {
supportFragmentManager.commitTransaction(allowStateLoss) {
replace(frameId, fragmentClass, params.toMvRxBundle(), tag) replace(frameId, fragmentClass, params.toMvRxBundle(), tag)
} }
} }
fun VectorBaseActivity.addFragmentToBackstack(frameId: Int, fragment: Fragment, tag: String? = null) { fun VectorBaseActivity.addFragmentToBackstack(
supportFragmentManager.commitTransaction { replace(frameId, fragment).addToBackStack(tag) } frameId: Int,
fragment: Fragment,
tag: String? = null,
allowStateLoss: Boolean = false
) {
supportFragmentManager.commitTransaction(allowStateLoss) { replace(frameId, fragment).addToBackStack(tag) }
} }
fun <T : Fragment> VectorBaseActivity.addFragmentToBackstack(frameId: Int, fun <T : Fragment> VectorBaseActivity.addFragmentToBackstack(frameId: Int,
fragmentClass: Class<T>, fragmentClass: Class<T>,
params: Parcelable? = null, params: Parcelable? = null,
tag: String? = null, tag: String? = null,
allowStateLoss: Boolean = false,
option: ((FragmentTransaction) -> Unit)? = null) { option: ((FragmentTransaction) -> Unit)? = null) {
supportFragmentManager.commitTransaction { supportFragmentManager.commitTransaction(allowStateLoss) {
option?.invoke(this) option?.invoke(this)
replace(frameId, fragmentClass, params.toMvRxBundle(), tag).addToBackStack(tag) replace(frameId, fragmentClass, params.toMvRxBundle(), tag).addToBackStack(tag)
} }

View file

@ -26,62 +26,126 @@ import java.text.SimpleDateFormat
import java.util.Date import java.util.Date
import java.util.Locale import java.util.Locale
fun VectorBaseFragment.addFragment(frameId: Int, fragment: Fragment) { fun VectorBaseFragment.addFragment(
parentFragmentManager.commitTransaction { add(frameId, fragment) } frameId: Int,
fragment: Fragment,
allowStateLoss: Boolean = false
) {
parentFragmentManager.commitTransaction(allowStateLoss) { add(frameId, fragment) }
} }
fun <T : Fragment> VectorBaseFragment.addFragment(frameId: Int, fragmentClass: Class<T>, params: Parcelable? = null, tag: String? = null) { fun <T : Fragment> VectorBaseFragment.addFragment(
parentFragmentManager.commitTransaction { frameId: Int,
fragmentClass: Class<T>,
params: Parcelable? = null,
tag: String? = null,
allowStateLoss: Boolean = false
) {
parentFragmentManager.commitTransaction(allowStateLoss) {
add(frameId, fragmentClass, params.toMvRxBundle(), tag) add(frameId, fragmentClass, params.toMvRxBundle(), tag)
} }
} }
fun VectorBaseFragment.replaceFragment(frameId: Int, fragment: Fragment) { fun VectorBaseFragment.replaceFragment(
parentFragmentManager.commitTransaction { replace(frameId, fragment) } frameId: Int,
fragment: Fragment,
allowStateLoss: Boolean = false
) {
parentFragmentManager.commitTransaction(allowStateLoss) { replace(frameId, fragment) }
} }
fun <T : Fragment> VectorBaseFragment.replaceFragment(frameId: Int, fragmentClass: Class<T>, params: Parcelable? = null, tag: String? = null) { fun <T : Fragment> VectorBaseFragment.replaceFragment(
parentFragmentManager.commitTransaction { frameId: Int,
fragmentClass: Class<T>,
params: Parcelable? = null,
tag: String? = null,
allowStateLoss: Boolean = false
) {
parentFragmentManager.commitTransaction(allowStateLoss) {
replace(frameId, fragmentClass, params.toMvRxBundle(), tag) replace(frameId, fragmentClass, params.toMvRxBundle(), tag)
} }
} }
fun VectorBaseFragment.addFragmentToBackstack(frameId: Int, fragment: Fragment, tag: String? = null) { fun VectorBaseFragment.addFragmentToBackstack(
parentFragmentManager.commitTransaction { replace(frameId, fragment, tag).addToBackStack(tag) } frameId: Int,
fragment: Fragment,
tag: String? = null,
allowStateLoss: Boolean = false
) {
parentFragmentManager.commitTransaction(allowStateLoss) { replace(frameId, fragment, tag).addToBackStack(tag) }
} }
fun <T : Fragment> VectorBaseFragment.addFragmentToBackstack(frameId: Int, fragmentClass: Class<T>, params: Parcelable? = null, tag: String? = null) { fun <T : Fragment> VectorBaseFragment.addFragmentToBackstack(
parentFragmentManager.commitTransaction { frameId: Int,
fragmentClass: Class<T>,
params: Parcelable? = null,
tag: String? = null,
allowStateLoss: Boolean = false
) {
parentFragmentManager.commitTransaction(allowStateLoss) {
replace(frameId, fragmentClass, params.toMvRxBundle(), tag).addToBackStack(tag) replace(frameId, fragmentClass, params.toMvRxBundle(), tag).addToBackStack(tag)
} }
} }
fun VectorBaseFragment.addChildFragment(frameId: Int, fragment: Fragment, tag: String? = null) { fun VectorBaseFragment.addChildFragment(
childFragmentManager.commitTransaction { add(frameId, fragment, tag) } frameId: Int,
fragment: Fragment,
tag: String? = null,
allowStateLoss: Boolean = false
) {
childFragmentManager.commitTransaction(allowStateLoss) { add(frameId, fragment, tag) }
} }
fun <T : Fragment> VectorBaseFragment.addChildFragment(frameId: Int, fragmentClass: Class<T>, params: Parcelable? = null, tag: String? = null) { fun <T : Fragment> VectorBaseFragment.addChildFragment(
childFragmentManager.commitTransaction { frameId: Int,
fragmentClass: Class<T>,
params: Parcelable? = null,
tag: String? = null,
allowStateLoss: Boolean = false
) {
childFragmentManager.commitTransaction(allowStateLoss) {
add(frameId, fragmentClass, params.toMvRxBundle(), tag) add(frameId, fragmentClass, params.toMvRxBundle(), tag)
} }
} }
fun VectorBaseFragment.replaceChildFragment(frameId: Int, fragment: Fragment, tag: String? = null) { fun VectorBaseFragment.replaceChildFragment(
childFragmentManager.commitTransaction { replace(frameId, fragment, tag) } frameId: Int,
fragment: Fragment,
tag: String? = null,
allowStateLoss: Boolean = false
) {
childFragmentManager.commitTransaction(allowStateLoss) { replace(frameId, fragment, tag) }
} }
fun <T : Fragment> VectorBaseFragment.replaceChildFragment(frameId: Int, fragmentClass: Class<T>, params: Parcelable? = null, tag: String? = null) { fun <T : Fragment> VectorBaseFragment.replaceChildFragment(
childFragmentManager.commitTransaction { frameId: Int,
fragmentClass: Class<T>,
params: Parcelable? = null,
tag: String? = null,
allowStateLoss: Boolean = false
) {
childFragmentManager.commitTransaction(allowStateLoss) {
replace(frameId, fragmentClass, params.toMvRxBundle(), tag) replace(frameId, fragmentClass, params.toMvRxBundle(), tag)
} }
} }
fun VectorBaseFragment.addChildFragmentToBackstack(frameId: Int, fragment: Fragment, tag: String? = null) { fun VectorBaseFragment.addChildFragmentToBackstack(
childFragmentManager.commitTransaction { replace(frameId, fragment).addToBackStack(tag) } frameId: Int,
fragment: Fragment,
tag: String? = null,
allowStateLoss: Boolean = false
) {
childFragmentManager.commitTransaction(allowStateLoss) { replace(frameId, fragment).addToBackStack(tag) }
} }
fun <T : Fragment> VectorBaseFragment.addChildFragmentToBackstack(frameId: Int, fragmentClass: Class<T>, params: Parcelable? = null, tag: String? = null) { fun <T : Fragment> VectorBaseFragment.addChildFragmentToBackstack(
childFragmentManager.commitTransaction { frameId: Int,
fragmentClass: Class<T>,
params: Parcelable? = null,
tag: String? = null,
allowStateLoss: Boolean = false
) {
childFragmentManager.commitTransaction(allowStateLoss) {
replace(frameId, fragmentClass, params.toMvRxBundle(), tag).addToBackStack(tag) replace(frameId, fragmentClass, params.toMvRxBundle(), tag).addToBackStack(tag)
} }
} }

View file

@ -27,6 +27,11 @@ inline fun androidx.fragment.app.FragmentManager.commitTransactionNow(func: Frag
} }
} }
inline fun androidx.fragment.app.FragmentManager.commitTransaction(func: FragmentTransaction.() -> FragmentTransaction) { inline fun androidx.fragment.app.FragmentManager.commitTransaction(allowStateLoss: Boolean = false, func: FragmentTransaction.() -> FragmentTransaction) {
beginTransaction().func().commit() val transaction = beginTransaction().func()
if (allowStateLoss) {
transaction.commitAllowingStateLoss()
} else {
transaction.commit()
}
} }

View file

@ -0,0 +1,32 @@
/*
* 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.features.call
import im.vector.app.core.platform.VectorViewModelAction
sealed class VectorCallViewActions : VectorViewModelAction {
object EndCall : VectorCallViewActions()
object AcceptCall : VectorCallViewActions()
object DeclineCall : VectorCallViewActions()
object ToggleMute : VectorCallViewActions()
object ToggleVideo : VectorCallViewActions()
data class ChangeAudioDevice(val device: CallAudioManager.SoundDevice) : VectorCallViewActions()
object SwitchSoundDevice : VectorCallViewActions()
object HeadSetButtonPressed : VectorCallViewActions()
object ToggleCamera : VectorCallViewActions()
object ToggleHDSD : VectorCallViewActions()
}

View file

@ -0,0 +1,33 @@
/*
* 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.features.call
import im.vector.app.core.platform.VectorViewEvents
import org.matrix.android.sdk.api.session.call.TurnServerResponse
sealed class VectorCallViewEvents : VectorViewEvents {
object DismissNoCall : VectorCallViewEvents()
data class ConnectionTimeout(val turn: TurnServerResponse?) : VectorCallViewEvents()
data class ShowSoundDeviceChooser(
val available: List<CallAudioManager.SoundDevice>,
val current: CallAudioManager.SoundDevice
) : VectorCallViewEvents()
// data class CallAnswered(val content: CallAnswerContent) : VectorCallViewEvents()
// data class CallHangup(val content: CallHangupContent) : VectorCallViewEvents()
// object CallAccepted : VectorCallViewEvents()
}

View file

@ -16,10 +16,8 @@
package im.vector.app.features.call package im.vector.app.features.call
import com.airbnb.mvrx.Async
import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Loading import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Success import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.Uninitialized
@ -27,9 +25,7 @@ import com.airbnb.mvrx.ViewModelContext
import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject import com.squareup.inject.assisted.AssistedInject
import im.vector.app.core.extensions.exhaustive import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewEvents
import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.platform.VectorViewModelAction
import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.call.CallState import org.matrix.android.sdk.api.session.call.CallState
@ -41,48 +37,6 @@ import org.webrtc.PeerConnection
import java.util.Timer import java.util.Timer
import java.util.TimerTask import java.util.TimerTask
data class VectorCallViewState(
val callId: String? = null,
val roomId: String = "",
val isVideoCall: Boolean,
val isAudioMuted: Boolean = false,
val isVideoEnabled: Boolean = true,
val isVideoCaptureInError: Boolean = false,
val isHD: Boolean = false,
val isFrontCamera: Boolean = true,
val canSwitchCamera: Boolean = true,
val soundDevice: CallAudioManager.SoundDevice = CallAudioManager.SoundDevice.PHONE,
val availableSoundDevices: List<CallAudioManager.SoundDevice> = emptyList(),
val otherUserMatrixItem: Async<MatrixItem> = Uninitialized,
val callState: Async<CallState> = Uninitialized
) : MvRxState
sealed class VectorCallViewActions : VectorViewModelAction {
object EndCall : VectorCallViewActions()
object AcceptCall : VectorCallViewActions()
object DeclineCall : VectorCallViewActions()
object ToggleMute : VectorCallViewActions()
object ToggleVideo : VectorCallViewActions()
data class ChangeAudioDevice(val device: CallAudioManager.SoundDevice) : VectorCallViewActions()
object SwitchSoundDevice : VectorCallViewActions()
object HeadSetButtonPressed : VectorCallViewActions()
object ToggleCamera : VectorCallViewActions()
object ToggleHDSD : VectorCallViewActions()
}
sealed class VectorCallViewEvents : VectorViewEvents {
object DismissNoCall : VectorCallViewEvents()
data class ConnectionTimeout(val turn: TurnServerResponse?) : VectorCallViewEvents()
data class ShowSoundDeviceChooser(
val available: List<CallAudioManager.SoundDevice>,
val current: CallAudioManager.SoundDevice
) : VectorCallViewEvents()
// data class CallAnswered(val content: CallAnswerContent) : VectorCallViewEvents()
// data class CallHangup(val content: CallHangupContent) : VectorCallViewEvents()
// object CallAccepted : VectorCallViewEvents()
}
class VectorCallViewModel @AssistedInject constructor( class VectorCallViewModel @AssistedInject constructor(
@Assisted initialState: VectorCallViewState, @Assisted initialState: VectorCallViewState,
@Assisted val args: CallArgs, @Assisted val args: CallArgs,
@ -91,23 +45,23 @@ class VectorCallViewModel @AssistedInject constructor(
val proximityManager: CallProximityManager val proximityManager: CallProximityManager
) : VectorViewModel<VectorCallViewState, VectorCallViewActions, VectorCallViewEvents>(initialState) { ) : VectorViewModel<VectorCallViewState, VectorCallViewActions, VectorCallViewEvents>(initialState) {
var call: MxCall? = null private var call: MxCall? = null
var connectionTimoutTimer: Timer? = null private var connectionTimeoutTimer: Timer? = null
var hasBeenConnectedOnce = false private var hasBeenConnectedOnce = false
private val callStateListener = object : MxCall.StateListener { private val callStateListener = object : MxCall.StateListener {
override fun onStateUpdate(call: MxCall) { override fun onStateUpdate(call: MxCall) {
val callState = call.state val callState = call.state
if (callState is CallState.Connected && callState.iceConnectionState == PeerConnection.PeerConnectionState.CONNECTED) { if (callState is CallState.Connected && callState.iceConnectionState == PeerConnection.PeerConnectionState.CONNECTED) {
hasBeenConnectedOnce = true hasBeenConnectedOnce = true
connectionTimoutTimer?.cancel() connectionTimeoutTimer?.cancel()
connectionTimoutTimer = null connectionTimeoutTimer = null
} else { } else {
// do we reset as long as it's moving? // do we reset as long as it's moving?
connectionTimoutTimer?.cancel() connectionTimeoutTimer?.cancel()
if (hasBeenConnectedOnce) { if (hasBeenConnectedOnce) {
connectionTimoutTimer = Timer().apply { connectionTimeoutTimer = Timer().apply {
schedule(object : TimerTask() { schedule(object : TimerTask() {
override fun run() { override fun run() {
session.callSignalingService().getTurnServer(object : MatrixCallback<TurnServerResponse> { session.callSignalingService().getTurnServer(object : MatrixCallback<TurnServerResponse> {
@ -136,17 +90,17 @@ class VectorCallViewModel @AssistedInject constructor(
override fun onCurrentCallChange(call: MxCall?) { override fun onCurrentCallChange(call: MxCall?) {
} }
override fun onCaptureStateChanged(mgr: WebRtcPeerConnectionManager) { override fun onCaptureStateChanged() {
setState { setState {
copy( copy(
isVideoCaptureInError = mgr.capturerIsInError, isVideoCaptureInError = webRtcPeerConnectionManager.capturerIsInError,
isHD = mgr.currentCaptureFormat() is CaptureFormat.HD isHD = webRtcPeerConnectionManager.currentCaptureFormat() is CaptureFormat.HD
) )
} }
} }
override fun onAudioDevicesChange(mgr: WebRtcPeerConnectionManager) { override fun onAudioDevicesChange() {
val currentSoundDevice = mgr.audioManager.getCurrentSoundDevice() val currentSoundDevice = webRtcPeerConnectionManager.callAudioManager.getCurrentSoundDevice()
if (currentSoundDevice == CallAudioManager.SoundDevice.PHONE) { if (currentSoundDevice == CallAudioManager.SoundDevice.PHONE) {
proximityManager.start() proximityManager.start()
} else { } else {
@ -155,17 +109,17 @@ class VectorCallViewModel @AssistedInject constructor(
setState { setState {
copy( copy(
availableSoundDevices = mgr.audioManager.getAvailableSoundDevices(), availableSoundDevices = webRtcPeerConnectionManager.callAudioManager.getAvailableSoundDevices(),
soundDevice = currentSoundDevice soundDevice = currentSoundDevice
) )
} }
} }
override fun onCameraChange(mgr: WebRtcPeerConnectionManager) { override fun onCameraChange() {
setState { setState {
copy( copy(
canSwitchCamera = mgr.canSwitchCamera(), canSwitchCamera = webRtcPeerConnectionManager.canSwitchCamera(),
isFrontCamera = mgr.currentCameraType() == CameraType.FRONT isFrontCamera = webRtcPeerConnectionManager.currentCameraType() == CameraType.FRONT
) )
} }
} }
@ -182,7 +136,7 @@ class VectorCallViewModel @AssistedInject constructor(
mxCall.addListener(callStateListener) mxCall.addListener(callStateListener)
val currentSoundDevice = webRtcPeerConnectionManager.audioManager.getCurrentSoundDevice() val currentSoundDevice = webRtcPeerConnectionManager.callAudioManager.getCurrentSoundDevice()
if (currentSoundDevice == CallAudioManager.SoundDevice.PHONE) { if (currentSoundDevice == CallAudioManager.SoundDevice.PHONE) {
proximityManager.start() proximityManager.start()
} }
@ -193,7 +147,7 @@ class VectorCallViewModel @AssistedInject constructor(
callState = Success(mxCall.state), callState = Success(mxCall.state),
otherUserMatrixItem = item?.let { Success(it) } ?: Uninitialized, otherUserMatrixItem = item?.let { Success(it) } ?: Uninitialized,
soundDevice = currentSoundDevice, soundDevice = currentSoundDevice,
availableSoundDevices = webRtcPeerConnectionManager.audioManager.getAvailableSoundDevices(), availableSoundDevices = webRtcPeerConnectionManager.callAudioManager.getAvailableSoundDevices(),
isFrontCamera = webRtcPeerConnectionManager.currentCameraType() == CameraType.FRONT, isFrontCamera = webRtcPeerConnectionManager.currentCameraType() == CameraType.FRONT,
canSwitchCamera = webRtcPeerConnectionManager.canSwitchCamera(), canSwitchCamera = webRtcPeerConnectionManager.canSwitchCamera(),
isHD = mxCall.isVideoCall && webRtcPeerConnectionManager.currentCaptureFormat() is CaptureFormat.HD isHD = mxCall.isVideoCall && webRtcPeerConnectionManager.currentCaptureFormat() is CaptureFormat.HD
@ -250,10 +204,10 @@ class VectorCallViewModel @AssistedInject constructor(
Unit Unit
} }
is VectorCallViewActions.ChangeAudioDevice -> { is VectorCallViewActions.ChangeAudioDevice -> {
webRtcPeerConnectionManager.audioManager.setCurrentSoundDevice(action.device) webRtcPeerConnectionManager.callAudioManager.setCurrentSoundDevice(action.device)
setState { setState {
copy( copy(
soundDevice = webRtcPeerConnectionManager.audioManager.getCurrentSoundDevice() soundDevice = webRtcPeerConnectionManager.callAudioManager.getCurrentSoundDevice()
) )
} }
} }

View file

@ -0,0 +1,39 @@
/*
* 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.features.call
import com.airbnb.mvrx.Async
import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.Uninitialized
import org.matrix.android.sdk.api.session.call.CallState
import org.matrix.android.sdk.api.util.MatrixItem
data class VectorCallViewState(
val callId: String? = null,
val roomId: String = "",
val isVideoCall: Boolean,
val isAudioMuted: Boolean = false,
val isVideoEnabled: Boolean = true,
val isVideoCaptureInError: Boolean = false,
val isHD: Boolean = false,
val isFrontCamera: Boolean = true,
val canSwitchCamera: Boolean = true,
val soundDevice: CallAudioManager.SoundDevice = CallAudioManager.SoundDevice.PHONE,
val availableSoundDevices: List<CallAudioManager.SoundDevice> = emptyList(),
val otherUserMatrixItem: Async<MatrixItem> = Uninitialized,
val callState: Async<CallState> = Uninitialized
) : MvRxState

View file

@ -23,6 +23,9 @@ import im.vector.app.ActiveSessionDataSource
import im.vector.app.core.services.BluetoothHeadsetReceiver import im.vector.app.core.services.BluetoothHeadsetReceiver
import im.vector.app.core.services.CallService import im.vector.app.core.services.CallService
import im.vector.app.core.services.WiredHeadsetStateReceiver import im.vector.app.core.services.WiredHeadsetStateReceiver
import io.reactivex.disposables.Disposable
import io.reactivex.subjects.PublishSubject
import io.reactivex.subjects.ReplaySubject
import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.extensions.tryThis import org.matrix.android.sdk.api.extensions.tryThis
import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.Session
@ -35,9 +38,6 @@ import org.matrix.android.sdk.api.session.room.model.call.CallAnswerContent
import org.matrix.android.sdk.api.session.room.model.call.CallCandidatesContent import org.matrix.android.sdk.api.session.room.model.call.CallCandidatesContent
import org.matrix.android.sdk.api.session.room.model.call.CallHangupContent import org.matrix.android.sdk.api.session.room.model.call.CallHangupContent
import org.matrix.android.sdk.api.session.room.model.call.CallInviteContent import org.matrix.android.sdk.api.session.room.model.call.CallInviteContent
import io.reactivex.disposables.Disposable
import io.reactivex.subjects.PublishSubject
import io.reactivex.subjects.ReplaySubject
import org.webrtc.AudioSource import org.webrtc.AudioSource
import org.webrtc.AudioTrack import org.webrtc.AudioTrack
import org.webrtc.Camera1Enumerator import org.webrtc.Camera1Enumerator
@ -79,9 +79,9 @@ class WebRtcPeerConnectionManager @Inject constructor(
interface CurrentCallListener { interface CurrentCallListener {
fun onCurrentCallChange(call: MxCall?) fun onCurrentCallChange(call: MxCall?)
fun onCaptureStateChanged(mgr: WebRtcPeerConnectionManager) {} fun onCaptureStateChanged() {}
fun onAudioDevicesChange(mgr: WebRtcPeerConnectionManager) {} fun onAudioDevicesChange() {}
fun onCameraChange(mgr: WebRtcPeerConnectionManager) {} fun onCameraChange() {}
} }
private val currentCallsListeners = emptyList<CurrentCallListener>().toMutableList() private val currentCallsListeners = emptyList<CurrentCallListener>().toMutableList()
@ -93,9 +93,9 @@ class WebRtcPeerConnectionManager @Inject constructor(
currentCallsListeners.remove(listener) currentCallsListeners.remove(listener)
} }
val audioManager = CallAudioManager(context.applicationContext) { val callAudioManager = CallAudioManager(context.applicationContext) {
currentCallsListeners.forEach { currentCallsListeners.forEach {
tryThis { it.onAudioDevicesChange(this) } tryThis { it.onAudioDevicesChange() }
} }
} }
@ -174,7 +174,7 @@ class WebRtcPeerConnectionManager @Inject constructor(
set(value) { set(value) {
field = value field = value
currentCallsListeners.forEach { currentCallsListeners.forEach {
tryThis { it.onCaptureStateChanged(this) } tryThis { it.onCaptureStateChanged() }
} }
} }
@ -577,7 +577,7 @@ class WebRtcPeerConnectionManager @Inject constructor(
fun close() { fun close() {
Timber.v("## VOIP WebRtcPeerConnectionManager close() >") Timber.v("## VOIP WebRtcPeerConnectionManager close() >")
CallService.onNoActiveCall(context) CallService.onNoActiveCall(context)
audioManager.stop() callAudioManager.stop()
val callToEnd = currentCall val callToEnd = currentCall
currentCall = null currentCall = null
// This must be done in this thread // This must be done in this thread
@ -631,7 +631,7 @@ class WebRtcPeerConnectionManager @Inject constructor(
val createdCall = currentSession?.callSignalingService()?.createOutgoingCall(signalingRoomId, otherUserId, isVideoCall) ?: return val createdCall = currentSession?.callSignalingService()?.createOutgoingCall(signalingRoomId, otherUserId, isVideoCall) ?: return
val callContext = CallContext(createdCall) val callContext = CallContext(createdCall)
audioManager.startForCall(createdCall) callAudioManager.startForCall(createdCall)
currentCall = callContext currentCall = callContext
val name = currentSession?.getUser(createdCall.otherUserId)?.getBestName() val name = currentSession?.getUser(createdCall.otherUserId)?.getBestName()
@ -684,7 +684,7 @@ class WebRtcPeerConnectionManager @Inject constructor(
val callContext = CallContext(mxCall) val callContext = CallContext(mxCall)
currentCall = callContext currentCall = callContext
audioManager.startForCall(mxCall) callAudioManager.startForCall(mxCall)
executor.execute { executor.execute {
callContext.remoteCandidateSource = ReplaySubject.create() callContext.remoteCandidateSource = ReplaySubject.create()
} }
@ -745,7 +745,7 @@ class WebRtcPeerConnectionManager @Inject constructor(
} }
currentCallsListeners.forEach { currentCallsListeners.forEach {
tryThis { it.onCameraChange(this@WebRtcPeerConnectionManager) } tryThis { it.onCameraChange() }
} }
} }
@ -771,7 +771,7 @@ class WebRtcPeerConnectionManager @Inject constructor(
// videoCapturer?.stopCapture() // videoCapturer?.stopCapture()
videoCapturer?.changeCaptureFormat(format.width, format.height, format.fps) videoCapturer?.changeCaptureFormat(format.width, format.height, format.fps)
currentCaptureMode = format currentCaptureMode = format
currentCallsListeners.forEach { tryThis { it.onCaptureStateChanged(this) } } currentCallsListeners.forEach { tryThis { it.onCaptureStateChanged() } }
} }
} }
@ -802,12 +802,12 @@ class WebRtcPeerConnectionManager @Inject constructor(
Timber.v("## VOIP onWiredDeviceEvent $event") Timber.v("## VOIP onWiredDeviceEvent $event")
currentCall ?: return currentCall ?: return
// sometimes we received un-wanted unplugged... // sometimes we received un-wanted unplugged...
audioManager.wiredStateChange(event) callAudioManager.wiredStateChange(event)
} }
fun onWirelessDeviceEvent(event: BluetoothHeadsetReceiver.BTHeadsetPlugEvent) { fun onWirelessDeviceEvent(event: BluetoothHeadsetReceiver.BTHeadsetPlugEvent) {
Timber.v("## VOIP onWirelessDeviceEvent $event") Timber.v("## VOIP onWirelessDeviceEvent $event")
audioManager.bluetoothStateChange(event.plugged) callAudioManager.bluetoothStateChange(event.plugged)
} }
override fun onCallAnswerReceived(callAnswerContent: CallAnswerContent) { override fun onCallAnswerReceived(callAnswerContent: CallAnswerContent) {
@ -862,7 +862,7 @@ class WebRtcPeerConnectionManager @Inject constructor(
*/ */
PeerConnection.PeerConnectionState.CONNECTED -> { PeerConnection.PeerConnectionState.CONNECTED -> {
callContext.mxCall.state = CallState.Connected(newState) callContext.mxCall.state = CallState.Connected(newState)
audioManager.onCallConnected(callContext.mxCall) callAudioManager.onCallConnected(callContext.mxCall)
} }
/** /**
* One or more of the ICE transports on the connection is in the "failed" state. * One or more of the ICE transports on the connection is in the "failed" state.

View file

@ -87,13 +87,13 @@ class KeysBackupRestoreActivity : SimpleFragmentActivity() {
viewModel.navigateEvent.observeEvent(this) { uxStateEvent -> viewModel.navigateEvent.observeEvent(this) { uxStateEvent ->
when (uxStateEvent) { when (uxStateEvent) {
KeysBackupRestoreSharedViewModel.NAVIGATE_TO_RECOVER_WITH_KEY -> { KeysBackupRestoreSharedViewModel.NAVIGATE_TO_RECOVER_WITH_KEY -> {
addFragmentToBackstack(R.id.container, KeysBackupRestoreFromKeyFragment::class.java) addFragmentToBackstack(R.id.container, KeysBackupRestoreFromKeyFragment::class.java, allowStateLoss = true)
} }
KeysBackupRestoreSharedViewModel.NAVIGATE_TO_SUCCESS -> { KeysBackupRestoreSharedViewModel.NAVIGATE_TO_SUCCESS -> {
viewModel.keyVersionResult.value?.version?.let { viewModel.keyVersionResult.value?.version?.let {
KeysBackupBanner.onRecoverDoneForVersion(this, it) KeysBackupBanner.onRecoverDoneForVersion(this, it)
} }
replaceFragment(R.id.container, KeysBackupRestoreSuccessFragment::class.java) replaceFragment(R.id.container, KeysBackupRestoreSuccessFragment::class.java, allowStateLoss = true)
} }
KeysBackupRestoreSharedViewModel.NAVIGATE_TO_4S -> { KeysBackupRestoreSharedViewModel.NAVIGATE_TO_4S -> {
launch4SActivity() launch4SActivity()

View file

@ -121,7 +121,7 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
is HomeActivitySharedAction.CloseDrawer -> drawerLayout.closeDrawer(GravityCompat.START) is HomeActivitySharedAction.CloseDrawer -> drawerLayout.closeDrawer(GravityCompat.START)
is HomeActivitySharedAction.OpenGroup -> { is HomeActivitySharedAction.OpenGroup -> {
drawerLayout.closeDrawer(GravityCompat.START) drawerLayout.closeDrawer(GravityCompat.START)
replaceFragment(R.id.homeDetailFragmentContainer, HomeDetailFragment::class.java) replaceFragment(R.id.homeDetailFragmentContainer, HomeDetailFragment::class.java, allowStateLoss = true)
} }
}.exhaustive }.exhaustive
} }

View file

@ -20,6 +20,7 @@ import android.content.Intent
import android.net.Uri import android.net.Uri
import android.provider.Settings import android.provider.Settings
import androidx.preference.Preference import androidx.preference.Preference
import im.vector.app.BuildConfig
import org.matrix.android.sdk.api.Matrix import org.matrix.android.sdk.api.Matrix
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.preference.VectorPreference import im.vector.app.core.preference.VectorPreference
@ -58,7 +59,13 @@ class VectorSettingsHelpAboutFragment @Inject constructor(
// application version // application version
findPreference<VectorPreference>(VectorPreferences.SETTINGS_VERSION_PREFERENCE_KEY)!!.let { findPreference<VectorPreference>(VectorPreferences.SETTINGS_VERSION_PREFERENCE_KEY)!!.let {
it.summary = versionProvider.getVersion(longFormat = false, useBuildNumber = true) it.summary = buildString {
append(versionProvider.getVersion(longFormat = false, useBuildNumber = true))
if (BuildConfig.DEBUG) {
append(" ")
append(BuildConfig.GIT_BRANCH_NAME)
}
}
it.setOnPreferenceClickListener { pref -> it.setOnPreferenceClickListener { pref ->
copyToClipboard(requireContext(), pref.summary) copyToClipboard(requireContext(), pref.summary)