Handle more call recording states

more call recording states are:
3 = Starting video recording
4 = Starting audio recording
5 = Recording failed

these actions were added:
- Show grey recording icon to moderators if recording is starting
- Show toast to moderators if recording failed
- Add system message for recording failed

Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
This commit is contained in:
Marcel Hibbe 2023-02-22 12:08:17 +01:00
parent c1b79ac4dd
commit 499e022114
No known key found for this signature in database
GPG Key ID: C793F8B59F43CE7B
8 changed files with 89 additions and 41 deletions

View File

@ -445,30 +445,44 @@ public class CallActivity extends CallBaseActivity {
callRecordingViewModel.getViewState().observe(this, viewState -> {
if (viewState instanceof CallRecordingViewModel.RecordingStartedState) {
binding.callRecordingIndicator.setImageResource(R.drawable.record_stop);
binding.callRecordingIndicator.setVisibility(View.VISIBLE);
if (((CallRecordingViewModel.RecordingStartedState) viewState).getShowStartedInfo()) {
VibrationUtils.INSTANCE.vibrateShort(context);
Toast.makeText(context, context.getResources().getString(R.string.record_active_info), Toast.LENGTH_LONG).show();
}
} else if (viewState instanceof CallRecordingViewModel.RecordingStartingState) {
if (isAllowedToStartOrStopRecording()) {
binding.callRecordingIndicator.setImageResource(R.drawable.record_starting);
binding.callRecordingIndicator.setVisibility(View.VISIBLE);
} else {
binding.callRecordingIndicator.setVisibility(View.GONE);
}
} else if (viewState instanceof CallRecordingViewModel.RecordingConfirmStopState) {
MaterialAlertDialogBuilder dialogBuilder = new MaterialAlertDialogBuilder(this)
.setTitle(R.string.record_stop_confirm_title)
.setMessage(R.string.record_stop_confirm_message)
.setPositiveButton(R.string.record_stop_description,
(dialog, which) -> callRecordingViewModel.stopRecording())
.setNegativeButton(R.string.nc_common_dismiss,
(dialog, which) -> callRecordingViewModel.dismissStopRecording());
viewThemeUtils.dialog.colorMaterialAlertDialogBackground(this, dialogBuilder);
AlertDialog dialog = dialogBuilder.show();
viewThemeUtils.platform.colorTextButtons(
dialog.getButton(AlertDialog.BUTTON_POSITIVE),
dialog.getButton(AlertDialog.BUTTON_NEGATIVE)
);
if (isAllowedToStartOrStopRecording()) {
MaterialAlertDialogBuilder dialogBuilder = new MaterialAlertDialogBuilder(this)
.setTitle(R.string.record_stop_confirm_title)
.setMessage(R.string.record_stop_confirm_message)
.setPositiveButton(R.string.record_stop_description,
(dialog, which) -> callRecordingViewModel.stopRecording())
.setNegativeButton(R.string.nc_common_dismiss,
(dialog, which) -> callRecordingViewModel.dismissStopRecording());
viewThemeUtils.dialog.colorMaterialAlertDialogBackground(this, dialogBuilder);
AlertDialog dialog = dialogBuilder.show();
viewThemeUtils.platform.colorTextButtons(
dialog.getButton(AlertDialog.BUTTON_POSITIVE),
dialog.getButton(AlertDialog.BUTTON_NEGATIVE)
);
} else {
Log.e(TAG, "Being in RecordingConfirmStopState as non moderator. This should not happen!");
}
} else if (viewState instanceof CallRecordingViewModel.RecordingErrorState) {
Toast.makeText(context, context.getResources().getString(R.string.nc_common_error_sorry),
Toast.LENGTH_LONG).show();
if (isAllowedToStartOrStopRecording()) {
Toast.makeText(context, context.getResources().getString(R.string.record_failed_info),
Toast.LENGTH_LONG).show();
}
binding.callRecordingIndicator.setVisibility(View.GONE);
} else {
binding.callRecordingIndicator.setVisibility(View.GONE);
}
@ -603,7 +617,14 @@ public class CallActivity extends CallBaseActivity {
binding.callRecordingIndicator.setOnClickListener(l -> {
if (isAllowedToStartOrStopRecording()) {
callRecordingViewModel.clickRecordButton();
if (callRecordingViewModel.getViewState().getValue() instanceof CallRecordingViewModel.RecordingStartingState) {
if (moreCallActionsDialog == null) {
moreCallActionsDialog = new MoreCallActionsDialog(this);
}
moreCallActionsDialog.show();
} else {
callRecordingViewModel.clickRecordButton();
}
} else {
Toast.makeText(context, context.getResources().getString(R.string.record_active_info), Toast.LENGTH_LONG).show();
}

View File

@ -530,6 +530,7 @@ data class ChatMessage(
RECORDING_STOPPED,
AUDIO_RECORDING_STARTED,
AUDIO_RECORDING_STOPPED,
RECORDING_FAILED,
BREAKOUT_ROOMS_STARTED,
BREAKOUT_ROOMS_STOPPED
}

View File

@ -78,6 +78,7 @@ import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.REACTIO
import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.REACTION_REVOKED
import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.READ_ONLY
import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.READ_ONLY_OFF
import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.RECORDING_FAILED
import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.RECORDING_STARTED
import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.RECORDING_STOPPED
import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.USER_ADDED
@ -143,6 +144,7 @@ class EnumSystemMessageTypeConverter : StringBasedTypeConverter<ChatMessage.Syst
"recording_stopped" -> RECORDING_STOPPED
"audio_recording_started" -> AUDIO_RECORDING_STARTED
"audio_recording_stopped" -> AUDIO_RECORDING_STOPPED
"recording_failed" -> RECORDING_FAILED
"breakout_rooms_started" -> BREAKOUT_ROOMS_STARTED
"breakout_rooms_stopped" -> BREAKOUT_ROOMS_STOPPED
else -> DUMMY
@ -206,6 +208,7 @@ class EnumSystemMessageTypeConverter : StringBasedTypeConverter<ChatMessage.Syst
RECORDING_STOPPED -> "recording_stopped"
AUDIO_RECORDING_STARTED -> "audio_recording_started"
AUDIO_RECORDING_STOPPED -> "audio_recording_stopped"
RECORDING_FAILED -> "recording_failed"
BREAKOUT_ROOMS_STARTED -> "breakout_rooms_started"
BREAKOUT_ROOMS_STOPPED -> "breakout_rooms_stopped"
else -> ""

View File

@ -94,6 +94,20 @@ class MoreCallActionsDialog(private val callActivity: CallActivity) : BottomShee
private fun initObservers() {
callActivity.callRecordingViewModel.viewState.observe(this) { state ->
when (state) {
is CallRecordingViewModel.RecordingStoppedState,
is CallRecordingViewModel.RecordingErrorState -> {
binding.recordCallText.text = context.getText(R.string.record_start_description)
binding.recordCallIcon.setImageDrawable(
ContextCompat.getDrawable(context, R.drawable.record_start)
)
dismiss()
}
is CallRecordingViewModel.RecordingStartingState -> {
binding.recordCallText.text = context.getText(R.string.record_starting)
binding.recordCallIcon.setImageDrawable(
ContextCompat.getDrawable(context, R.drawable.record_stop)
)
}
is CallRecordingViewModel.RecordingStartedState -> {
binding.recordCallText.text = context.getText(R.string.record_stop_description)
binding.recordCallIcon.setImageDrawable(
@ -101,18 +115,8 @@ class MoreCallActionsDialog(private val callActivity: CallActivity) : BottomShee
)
dismiss()
}
is CallRecordingViewModel.RecordingStoppedState -> {
binding.recordCallText.text = context.getText(R.string.record_start_description)
binding.recordCallIcon.setImageDrawable(
ContextCompat.getDrawable(context, R.drawable.record_start)
)
dismiss()
}
is CallRecordingViewModel.RecordingStartLoadingState -> {
binding.recordCallText.text = context.getText(R.string.record_start_loading)
}
is CallRecordingViewModel.RecordingStopLoadingState -> {
binding.recordCallText.text = context.getText(R.string.record_stop_loading)
is CallRecordingViewModel.RecordingStoppingState -> {
binding.recordCallText.text = context.getText(R.string.record_stopping)
}
is CallRecordingViewModel.RecordingConfirmStopState -> {
binding.recordCallText.text = context.getText(R.string.record_stop_description)

View File

@ -42,11 +42,11 @@ class CallRecordingViewModel @Inject constructor(private val repository: CallRec
lateinit var roomToken: String
sealed interface ViewState
open class RecordingStartedState(val showStartedInfo: Boolean) : ViewState
open class RecordingStartedState(val hasVideo: Boolean, val showStartedInfo: Boolean) : ViewState
object RecordingStoppedState : ViewState
object RecordingStartLoadingState : ViewState
object RecordingStopLoadingState : ViewState
open class RecordingStartingState(val hasVideo: Boolean) : ViewState
object RecordingStoppingState : ViewState
object RecordingConfirmStopState : ViewState
object RecordingErrorState : ViewState
@ -69,6 +69,9 @@ class CallRecordingViewModel @Inject constructor(private val repository: CallRec
// just show it again.
_viewState.value = RecordingConfirmStopState
}
is RecordingStartingState -> {
stopRecording()
}
RecordingErrorState -> {
stopRecording()
}
@ -77,7 +80,7 @@ class CallRecordingViewModel @Inject constructor(private val repository: CallRec
}
private fun startRecording() {
_viewState.value = RecordingStartLoadingState
_viewState.value = RecordingStartingState(true)
repository.startRecording(roomToken)
.subscribeOn(Schedulers.io())
?.observeOn(AndroidSchedulers.mainThread())
@ -85,7 +88,7 @@ class CallRecordingViewModel @Inject constructor(private val repository: CallRec
}
fun stopRecording() {
_viewState.value = RecordingStopLoadingState
_viewState.value = RecordingStoppingState
repository.stopRecording(roomToken)
.subscribeOn(Schedulers.io())
?.observeOn(AndroidSchedulers.mainThread())
@ -93,7 +96,7 @@ class CallRecordingViewModel @Inject constructor(private val repository: CallRec
}
fun dismissStopRecording() {
_viewState.value = RecordingStartedState(false)
_viewState.value = RecordingStartedState(true, false)
}
override fun onCleared() {
@ -109,8 +112,11 @@ class CallRecordingViewModel @Inject constructor(private val repository: CallRec
fun setRecordingState(state: Int) {
when (state) {
RECORDING_STOPPED_CODE -> _viewState.value = RecordingStoppedState
RECORDING_STARTED_VIDEO_CODE -> _viewState.value = RecordingStartedState(true)
RECORDING_STARTED_AUDIO_CODE -> _viewState.value = RecordingStartedState(true)
RECORDING_STARTED_VIDEO_CODE -> _viewState.value = RecordingStartedState(true, true)
RECORDING_STARTED_AUDIO_CODE -> _viewState.value = RecordingStartedState(false, true)
RECORDING_STARTING_VIDEO_CODE -> _viewState.value = RecordingStartingState(true)
RECORDING_STARTING_AUDIO_CODE -> _viewState.value = RecordingStartingState(false)
RECORDING_FAILED_CODE -> _viewState.value = RecordingErrorState
else -> {}
}
}
@ -160,5 +166,8 @@ class CallRecordingViewModel @Inject constructor(private val repository: CallRec
const val RECORDING_STOPPED_CODE = 0
const val RECORDING_STARTED_VIDEO_CODE = 1
const val RECORDING_STARTED_AUDIO_CODE = 2
const val RECORDING_STARTING_VIDEO_CODE = 3
const val RECORDING_STARTING_AUDIO_CODE = 4
const val RECORDING_FAILED_CODE = 5
}
}

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@color/grey_600"
android:pathData="M12,2A10,10 0,0 0,2 12A10,10 0,0 0,12 22A10,10 0,0 0,22 12A10,10 0,0 0,12 2M9,9H15V15H9"/>
</vector>

View File

@ -582,12 +582,13 @@ How to translate with transifex:
<!-- Call recording -->
<string name="record_start_description">Start recording</string>
<string name="record_start_loading">Starting …</string>
<string name="record_starting">Cancel recording start</string>
<string name="record_stop_description">Stop recording</string>
<string name="record_stop_loading">Stopping …</string>
<string name="record_stopping">Stopping recording …</string>
<string name="record_stop_confirm_title">Stop Call recording</string>
<string name="record_stop_confirm_message">Do you really want to stop the recording?</string>
<string name="record_active_info">The call is being recorded</string>
<string name="record_failed_info">The recording failed. Please contact your administrator.</string>
<!-- Shared items -->
<string name="shared_items_media">Media</string>

View File

@ -21,7 +21,7 @@ class CallRecordingViewModelTest : AbstractViewModelTest() {
viewModel.setData("foo")
viewModel.clickRecordButton()
Assert.equals(CallRecordingViewModel.RecordingStartLoadingState, viewModel.viewState.value)
Assert.equals(CallRecordingViewModel.RecordingStartingState(true), viewModel.viewState.value)
// fake to execute setRecordingState which would be triggered by signaling message
viewModel.setRecordingState(CallRecordingViewModel.RECORDING_STARTED_VIDEO_CODE)
@ -78,7 +78,7 @@ class CallRecordingViewModelTest : AbstractViewModelTest() {
viewModel.dismissStopRecording()
Assert.equals(
CallRecordingViewModel.RecordingStartedState(false).javaClass,
CallRecordingViewModel.RecordingStartedState(true, false).javaClass,
viewModel.viewState.value?.javaClass
)
Assert.equals(