Request assistance for breakout room

Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
This commit is contained in:
Marcel Hibbe 2023-02-15 12:59:31 +01:00
parent 2835bb6c02
commit 96dce63e20
No known key found for this signature in database
GPG key ID: C793F8B59F43CE7B
13 changed files with 315 additions and 8 deletions

View file

@ -86,6 +86,7 @@ import com.nextcloud.talk.models.json.signaling.Signaling;
import com.nextcloud.talk.models.json.signaling.SignalingOverall;
import com.nextcloud.talk.models.json.signaling.settings.IceServer;
import com.nextcloud.talk.models.json.signaling.settings.SignalingSettingsOverall;
import com.nextcloud.talk.raisehand.viewmodel.RaiseHandViewModel;
import com.nextcloud.talk.signaling.SignalingMessageReceiver;
import com.nextcloud.talk.signaling.SignalingMessageSender;
import com.nextcloud.talk.ui.dialog.AudioOutputDialog;
@ -211,7 +212,7 @@ public class CallActivity extends CallBaseActivity {
public WebRtcAudioManager audioManager;
public CallRecordingViewModel callRecordingViewModel;
// public RaiseHandViewModel raiseHandViewModel;
public RaiseHandViewModel raiseHandViewModel;
private static final String[] PERMISSIONS_CALL = {
Manifest.permission.CAMERA,
@ -411,6 +412,9 @@ public class CallActivity extends CallBaseActivity {
setCallState(CallStatus.CONNECTING);
}
raiseHandViewModel = new ViewModelProvider(this, viewModelFactory).get((RaiseHandViewModel.class));
raiseHandViewModel.setData(roomToken, isBreakoutRoom);
callRecordingViewModel = new ViewModelProvider(this, viewModelFactory).get((CallRecordingViewModel.class));
callRecordingViewModel.setData(roomToken);
callRecordingViewModel.setRecordingState(extras.getInt(KEY_RECORDING_STATE));
@ -1232,10 +1236,7 @@ public class CallActivity extends CallBaseActivity {
public void clickHand(Boolean raise) {
if (isBreakoutRoom) {
Log.d(TAG, "send request to request help for breakout rooms.");
}
//
raiseHandViewModel.clickHandButton();
// TODO: fix how to build&send the message
// if (isConnectionEstablished() && peerConnectionWrapperList != null) {

View file

@ -587,4 +587,12 @@ public interface NcApi {
@DELETE
Observable<GenericOverall> stopRecording(@Header("Authorization") String authorization,
@Url String url);
@POST
Observable<GenericOverall> requestAssistance(@Header("Authorization") String authorization,
@Url String url);
@DELETE
Observable<GenericOverall> withdrawRequestAssistance(@Header("Authorization") String authorization,
@Url String url);
}

View file

@ -33,6 +33,8 @@ import com.nextcloud.talk.data.user.UsersRepository
import com.nextcloud.talk.data.user.UsersRepositoryImpl
import com.nextcloud.talk.polls.repositories.PollRepository
import com.nextcloud.talk.polls.repositories.PollRepositoryImpl
import com.nextcloud.talk.raisehand.RequestAssistanceRepository
import com.nextcloud.talk.raisehand.RequestAssistanceRepositoryImpl
import com.nextcloud.talk.remotefilebrowser.repositories.RemoteFileBrowserItemsRepository
import com.nextcloud.talk.remotefilebrowser.repositories.RemoteFileBrowserItemsRepositoryImpl
import com.nextcloud.talk.repositories.callrecording.CallRecordingRepository
@ -99,4 +101,10 @@ class RepositoryModule {
fun provideCallRecordingRepository(ncApi: NcApi, userProvider: CurrentUserProviderNew): CallRecordingRepository {
return CallRecordingRepositoryImpl(ncApi, userProvider)
}
@Provides
fun provideRequestAssistanceRepository(ncApi: NcApi, userProvider: CurrentUserProviderNew):
RequestAssistanceRepository {
return RequestAssistanceRepositoryImpl(ncApi, userProvider)
}
}

View file

@ -28,6 +28,7 @@ import com.nextcloud.talk.polls.viewmodels.PollCreateViewModel
import com.nextcloud.talk.polls.viewmodels.PollMainViewModel
import com.nextcloud.talk.polls.viewmodels.PollResultsViewModel
import com.nextcloud.talk.polls.viewmodels.PollVoteViewModel
import com.nextcloud.talk.raisehand.viewmodel.RaiseHandViewModel
import com.nextcloud.talk.remotefilebrowser.viewmodels.RemoteFileBrowserItemsViewModel
import com.nextcloud.talk.shareditems.viewmodels.SharedItemsViewModel
import com.nextcloud.talk.viewmodels.CallRecordingViewModel
@ -95,4 +96,9 @@ abstract class ViewModelModule {
@IntoMap
@ViewModelKey(CallRecordingViewModel::class)
abstract fun callRecordingViewModel(viewModel: CallRecordingViewModel): ViewModel
@Binds
@IntoMap
@ViewModelKey(RaiseHandViewModel::class)
abstract fun raiseHandViewModel(viewModel: RaiseHandViewModel): ViewModel
}

View file

@ -0,0 +1,5 @@
package com.nextcloud.talk.raisehand
data class RequestAssistanceModel(
var success: Boolean
)

View file

@ -0,0 +1,34 @@
/*
* Nextcloud Talk application
*
* @author Marcel Hibbe
* Copyright (C) 2022 Marcel Hibbe <dev@mhibbe.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.raisehand
import io.reactivex.Observable
interface RequestAssistanceRepository {
fun requestAssistance(
roomToken: String
): Observable<RequestAssistanceModel>
fun withdrawRequestAssistance(
roomToken: String
): Observable<WithdrawRequestAssistanceModel>
}

View file

@ -0,0 +1,81 @@
/*
* Nextcloud Talk application
*
* @author Marcel Hibbe
* Copyright (C) 2022 Marcel Hibbe <dev@mhibbe.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.raisehand
import com.nextcloud.talk.api.NcApi
import com.nextcloud.talk.data.user.model.User
import com.nextcloud.talk.models.json.generic.GenericMeta
import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew
import io.reactivex.Observable
class RequestAssistanceRepositoryImpl(private val ncApi: NcApi, currentUserProvider: CurrentUserProviderNew) :
RequestAssistanceRepository {
val currentUser: User = currentUserProvider.currentUser.blockingGet()
val credentials: String = ApiUtils.getCredentials(currentUser.username, currentUser.token)
var apiVersion = 1
override fun requestAssistance(roomToken: String): Observable<RequestAssistanceModel> {
return ncApi.requestAssistance(
credentials,
ApiUtils.getUrlForRequestAssistance(
apiVersion,
currentUser.baseUrl,
roomToken
)
).map { mapToRequestAssistanceModel(it.ocs?.meta!!) }
}
override fun withdrawRequestAssistance(roomToken: String): Observable<WithdrawRequestAssistanceModel> {
return ncApi.withdrawRequestAssistance(
credentials,
ApiUtils.getUrlForRequestAssistance(
apiVersion,
currentUser.baseUrl,
roomToken
)
).map { mapToWithdrawRequestAssistanceModel(it.ocs?.meta!!) }
}
private fun mapToRequestAssistanceModel(
response: GenericMeta
): RequestAssistanceModel {
val success = response.statusCode == HTTP_OK
return RequestAssistanceModel(
success
)
}
private fun mapToWithdrawRequestAssistanceModel(
response: GenericMeta
): WithdrawRequestAssistanceModel {
val success = response.statusCode == HTTP_OK
return WithdrawRequestAssistanceModel(
success
)
}
companion object {
private const val HTTP_OK: Int = 200
}
}

View file

@ -0,0 +1,5 @@
package com.nextcloud.talk.raisehand
data class WithdrawRequestAssistanceModel(
var success: Boolean
)

View file

@ -0,0 +1,115 @@
package com.nextcloud.talk.raisehand.viewmodel
import android.util.Log
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.nextcloud.talk.raisehand.RequestAssistanceModel
import com.nextcloud.talk.raisehand.RequestAssistanceRepository
import com.nextcloud.talk.raisehand.WithdrawRequestAssistanceModel
import com.nextcloud.talk.users.UserManager
import io.reactivex.Observer
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import javax.inject.Inject
class RaiseHandViewModel @Inject constructor(private val repository: RequestAssistanceRepository) : ViewModel() {
@Inject
lateinit var userManager: UserManager
lateinit var roomToken: String
private var isBreakoutRoom: Boolean = false
sealed interface ViewState
object RaisedHandState : ViewState
object LoweredHandState : ViewState
object ErrorState : ViewState
private val _viewState: MutableLiveData<ViewState> = MutableLiveData(LoweredHandState)
val viewState: LiveData<ViewState>
get() = _viewState
fun clickHandButton() {
when (viewState.value) {
LoweredHandState -> {
raiseHand()
}
RaisedHandState -> {
lowerHand()
}
else -> {}
}
}
private fun raiseHand() {
_viewState.value = RaisedHandState
if (isBreakoutRoom) {
repository.requestAssistance(roomToken)
.subscribeOn(Schedulers.io())
?.observeOn(AndroidSchedulers.mainThread())
?.subscribe(RequestAssistanceObserver())
}
}
private fun lowerHand() {
_viewState.value = LoweredHandState
if (isBreakoutRoom) {
repository.withdrawRequestAssistance(roomToken)
.subscribeOn(Schedulers.io())
?.observeOn(AndroidSchedulers.mainThread())
?.subscribe(WithdrawRequestAssistanceObserver())
}
}
fun setData(roomToken: String, isBreakoutRoom: Boolean) {
this.roomToken = roomToken
this.isBreakoutRoom = isBreakoutRoom
}
inner class RequestAssistanceObserver : Observer<RequestAssistanceModel> {
override fun onSubscribe(d: Disposable) {
// unused atm
}
override fun onNext(requestAssistanceModel: RequestAssistanceModel) {
// RaisedHandState was already set because it's also used for signaling message
Log.d(TAG, "requestAssistance successful")
}
override fun onError(e: Throwable) {
Log.e(TAG, "failure in RequestAssistanceObserver", e)
_viewState.value = ErrorState
}
override fun onComplete() {
// dismiss()
}
}
inner class WithdrawRequestAssistanceObserver : Observer<WithdrawRequestAssistanceModel> {
override fun onSubscribe(d: Disposable) {
// unused atm
}
override fun onNext(withdrawRequestAssistanceModel: WithdrawRequestAssistanceModel) {
// LoweredHandState was already set because it's also used for signaling message
Log.d(TAG, "withdrawRequestAssistance successful")
}
override fun onError(e: Throwable) {
Log.e(TAG, "failure in WithdrawRequestAssistanceObserver", e)
_viewState.value = ErrorState
}
override fun onComplete() {
// dismiss()
}
}
companion object {
private val TAG = RaiseHandViewModel::class.java.simpleName
}
}

View file

@ -32,6 +32,7 @@ import com.nextcloud.talk.R
import com.nextcloud.talk.activities.CallActivity
import com.nextcloud.talk.application.NextcloudTalkApplication
import com.nextcloud.talk.databinding.DialogMoreCallActionsBinding
import com.nextcloud.talk.raisehand.viewmodel.RaiseHandViewModel
import com.nextcloud.talk.ui.theme.ViewThemeUtils
import com.nextcloud.talk.viewmodels.CallRecordingViewModel
import javax.inject.Inject
@ -122,6 +123,24 @@ class MoreCallActionsDialog(private val callActivity: CallActivity) : BottomShee
}
}
}
callActivity.raiseHandViewModel.viewState.observe(this) { state ->
when (state) {
is RaiseHandViewModel.RaisedHandState -> {
binding.raiseHandText.text = context.getText(R.string.lower_hand)
binding.raiseHandIcon.setImageDrawable(
ContextCompat.getDrawable(context, R.drawable.ic_baseline_do_not_touch_24)
)
}
is RaiseHandViewModel.LoweredHandState -> {
binding.raiseHandText.text = context.getText(R.string.raise_hand)
binding.raiseHandIcon.setImageDrawable(
ContextCompat.getDrawable(context, R.drawable.ic_hand_back_left)
)
}
else -> {}
}
}
}
companion object {

View file

@ -498,4 +498,8 @@ public class ApiUtils {
public static String getUrlForRecording(int version, String baseUrl, String token) {
return getUrlForApi(version, baseUrl) + "/recording/" + token;
}
public static String getUrlForRequestAssistance(int version, String baseUrl, String token) {
return getUrlForApi(version, baseUrl) + "/breakout-rooms/" + token + "/request-assistance";
}
}

View file

@ -1,5 +1,23 @@
<vector android:height="24dp" android:tint="#000000"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<!--
@author Google LLC
Copyright (C) 2023 Google LLC
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.
-->
<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="@android:color/white" android:pathData="M13,10.17l-2.5,-2.5V2.25C10.5,1.56 11.06,1 11.75,1S13,1.56 13,2.25V10.17zM20,12.75V11V5.25C20,4.56 19.44,4 18.75,4S17.5,4.56 17.5,5.25V11h-1V3.25C16.5,2.56 15.94,2 15.25,2S14,2.56 14,3.25v7.92l6,6V12.75zM9.5,4.25C9.5,3.56 8.94,3 8.25,3c-0.67,0 -1.2,0.53 -1.24,1.18L9.5,6.67V4.25zM13,10.17l-2.5,-2.5V2.25C10.5,1.56 11.06,1 11.75,1S13,1.56 13,2.25V10.17zM20,12.75V11V5.25C20,4.56 19.44,4 18.75,4S17.5,4.56 17.5,5.25V11h-1V3.25C16.5,2.56 15.94,2 15.25,2S14,2.56 14,3.25v7.92l6,6V12.75zM9.5,4.25C9.5,3.56 8.94,3 8.25,3c-0.67,0 -1.2,0.53 -1.24,1.18L9.5,6.67V4.25zM21.19,21.19L2.81,2.81L1.39,4.22l5.63,5.63L7,9.83v4.3c-1.11,-0.64 -2.58,-1.47 -2.6,-1.48c-0.17,-0.09 -0.34,-0.14 -0.54,-0.14c-0.26,0 -0.5,0.09 -0.7,0.26C3.12,12.78 2,13.88 2,13.88l6.8,7.18c0.57,0.6 1.35,0.94 2.18,0.94H17c0.62,0 1.18,-0.19 1.65,-0.52l-0.02,-0.02l1.15,1.15L21.19,21.19z"/>
</vector>

View file

@ -573,6 +573,9 @@
<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="raise_hand">Raise hand</string>
<string name="lower_hand">Lower hand</string>
<!-- Shared items -->
<string name="shared_items_media">Media</string>
<string name="shared_items_file">File</string>