Merge pull request #4446 from nextcloud/coroutine_2

Migrate Rxjava to coroutines #2
This commit is contained in:
Marcel Hibbe 2024-11-18 22:12:33 +01:00 коммит произвёл GitHub
Родитель 7d097474ab e5802d70f3
Коммит b748080360
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
8 изменённых файлов: 135 добавлений и 149 удалений

Просмотреть файл

@ -195,18 +195,6 @@ public interface NcApi {
@DELETE
Observable<GenericOverall> removeSelfFromRoom(@Header("Authorization") String authorization, @Url String url);
/*
Server URL is: baseUrl + ocsApiVersion + spreedApiVersion + /room/roomToken/public
*/
@POST
Observable<GenericOverall> makeRoomPublic(@Header("Authorization") String authorization, @Url String url);
/*
Server URL is: baseUrl + ocsApiVersion + spreedApiVersion + /room/roomToken/public
*/
@DELETE
Observable<GenericOverall> makeRoomPrivate(@Header("Authorization") String authorization, @Url String url);
@DELETE
Observable<GenericOverall> deleteRoom(@Header("Authorization") String authorization, @Url String url);
@ -341,18 +329,6 @@ public interface NcApi {
Observable<Void> unregisterDeviceForNotificationsWithProxy(@Url String url,
@QueryMap Map<String, String> fields);
@FormUrlEncoded
@PUT
Observable<GenericOverall> setPassword(@Header("Authorization") String authorization,
@Url String url,
@Field("password") String password);
@FormUrlEncoded
@PUT
Observable<Response<GenericOverall>> setPassword2(@Header("Authorization") String authorization,
@Url String url,
@Field("password") String password);
@GET
Observable<CapabilitiesOverall> getCapabilities(@Header("Authorization") String authorization, @Url String url);

Просмотреть файл

@ -89,10 +89,10 @@ interface NcApiCoroutines {
): AddParticipantOverall
@POST
suspend fun makeRoomPublic(@Header("Authorization") authorization: String?, @Url url: String): GenericOverall
suspend fun makeRoomPublic(@Header("Authorization") authorization: String, @Url url: String): GenericOverall
@DELETE
suspend fun makeRoomPrivate(@Header("Authorization") authorization: String?, @Url url: String): GenericOverall
suspend fun makeRoomPrivate(@Header("Authorization") authorization: String, @Url url: String): GenericOverall
@FormUrlEncoded
@PUT
@ -132,4 +132,12 @@ interface NcApiCoroutines {
@Url url: String,
@Body body: RequestBody
): GenericOverall
@FormUrlEncoded
@PUT
suspend fun setPassword2(
@Header("Authorization") authorization: String,
@Url url: String,
@Field("password") password: String
): GenericOverall
}

Просмотреть файл

@ -168,16 +168,15 @@ class ConversationCreationRepositoryImpl(
val result: GenericOverall = if (allow) {
ncApiCoroutines.makeRoomPublic(
credentials,
credentials!!,
url
)
} else {
ncApiCoroutines.makeRoomPrivate(
credentials,
credentials!!,
url
)
}
return result
}
}

Просмотреть файл

