Эх сурвалжийг харах

Request assistance for breakout room

Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
Marcel Hibbe 2 жил өмнө
parent
commit
96dce63e20

+ 6 - 5
app/src/main/java/com/nextcloud/talk/activities/CallActivity.java

@@ -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) {

+ 8 - 0
app/src/main/java/com/nextcloud/talk/api/NcApi.java

@@ -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);
 }

+ 8 - 0
app/src/main/java/com/nextcloud/talk/dagger/modules/RepositoryModule.kt

@@ -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)
+    }
 }

+ 6 - 0
app/src/main/java/com/nextcloud/talk/dagger/modules/ViewModelModule.kt

@@ -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
 }

+ 5 - 0
app/src/main/java/com/nextcloud/talk/raisehand/RequestAssistanceModel.kt

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

+ 34 - 0
app/src/main/java/com/nextcloud/talk/raisehand/RequestAssistanceRepository.kt

@@ -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>
+}

+ 81 - 0
app/src/main/java/com/nextcloud/talk/raisehand/RequestAssistanceRepositoryImpl.kt

@@ -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
+    }
+}

+ 5 - 0
app/src/main/java/com/nextcloud/talk/raisehand/WithdrawRequestAssistanceModel.kt

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

+ 115 - 0
app/src/main/java/com/nextcloud/talk/raisehand/viewmodel/RaiseHandViewModel.kt

@@ -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
+    }
+}

+ 19 - 0
app/src/main/java/com/nextcloud/talk/ui/dialog/MoreCallActionsDialog.kt

@@ -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 {

+ 4 - 0
app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java

@@ -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";
+    }
 }

+ 21 - 3
app/src/main/res/drawable/ic_baseline_do_not_touch_24.xml

@@ -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>

+ 3 - 0
app/src/main/res/values/strings.xml

@@ -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>