@ -916,7 +916,9 @@ class ConversationInfoActivity :
it,
conversation!!,
spreedCapabilities,
conversationUser
conversationUser,
viewModel,
this
).setupGuestAccess()
}
if (ConversationUtils.isNoteToSelfConversation(conversation!!)) {

Просмотреть файл

@ -12,9 +12,11 @@ import android.util.Log
import android.view.LayoutInflater
import android.view.View
import androidx.appcompat.app.AlertDialog
import androidx.lifecycle.LifecycleOwner
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar
import com.nextcloud.talk.R
import com.nextcloud.talk.conversationinfo.viewmodel.ConversationInfoViewModel
import com.nextcloud.talk.data.user.model.User
import com.nextcloud.talk.databinding.ActivityConversationInfoBinding
import com.nextcloud.talk.databinding.DialogPasswordBinding
@ -33,9 +35,10 @@ class GuestAccessHelper(
private val binding: ActivityConversationInfoBinding,
private val conversation: ConversationModel,
private val spreedCapabilities: SpreedCapability,
private val conversationUser: User
private val conversationUser: User,
private val viewModel: ConversationInfoViewModel,
private val lifecycleOwner: LifecycleOwner
) {
private val conversationsRepository = activity.conversationsRepository
private val viewThemeUtils = activity.viewThemeUtils
private val context = activity.context
@ -61,19 +64,35 @@ class GuestAccessHelper(
binding.guestAccessView.guestAccessSettingsAllowGuest.setOnClickListener {
val isChecked = binding.guestAccessView.allowGuestsSwitch.isChecked
binding.guestAccessView.allowGuestsSwitch.isChecked = !isChecked
conversationsRepository.allowGuests(
conversation.token!!,
!isChecked
).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()).subscribe(AllowGuestsResultObserver())
viewModel.allowGuests(conversation.token, !isChecked)
viewModel.allowGuestsViewState.observe(lifecycleOwner) { uiState ->
when (uiState) {
is ConversationInfoViewModel.AllowGuestsUIState.Success -> {
binding.guestAccessView.allowGuestsSwitch.isChecked = uiState.allow
if (uiState.allow) {
showAllOptions()
} else {
hideAllOptions()
}
}
is ConversationInfoViewModel.AllowGuestsUIState.Error -> {
val exception = uiState.exception
val message = context.getString(R.string.nc_guest_access_allow_failed)
Snackbar.make(binding.root, message, Snackbar.LENGTH_LONG).show()
Log.e(TAG, message, exception)
}
ConversationInfoViewModel.AllowGuestsUIState.None -> {
}
}
}
}
binding.guestAccessView.guestAccessSettingsPasswordProtection.setOnClickListener {
val isChecked = binding.guestAccessView.passwordProtectionSwitch.isChecked
binding.guestAccessView.passwordProtectionSwitch.isChecked = !isChecked
if (isChecked) {
conversationsRepository.password("", conversation.token!!).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()).subscribe(PasswordResultObserver(false))
viewModel.setPassword("", conversation.token)
passwordObserver()
} else {
showPasswordDialog()
}
@ -85,6 +104,25 @@ class GuestAccessHelper(
}
}
private fun passwordObserver() {
viewModel.passwordViewState.observe(lifecycleOwner) { uiState ->
when (uiState) {
is ConversationInfoViewModel.PasswordUiState.Success -> {
// unused atm
}
is ConversationInfoViewModel.PasswordUiState.Error -> {
val exception = uiState.exception
val message = context.getString(R.string.nc_guest_access_password_failed)
Snackbar.make(binding.root, message, Snackbar.LENGTH_LONG).show()
Log.e(TAG, message, exception)
}
is ConversationInfoViewModel.PasswordUiState.None -> {
// unused atm
}
}
}
}
private fun showPasswordDialog() {
val builder = MaterialAlertDialogBuilder(activity)
builder.apply {
@ -94,16 +132,14 @@ class GuestAccessHelper(
setTitle(R.string.nc_guest_access_password_dialog_title)
setPositiveButton(R.string.nc_ok) { _, _ ->
val password = dialogPassword.password.text.toString()
conversationsRepository.password(password, conversation.token!!)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(PasswordResultObserver(true))
viewModel.setPassword(password, conversation.token)
}
setNegativeButton(R.string.nc_cancel) { _, _ ->
binding.guestAccessView.passwordProtectionSwitch.isChecked = false
}
}
createDialog(builder)
passwordObserver()
}
private fun createDialog(builder: MaterialAlertDialogBuilder) {
@ -143,32 +179,6 @@ class GuestAccessHelper(
}
}
inner class AllowGuestsResultObserver : Observer<ConversationsRepository.AllowGuestsResult> {
private lateinit var allowGuestsResult: ConversationsRepository.AllowGuestsResult
override fun onNext(t: ConversationsRepository.AllowGuestsResult) {
allowGuestsResult = t
}
override fun onError(e: Throwable) {
val message = context.getString(R.string.nc_guest_access_allow_failed)
Snackbar.make(binding.root, message, Snackbar.LENGTH_LONG).show()
Log.e(TAG, message, e)
}
override fun onComplete() {
binding.guestAccessView.allowGuestsSwitch.isChecked = allowGuestsResult.allow
if (allowGuestsResult.allow) {
showAllOptions()
} else {
hideAllOptions()
}
}
override fun onSubscribe(d: Disposable) = Unit
}
private fun showAllOptions() {
binding.guestAccessView.guestAccessSettingsPasswordProtection.visibility = View.VISIBLE
if (conversationUser.capabilities?.spreedCapability?.features?.contains("sip-support") == true) {
@ -181,37 +191,6 @@ class GuestAccessHelper(
binding.guestAccessView.resendInvitationsButton.visibility = View.GONE
}
inner class PasswordResultObserver(private val setPassword: Boolean) :
Observer<ConversationsRepository.PasswordResult> {
private lateinit var passwordResult: ConversationsRepository.PasswordResult
override fun onSubscribe(d: Disposable) = Unit
override fun onNext(t: ConversationsRepository.PasswordResult) {
passwordResult = t
}
override fun onError(e: Throwable) {
val message = context.getString(R.string.nc_guest_access_password_failed)
Snackbar.make(binding.root, message, Snackbar.LENGTH_LONG).show()
Log.e(TAG, message, e)
}
override fun onComplete() {
binding.guestAccessView.passwordProtectionSwitch.isChecked = passwordResult.passwordSet && setPassword
if (passwordResult.passwordIsWeak) {
val builder = MaterialAlertDialogBuilder(activity)
builder.apply {
setTitle(R.string.nc_guest_access_password_weak_alert_title)
setMessage(passwordResult.message)
setPositiveButton("OK") { _, _ -> }
}
createDialog(builder)
}
}
}
companion object {
private val TAG = GuestAccessHelper::class.simpleName
}

Просмотреть файл

@ -6,12 +6,14 @@
*/
package com.nextcloud.talk.conversationinfo.viewmodel
import android.annotation.SuppressLint
import android.util.Log
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.nextcloud.talk.chat.data.network.ChatNetworkDataSource
import com.nextcloud.talk.data.user.model.User
import com.nextcloud.talk.models.domain.ConversationModel
@ -24,6 +26,7 @@ import io.reactivex.Observer
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import kotlinx.coroutines.launch
import javax.inject.Inject
class ConversationInfoViewModel @Inject constructor(
@ -95,6 +98,14 @@ class ConversationInfoViewModel @Inject constructor(
object GetCapabilitiesErrorState : ViewState
open class GetCapabilitiesSuccessState(val spreedCapabilities: SpreedCapability) : ViewState
private val _allowGuestsViewState = MutableLiveData<AllowGuestsUIState>(AllowGuestsUIState.None)
val allowGuestsViewState: LiveData<AllowGuestsUIState>
get() = _allowGuestsViewState
private val _passwordViewState = MutableLiveData<PasswordUiState>(PasswordUiState.None)
val passwordViewState: LiveData<PasswordUiState>
get() = _passwordViewState
private val _getCapabilitiesViewState: MutableLiveData<ViewState> = MutableLiveData(GetCapabilitiesStartState)
val getCapabilitiesViewState: LiveData<ViewState>
get() = _getCapabilitiesViewState
@ -233,6 +244,29 @@ class ConversationInfoViewModel @Inject constructor(
})
}
fun allowGuests(token: String, allow: Boolean) {
viewModelScope.launch {
try {
conversationsRepository.allowGuests(token, allow)
_allowGuestsViewState.value = AllowGuestsUIState.Success(allow)
} catch (exception: Exception) {
_allowGuestsViewState.value = AllowGuestsUIState.Error(exception)
}
}
}
@SuppressLint("SuspiciousIndentation")
fun setPassword(password: String, token: String) {
viewModelScope.launch {
try {
conversationsRepository.setPassword(password, token)
_passwordViewState.value = PasswordUiState.Success
} catch (exception: Exception) {
_passwordViewState.value = PasswordUiState.Error(exception)
}
}
}
suspend fun archiveConversation(user: User, token: String) {
val apiVersion = ApiUtils.getConversationApiVersion(user, intArrayOf(ApiUtils.API_V4, ApiUtils.API_V1))
val url = ApiUtils.getUrlForArchive(apiVersion, user.baseUrl, token)
@ -267,4 +301,16 @@ class ConversationInfoViewModel @Inject constructor(
companion object {
private val TAG = ConversationInfoViewModel::class.simpleName
}
sealed class AllowGuestsUIState {
data object None : AllowGuestsUIState()
data class Success(val allow: Boolean) : AllowGuestsUIState()
data class Error(val exception: Exception) : AllowGuestsUIState()
}
sealed class PasswordUiState {
data object None : PasswordUiState()
data object Success : PasswordUiState()
data class Error(val exception: Exception) : PasswordUiState()
}
}

Просмотреть файл

@ -12,19 +12,7 @@ import io.reactivex.Observable
interface ConversationsRepository {
data class AllowGuestsResult(
val allow: Boolean
)
fun allowGuests(token: String, allow: Boolean): Observable<AllowGuestsResult>
data class PasswordResult(
val passwordSet: Boolean,
val passwordIsWeak: Boolean,
val message: String
)
fun password(password: String, token: String): Observable<PasswordResult>
suspend fun allowGuests(token: String, allow: Boolean): GenericOverall
data class ResendInvitationsResult(
val successful: Boolean
@ -35,5 +23,7 @@ interface ConversationsRepository {
suspend fun unarchiveConversation(credentials: String, url: String): GenericOverall
suspend fun setPassword(password: String, token: String): GenericOverall
fun setConversationReadOnly(credentials: String, url: String, state: Int): Observable<GenericOverall>
}

Просмотреть файл

@ -7,14 +7,10 @@
*/
package com.nextcloud.talk.repositories.conversations
import com.bluelinelabs.logansquare.LoganSquare
import com.nextcloud.talk.api.NcApi
import com.nextcloud.talk.api.NcApiCoroutines
import com.nextcloud.talk.data.user.model.User
import com.nextcloud.talk.models.json.conversations.password.PasswordOverall
import com.nextcloud.talk.models.json.generic.GenericOverall
import com.nextcloud.talk.repositories.conversations.ConversationsRepository.AllowGuestsResult
import com.nextcloud.talk.repositories.conversations.ConversationsRepository.PasswordResult
import com.nextcloud.talk.repositories.conversations.ConversationsRepository.ResendInvitationsResult
import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew
@ -24,8 +20,7 @@ class ConversationsRepositoryImpl(
private val api: NcApi,
private val coroutineApi: NcApiCoroutines,
private val userProvider: CurrentUserProviderNew
) :
ConversationsRepository {
) : ConversationsRepository {
private val user: User
get() = userProvider.currentUser.blockingGet()
@ -33,48 +28,27 @@ class ConversationsRepositoryImpl(
private val credentials: String
get() = ApiUtils.getCredentials(user.username, user.token)!!
override fun allowGuests(token: String, allow: Boolean): Observable<AllowGuestsResult> {
val apiVersion = ApiUtils.getConversationApiVersion(user, intArrayOf(ApiUtils.API_V4, ApiUtils.API_V1))
override suspend fun allowGuests(token: String, allow: Boolean): GenericOverall {
val url = ApiUtils.getUrlForRoomPublic(
apiVersion(),
apiVersion,
user.baseUrl!!,
token
)
val apiObservable = if (allow) {
api.makeRoomPublic(
val result: GenericOverall = if (allow) {
coroutineApi.makeRoomPublic(
credentials,
url
)
} else {
api.makeRoomPrivate(
coroutineApi.makeRoomPrivate(
credentials,
url
)
}
return apiObservable.map { AllowGuestsResult(it.ocs!!.meta!!.statusCode == STATUS_CODE_OK && allow) }
}
override fun password(password: String, token: String): Observable<PasswordResult> {
val apiObservable = api.setPassword2(
credentials,
ApiUtils.getUrlForRoomPassword(
apiVersion(),
user.baseUrl!!,
token
),
password
)
return apiObservable.map {
val passwordPolicyMessage = if (it.code() == STATUS_CODE_BAD_REQUEST) {
LoganSquare.parse(it.errorBody()!!.string(), PasswordOverall::class.java).ocs!!.data!!
.message!!
} else {
""
}
PasswordResult(it.isSuccessful, passwordPolicyMessage.isNotEmpty(), passwordPolicyMessage)
}
return result
}
override fun resendInvitations(token: String): Observable<ResendInvitationsResult> {
@ -104,12 +78,24 @@ class ConversationsRepositoryImpl(
return api.setConversationReadOnly(credentials, url, state)
}
override suspend fun setPassword(password: String, token: String): GenericOverall {
val result = coroutineApi.setPassword(
credentials,
ApiUtils.getUrlForRoomPassword(
apiVersion,
user.baseUrl!!,
token
),
password
)
return result
}
private fun apiVersion(): Int {
return ApiUtils.getConversationApiVersion(user, intArrayOf(ApiUtils.API_V4))
}
companion object {
const val STATUS_CODE_OK = 200
const val STATUS_CODE_BAD_REQUEST = 400
}
}