mirror of
https://github.com/nextcloud/talk-android.git
synced 2024-11-28 09:38:14 +03:00
Avoid to send conversation and user via intent
sending too much data via intent always is a bad pattern which can lead to TransactionTooLargeException. When OpenAI translation is enabled, the capabilities contain a ton of translation combinations. These capabilities are contained in 'currentUser' as well in 'selectedConversation'. So, TransactionTooLargeException was thrown. this PR: - avoids passing too much data as parcelables in intents (esp. conversation and user) - introduces MVVM patterns to load required data (esp conversation) from backend (for now via requests, in the future from database first) - introduces ConversationModel which is created out of the Conversation json model - loads user data via injection when possible - creates some quickfixes in ConversationBottomDialog, EntryMenuController and OperationsMenuController. Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
This commit is contained in:
parent
e4c06d8ee8
commit
817ea1ab64
42 changed files with 1455 additions and 870 deletions
|
@ -140,7 +140,7 @@
|
|||
android:theme="@style/AppTheme.CallLauncher" />
|
||||
|
||||
<activity
|
||||
android:name=".activities.CallNotificationActivity"
|
||||
android:name=".callnotification.CallNotificationActivity"
|
||||
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
|
||||
android:excludeFromRecents="true"
|
||||
android:launchMode="singleTask"
|
||||
|
|
|
@ -100,6 +100,7 @@ import com.nextcloud.talk.utils.NotificationUtils;
|
|||
import com.nextcloud.talk.utils.VibrationUtils;
|
||||
import com.nextcloud.talk.utils.animations.PulseAnimation;
|
||||
import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew;
|
||||
import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew;
|
||||
import com.nextcloud.talk.utils.permissions.PlatformPermissionUtil;
|
||||
import com.nextcloud.talk.utils.power.PowerManagerUtils;
|
||||
import com.nextcloud.talk.utils.singletons.ApplicationWideCurrentRoomHolder;
|
||||
|
@ -186,7 +187,6 @@ import static com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_ID;
|
|||
import static com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN;
|
||||
import static com.nextcloud.talk.utils.bundle.BundleKeys.KEY_START_CALL_AFTER_ROOM_SWITCH;
|
||||
import static com.nextcloud.talk.utils.bundle.BundleKeys.KEY_SWITCH_TO_ROOM;
|
||||
import static com.nextcloud.talk.utils.bundle.BundleKeys.KEY_USER_ENTITY;
|
||||
|
||||
@AutoInjector(NextcloudTalkApplication.class)
|
||||
public class CallActivity extends CallBaseActivity {
|
||||
|
@ -199,6 +199,9 @@ public class CallActivity extends CallBaseActivity {
|
|||
@Inject
|
||||
NcApi ncApi;
|
||||
|
||||
@Inject
|
||||
CurrentUserProviderNew currentUserProvider;
|
||||
|
||||
@Inject
|
||||
UserManager userManager;
|
||||
|
||||
|
@ -386,10 +389,11 @@ public class CallActivity extends CallBaseActivity {
|
|||
|
||||
hideNavigationIfNoPipAvailable();
|
||||
|
||||
conversationUser = currentUserProvider.getCurrentUser().blockingGet();
|
||||
|
||||
Bundle extras = getIntent().getExtras();
|
||||
roomId = extras.getString(KEY_ROOM_ID, "");
|
||||
roomToken = extras.getString(KEY_ROOM_TOKEN, "");
|
||||
conversationUser = extras.getParcelable(KEY_USER_ENTITY);
|
||||
conversationPassword = extras.getString(KEY_CONVERSATION_PASSWORD, "");
|
||||
conversationName = extras.getString(KEY_CONVERSATION_NAME, "");
|
||||
isVoiceOnlyCall = extras.getBoolean(KEY_CALL_VOICE_ONLY, false);
|
||||
|
@ -1970,7 +1974,6 @@ public class CallActivity extends CallBaseActivity {
|
|||
bundle.putBoolean(KEY_SWITCH_TO_ROOM, true);
|
||||
bundle.putBoolean(KEY_START_CALL_AFTER_ROOM_SWITCH, true);
|
||||
bundle.putString(KEY_ROOM_TOKEN, switchToRoomToken);
|
||||
bundle.putParcelable(KEY_USER_ENTITY, conversationUser);
|
||||
bundle.putBoolean(KEY_CALL_VOICE_ONLY, isVoiceOnlyCall);
|
||||
intent.putExtras(bundle);
|
||||
startActivity(intent);
|
||||
|
@ -3151,7 +3154,7 @@ public class CallActivity extends CallBaseActivity {
|
|||
}
|
||||
|
||||
@Override
|
||||
void suppressFitsSystemWindows() {
|
||||
public void suppressFitsSystemWindows() {
|
||||
binding.controllerCallLayout.setFitsSystemWindows(false);
|
||||
}
|
||||
|
||||
|
|
|
@ -74,7 +74,7 @@ public abstract class CallBaseActivity extends BaseActivity {
|
|||
getOnBackPressedDispatcher().addCallback(this, onBackPressedCallback);
|
||||
}
|
||||
|
||||
void hideNavigationIfNoPipAvailable(){
|
||||
public void hideNavigationIfNoPipAvailable(){
|
||||
if (!isPipModePossible()) {
|
||||
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
|
||||
|
@ -160,9 +160,9 @@ public abstract class CallBaseActivity extends BaseActivity {
|
|||
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
|
||||
}
|
||||
|
||||
abstract void updateUiForPipMode();
|
||||
public abstract void updateUiForPipMode();
|
||||
|
||||
abstract void updateUiForNormalMode();
|
||||
public abstract void updateUiForNormalMode();
|
||||
|
||||
abstract void suppressFitsSystemWindows();
|
||||
public abstract void suppressFitsSystemWindows();
|
||||
}
|
||||
|
|
|
@ -47,6 +47,7 @@ import com.nextcloud.talk.BuildConfig
|
|||
import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.api.NcApi
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||
import com.nextcloud.talk.callnotification.CallNotificationActivity
|
||||
import com.nextcloud.talk.chat.ChatActivity
|
||||
import com.nextcloud.talk.controllers.ServerSelectionController
|
||||
import com.nextcloud.talk.controllers.WebViewLoginController
|
||||
|
@ -61,16 +62,13 @@ import com.nextcloud.talk.utils.ApiUtils
|
|||
import com.nextcloud.talk.utils.SecurityUtils
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.ADD_ACCOUNT
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ACTIVE_CONVERSATION
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_ID
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_USER_ENTITY
|
||||
import io.reactivex.Observer
|
||||
import io.reactivex.SingleObserver
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import org.parceler.Parcels
|
||||
import javax.inject.Inject
|
||||
|
||||
@AutoInjector(NextcloudTalkApplication::class)
|
||||
|
@ -282,32 +280,9 @@ class MainActivity : BaseActivity(), ActionBarProvider {
|
|||
|
||||
override fun onNext(roomOverall: RoomOverall) {
|
||||
val bundle = Bundle()
|
||||
bundle.putParcelable(KEY_USER_ENTITY, currentUser)
|
||||
bundle.putString(KEY_ROOM_TOKEN, roomOverall.ocs!!.data!!.token)
|
||||
bundle.putString(KEY_ROOM_ID, roomOverall.ocs!!.data!!.roomId)
|
||||
|
||||
// FIXME once APIv2 or later is used only, the createRoom already returns all the data
|
||||
ncApi.getRoom(
|
||||
credentials,
|
||||
ApiUtils.getUrlForRoom(
|
||||
apiVersion,
|
||||
currentUser?.baseUrl,
|
||||
roomOverall.ocs!!.data!!.token
|
||||
)
|
||||
)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : Observer<RoomOverall> {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
// unused atm
|
||||
}
|
||||
|
||||
override fun onNext(roomOverall: RoomOverall) {
|
||||
bundle.putParcelable(
|
||||
KEY_ACTIVE_CONVERSATION,
|
||||
Parcels.wrap(roomOverall.ocs!!.data)
|
||||
)
|
||||
|
||||
val chatIntent = Intent(context, ChatActivity::class.java)
|
||||
chatIntent.putExtras(bundle)
|
||||
startActivity(chatIntent)
|
||||
|
@ -323,21 +298,13 @@ class MainActivity : BaseActivity(), ActionBarProvider {
|
|||
})
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
// unused atm
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
// unused atm
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onNewIntent(intent: Intent) {
|
||||
super.onNewIntent(intent)
|
||||
Log.d(TAG, "onNewIntent Activity: " + System.identityHashCode(this).toString())
|
||||
|
||||
val user = intent.getParcelableExtra<User>(KEY_USER_ENTITY)
|
||||
val internalUserId = intent.extras?.getLong(BundleKeys.KEY_INTERNAL_USER_ID)
|
||||
val user = userManager.getUserWithId(internalUserId!!).blockingGet()
|
||||
|
||||
if (user != null && userManager.setUserAsActive(user).blockingGet()) {
|
||||
handleIntent(intent)
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.nextcloud.talk.activities
|
||||
package com.nextcloud.talk.callnotification
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Intent
|
||||
|
@ -30,36 +30,36 @@ import android.os.Handler
|
|||
import android.os.Looper
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import autodagger.AutoInjector
|
||||
import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.activities.CallActivity
|
||||
import com.nextcloud.talk.activities.CallBaseActivity
|
||||
import com.nextcloud.talk.api.NcApi
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
|
||||
import com.nextcloud.talk.callnotification.viewmodel.CallNotificationViewModel
|
||||
import com.nextcloud.talk.data.user.model.User
|
||||
import com.nextcloud.talk.databinding.CallNotificationActivityBinding
|
||||
import com.nextcloud.talk.extensions.loadUserAvatar
|
||||
import com.nextcloud.talk.models.json.conversations.Conversation
|
||||
import com.nextcloud.talk.models.json.conversations.RoomOverall
|
||||
import com.nextcloud.talk.models.domain.ConversationModel
|
||||
import com.nextcloud.talk.models.domain.ConversationType
|
||||
import com.nextcloud.talk.models.json.participants.Participant
|
||||
import com.nextcloud.talk.users.UserManager
|
||||
import com.nextcloud.talk.utils.ApiUtils
|
||||
import com.nextcloud.talk.utils.ConversationUtils
|
||||
import com.nextcloud.talk.utils.NotificationUtils
|
||||
import com.nextcloud.talk.utils.ParticipantPermissions
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CALL_VOICE_ONLY
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CONVERSATION_NAME
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_USER_ENTITY
|
||||
import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew.hasSpreedFeatureCapability
|
||||
import io.reactivex.Observer
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import okhttp3.Cache
|
||||
import org.parceler.Parcels
|
||||
import java.io.IOException
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -77,13 +77,18 @@ class CallNotificationActivity : CallBaseActivity() {
|
|||
@Inject
|
||||
lateinit var userManager: UserManager
|
||||
|
||||
@Inject
|
||||
lateinit var viewModelFactory: ViewModelProvider.Factory
|
||||
|
||||
lateinit var callNotificationViewModel: CallNotificationViewModel
|
||||
|
||||
private val disposablesList: MutableList<Disposable> = ArrayList()
|
||||
private var originalBundle: Bundle? = null
|
||||
private var roomToken: String? = null
|
||||
private var notificationTimestamp: Int? = null
|
||||
private var userBeingCalled: User? = null
|
||||
private var credentials: String? = null
|
||||
private var currentConversation: Conversation? = null
|
||||
var currentConversation: ConversationModel? = null
|
||||
private var leavingScreen = false
|
||||
private var handler: Handler? = null
|
||||
private var binding: CallNotificationActivityBinding? = null
|
||||
|
@ -98,19 +103,20 @@ class CallNotificationActivity : CallBaseActivity() {
|
|||
val extras = intent.extras
|
||||
roomToken = extras!!.getString(KEY_ROOM_TOKEN, "")
|
||||
notificationTimestamp = extras.getInt(BundleKeys.KEY_NOTIFICATION_TIMESTAMP)
|
||||
currentConversation = Parcels.unwrap(extras.getParcelable(KEY_ROOM))
|
||||
userBeingCalled = extras.getParcelable(KEY_USER_ENTITY)
|
||||
|
||||
val internalUserId = extras.getLong(BundleKeys.KEY_INTERNAL_USER_ID)
|
||||
userBeingCalled = userManager.getUserWithId(internalUserId).blockingGet()
|
||||
|
||||
originalBundle = extras
|
||||
credentials = ApiUtils.getCredentials(userBeingCalled!!.username, userBeingCalled!!.token)
|
||||
|
||||
callNotificationViewModel = ViewModelProvider(this, viewModelFactory)[CallNotificationViewModel::class.java]
|
||||
|
||||
initObservers()
|
||||
|
||||
if (userManager.setUserAsActive(userBeingCalled!!).blockingGet()) {
|
||||
setCallDescriptionText()
|
||||
if (currentConversation == null) {
|
||||
handleFromNotification()
|
||||
} else {
|
||||
setUpAfterConversationIsKnown()
|
||||
}
|
||||
initClickListeners()
|
||||
callNotificationViewModel.getRoom(userBeingCalled!!, roomToken!!)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -140,6 +146,78 @@ class CallNotificationActivity : CallBaseActivity() {
|
|||
binding!!.hangupButton.setOnClickListener { hangup() }
|
||||
}
|
||||
|
||||
private fun initObservers() {
|
||||
val apiVersion = ApiUtils.getConversationApiVersion(
|
||||
userBeingCalled,
|
||||
intArrayOf(
|
||||
ApiUtils.APIv4,
|
||||
ApiUtils.APIv3,
|
||||
1
|
||||
)
|
||||
)
|
||||
|
||||
callNotificationViewModel.getRoomViewState.observe(this) { state ->
|
||||
when (state) {
|
||||
is CallNotificationViewModel.GetRoomSuccessState -> {
|
||||
currentConversation = state.conversationModel
|
||||
|
||||
binding!!.conversationNameTextView.text = currentConversation!!.displayName
|
||||
if (currentConversation!!.type === ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL) {
|
||||
binding!!.avatarImageView.loadUserAvatar(
|
||||
userBeingCalled!!,
|
||||
currentConversation!!.name!!,
|
||||
true,
|
||||
false
|
||||
)
|
||||
} else {
|
||||
binding!!.avatarImageView.setImageResource(R.drawable.ic_circular_group)
|
||||
}
|
||||
|
||||
val notificationHandler = Handler(Looper.getMainLooper())
|
||||
notificationHandler.post(object : Runnable {
|
||||
override fun run() {
|
||||
if (NotificationUtils.isNotificationVisible(context, notificationTimestamp!!.toInt())) {
|
||||
notificationHandler.postDelayed(this, ONE_SECOND)
|
||||
} else {
|
||||
finish()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
showAnswerControls()
|
||||
|
||||
if (apiVersion >= ApiUtils.APIv3) {
|
||||
val hasCallFlags = hasSpreedFeatureCapability(
|
||||
userBeingCalled,
|
||||
"conversation-call-flags"
|
||||
)
|
||||
if (hasCallFlags) {
|
||||
if (isInCallWithVideo(currentConversation!!.callFlag)) {
|
||||
binding!!.incomingCallVoiceOrVideoTextView.text = String.format(
|
||||
resources.getString(R.string.nc_call_video),
|
||||
resources.getString(R.string.nc_app_product_name)
|
||||
)
|
||||
} else {
|
||||
binding!!.incomingCallVoiceOrVideoTextView.text = String.format(
|
||||
resources.getString(R.string.nc_call_voice),
|
||||
resources.getString(R.string.nc_app_product_name)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
initClickListeners()
|
||||
}
|
||||
|
||||
is CallNotificationViewModel.GetRoomErrorState -> {
|
||||
Toast.makeText(context, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setCallDescriptionText() {
|
||||
val callDescriptionWithoutTypeInfo = String.format(
|
||||
resources.getString(R.string.nc_call_unknown),
|
||||
|
@ -178,7 +256,7 @@ class CallNotificationActivity : CallBaseActivity() {
|
|||
)
|
||||
originalBundle!!.putBoolean(
|
||||
BundleKeys.KEY_IS_MODERATOR,
|
||||
currentConversation!!.isParticipantOwnerOrModerator
|
||||
ConversationUtils.isParticipantOwnerOrModerator(currentConversation!!)
|
||||
)
|
||||
|
||||
val intent = Intent(this, CallActivity::class.java)
|
||||
|
@ -189,85 +267,10 @@ class CallNotificationActivity : CallBaseActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
private fun handleFromNotification() {
|
||||
val apiVersion = ApiUtils.getConversationApiVersion(
|
||||
userBeingCalled,
|
||||
intArrayOf(
|
||||
ApiUtils.APIv4,
|
||||
ApiUtils.APIv3,
|
||||
1
|
||||
)
|
||||
)
|
||||
ncApi!!.getRoom(credentials, ApiUtils.getUrlForRoom(apiVersion, userBeingCalled!!.baseUrl, roomToken))
|
||||
.subscribeOn(Schedulers.io())
|
||||
.retry(GET_ROOM_RETRY_COUNT)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : Observer<RoomOverall> {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
disposablesList.add(d)
|
||||
}
|
||||
|
||||
override fun onNext(roomOverall: RoomOverall) {
|
||||
currentConversation = roomOverall.ocs!!.data
|
||||
setUpAfterConversationIsKnown()
|
||||
if (apiVersion >= 3) {
|
||||
val hasCallFlags = hasSpreedFeatureCapability(
|
||||
userBeingCalled,
|
||||
"conversation-call-flags"
|
||||
)
|
||||
if (hasCallFlags) {
|
||||
if (isInCallWithVideo(currentConversation!!.callFlag)) {
|
||||
binding!!.incomingCallVoiceOrVideoTextView.text = String.format(
|
||||
resources.getString(R.string.nc_call_video),
|
||||
resources.getString(R.string.nc_app_product_name)
|
||||
)
|
||||
} else {
|
||||
binding!!.incomingCallVoiceOrVideoTextView.text = String.format(
|
||||
resources.getString(R.string.nc_call_voice),
|
||||
resources.getString(R.string.nc_app_product_name)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
Log.e(TAG, e.message, e)
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
// unused atm
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun isInCallWithVideo(callFlag: Int): Boolean {
|
||||
return (callFlag and Participant.InCallFlags.WITH_VIDEO) > 0
|
||||
}
|
||||
|
||||
private fun setUpAfterConversationIsKnown() {
|
||||
binding!!.conversationNameTextView.text = currentConversation!!.displayName
|
||||
if (currentConversation!!.type === Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL) {
|
||||
binding!!.avatarImageView.loadUserAvatar(userBeingCalled!!, currentConversation!!.name!!, true, false)
|
||||
} else {
|
||||
binding!!.avatarImageView.setImageResource(R.drawable.ic_circular_group)
|
||||
}
|
||||
|
||||
val notificationHandler = Handler(Looper.getMainLooper())
|
||||
notificationHandler.post(object : Runnable {
|
||||
override fun run() {
|
||||
if (NotificationUtils.isNotificationVisible(context, notificationTimestamp!!.toInt())) {
|
||||
notificationHandler.postDelayed(this, ONE_SECOND)
|
||||
} else {
|
||||
finish()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
showAnswerControls()
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
val notificationManager = NotificationManagerCompat.from(context)
|
||||
notificationManager.cancel(notificationTimestamp!!)
|
||||
|
@ -303,23 +306,22 @@ class CallNotificationActivity : CallBaseActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
public override fun updateUiForPipMode() {
|
||||
override fun updateUiForPipMode() {
|
||||
binding!!.callAnswerButtons.visibility = View.INVISIBLE
|
||||
binding!!.incomingCallRelativeLayout.visibility = View.INVISIBLE
|
||||
}
|
||||
|
||||
public override fun updateUiForNormalMode() {
|
||||
override fun updateUiForNormalMode() {
|
||||
binding!!.callAnswerButtons.visibility = View.VISIBLE
|
||||
binding!!.incomingCallRelativeLayout.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
public override fun suppressFitsSystemWindows() {
|
||||
override fun suppressFitsSystemWindows() {
|
||||
binding!!.controllerCallNotificationLayout.fitsSystemWindows = false
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TAG = "CallNotificationActivity"
|
||||
const val GET_ROOM_RETRY_COUNT: Long = 3
|
||||
const val ONE_SECOND: Long = 1000
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Marcel Hibbe
|
||||
* Copyright (C) 2023 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.callnotification.viewmodel
|
||||
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.nextcloud.talk.chat.data.ChatRepository
|
||||
import com.nextcloud.talk.data.user.model.User
|
||||
import com.nextcloud.talk.models.domain.ConversationModel
|
||||
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 CallNotificationViewModel @Inject constructor(private val repository: ChatRepository) :
|
||||
ViewModel() {
|
||||
|
||||
sealed interface ViewState
|
||||
|
||||
object GetRoomStartState : ViewState
|
||||
object GetRoomErrorState : ViewState
|
||||
open class GetRoomSuccessState(val conversationModel: ConversationModel) : ViewState
|
||||
|
||||
private val _getRoomViewState: MutableLiveData<ViewState> = MutableLiveData(GetRoomStartState)
|
||||
val getRoomViewState: LiveData<ViewState>
|
||||
get() = _getRoomViewState
|
||||
|
||||
fun getRoom(user: User, token: String) {
|
||||
_getRoomViewState.value = GetRoomStartState
|
||||
repository.getRoom(user, token)
|
||||
.subscribeOn(Schedulers.io())
|
||||
?.observeOn(AndroidSchedulers.mainThread())
|
||||
?.subscribe(GetRoomObserver())
|
||||
}
|
||||
|
||||
inner class GetRoomObserver : Observer<ConversationModel> {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
// unused atm
|
||||
}
|
||||
|
||||
override fun onNext(conversationModel: ConversationModel) {
|
||||
_getRoomViewState.value = GetRoomSuccessState(conversationModel)
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
Log.e(TAG, "Error when fetching room")
|
||||
_getRoomViewState.value = GetRoomErrorState
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
// unused atm
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG = CallNotificationViewModel::class.simpleName
|
||||
}
|
||||
}
|
|
@ -48,7 +48,6 @@ import android.os.Build
|
|||
import android.os.Bundle
|
||||
import android.os.CountDownTimer
|
||||
import android.os.Handler
|
||||
import android.os.Parcelable
|
||||
import android.os.SystemClock
|
||||
import android.provider.ContactsContract
|
||||
import android.provider.MediaStore
|
||||
|
@ -85,6 +84,7 @@ import androidx.core.text.bold
|
|||
import androidx.core.widget.doAfterTextChanged
|
||||
import androidx.emoji2.text.EmojiCompat
|
||||
import androidx.emoji2.widget.EmojiTextView
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
@ -130,6 +130,7 @@ import com.nextcloud.talk.adapters.messages.VoiceMessageInterface
|
|||
import com.nextcloud.talk.api.NcApi
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||
import com.nextcloud.talk.callbacks.MentionAutocompleteCallback
|
||||
import com.nextcloud.talk.chat.viewmodels.ChatViewModel
|
||||
import com.nextcloud.talk.conversationinfo.ConversationInfoActivity
|
||||
import com.nextcloud.talk.conversationlist.ConversationsListActivity
|
||||
import com.nextcloud.talk.data.user.model.User
|
||||
|
@ -142,15 +143,18 @@ import com.nextcloud.talk.jobs.ShareOperationWorker
|
|||
import com.nextcloud.talk.jobs.UploadAndShareFilesWorker
|
||||
import com.nextcloud.talk.location.LocationPickerActivity
|
||||
import com.nextcloud.talk.messagesearch.MessageSearchActivity
|
||||
import com.nextcloud.talk.models.domain.ConversationModel
|
||||
import com.nextcloud.talk.models.domain.ConversationReadOnlyState
|
||||
import com.nextcloud.talk.models.domain.ConversationType
|
||||
import com.nextcloud.talk.models.domain.LobbyState
|
||||
import com.nextcloud.talk.models.domain.ObjectType
|
||||
import com.nextcloud.talk.models.domain.ReactionAddedModel
|
||||
import com.nextcloud.talk.models.domain.ReactionDeletedModel
|
||||
import com.nextcloud.talk.models.json.chat.ChatMessage
|
||||
import com.nextcloud.talk.models.json.chat.ChatOverall
|
||||
import com.nextcloud.talk.models.json.chat.ChatOverallSingleMessage
|
||||
import com.nextcloud.talk.models.json.chat.ReadStatus
|
||||
import com.nextcloud.talk.models.json.conversations.Conversation
|
||||
import com.nextcloud.talk.models.json.conversations.RoomOverall
|
||||
import com.nextcloud.talk.models.json.conversations.RoomsOverall
|
||||
import com.nextcloud.talk.models.json.generic.GenericOverall
|
||||
import com.nextcloud.talk.models.json.mention.Mention
|
||||
import com.nextcloud.talk.models.json.signaling.NCSignalingMessage
|
||||
|
@ -170,6 +174,7 @@ import com.nextcloud.talk.ui.recyclerview.MessageSwipeActions
|
|||
import com.nextcloud.talk.ui.recyclerview.MessageSwipeCallback
|
||||
import com.nextcloud.talk.utils.ApiUtils
|
||||
import com.nextcloud.talk.utils.ContactUtils
|
||||
import com.nextcloud.talk.utils.ConversationUtils
|
||||
import com.nextcloud.talk.utils.DateConstants
|
||||
import com.nextcloud.talk.utils.DateUtils
|
||||
import com.nextcloud.talk.utils.DisplayUtils
|
||||
|
@ -181,7 +186,6 @@ import com.nextcloud.talk.utils.NotificationUtils
|
|||
import com.nextcloud.talk.utils.ParticipantPermissions
|
||||
import com.nextcloud.talk.utils.VibrationUtils
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ACTIVE_CONVERSATION
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CALL_VOICE_ONLY
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CONVERSATION_NAME
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_FILE_PATHS
|
||||
|
@ -193,8 +197,8 @@ import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_ID
|
|||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_START_CALL_AFTER_ROOM_SWITCH
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_SWITCH_TO_ROOM
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_USER_ENTITY
|
||||
import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew
|
||||
import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew
|
||||
import com.nextcloud.talk.utils.permissions.PlatformPermissionUtil
|
||||
import com.nextcloud.talk.utils.rx.DisposableSet
|
||||
import com.nextcloud.talk.utils.singletons.ApplicationWideCurrentRoomHolder
|
||||
|
@ -215,7 +219,6 @@ import io.reactivex.disposables.Disposable
|
|||
import io.reactivex.schedulers.Schedulers
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
import org.parceler.Parcels
|
||||
import retrofit2.HttpException
|
||||
import retrofit2.Response
|
||||
import java.io.File
|
||||
|
@ -248,6 +251,9 @@ class ChatActivity :
|
|||
@Inject
|
||||
lateinit var ncApi: NcApi
|
||||
|
||||
@Inject
|
||||
lateinit var currentUserProvider: CurrentUserProviderNew
|
||||
|
||||
@Inject
|
||||
lateinit var reactionsRepository: ReactionsRepository
|
||||
|
||||
|
@ -257,6 +263,11 @@ class ChatActivity :
|
|||
@Inject
|
||||
lateinit var dateUtils: DateUtils
|
||||
|
||||
@Inject
|
||||
lateinit var viewModelFactory: ViewModelProvider.Factory
|
||||
|
||||
lateinit var chatViewModel: ChatViewModel
|
||||
|
||||
override val view: View
|
||||
get() = binding.root
|
||||
|
||||
|
@ -267,7 +278,7 @@ class ChatActivity :
|
|||
var conversationUser: User? = null
|
||||
private var roomPassword: String = ""
|
||||
var credentials: String? = null
|
||||
var currentConversation: Conversation? = null
|
||||
var currentConversation: ConversationModel? = null
|
||||
private var globalLastKnownFutureMessageId = -1
|
||||
private var globalLastKnownPastMessageId = -1
|
||||
var adapter: TalkMessagesListAdapter<ChatMessage>? = null
|
||||
|
@ -383,14 +394,17 @@ class ChatActivity :
|
|||
setContentView(binding.root)
|
||||
setupSystemColors()
|
||||
|
||||
conversationUser = currentUserProvider.currentUser.blockingGet()
|
||||
|
||||
handleIntent(intent)
|
||||
|
||||
chatViewModel = ViewModelProvider(this, viewModelFactory)[ChatViewModel::class.java]
|
||||
|
||||
binding.progressBar.visibility = View.VISIBLE
|
||||
|
||||
initAdapter()
|
||||
binding.messagesListView.setAdapter(adapter)
|
||||
|
||||
onBackPressedDispatcher.addCallback(this, onBackPressedCallback)
|
||||
|
||||
initObservers()
|
||||
}
|
||||
|
||||
override fun onNewIntent(intent: Intent) {
|
||||
|
@ -415,7 +429,6 @@ class ChatActivity :
|
|||
private fun handleIntent(intent: Intent) {
|
||||
val extras: Bundle? = intent.extras
|
||||
|
||||
conversationUser = extras?.getParcelable(KEY_USER_ENTITY)
|
||||
roomId = extras?.getString(KEY_ROOM_ID).orEmpty()
|
||||
roomToken = extras?.getString(KEY_ROOM_TOKEN).orEmpty()
|
||||
|
||||
|
@ -426,11 +439,6 @@ class ChatActivity :
|
|||
Log.d(TAG, " roomToken was null or empty!")
|
||||
}
|
||||
|
||||
if (intent.hasExtra(KEY_ACTIVE_CONVERSATION)) {
|
||||
currentConversation = Parcels.unwrap<Conversation>(extras?.getParcelable(KEY_ACTIVE_CONVERSATION))
|
||||
participantPermissions = ParticipantPermissions(conversationUser!!, currentConversation!!)
|
||||
}
|
||||
|
||||
roomPassword = extras?.getString(BundleKeys.KEY_CONVERSATION_PASSWORD).orEmpty()
|
||||
|
||||
credentials = if (conversationUser?.userId == "?") {
|
||||
|
@ -455,6 +463,106 @@ class ChatActivity :
|
|||
active = false
|
||||
}
|
||||
|
||||
private fun initObservers() {
|
||||
chatViewModel.getRoomViewState.observe(this) { state ->
|
||||
when (state) {
|
||||
is ChatViewModel.GetRoomSuccessState -> {
|
||||
currentConversation = state.conversationModel
|
||||
logConversationInfos("GetRoomSuccessState")
|
||||
|
||||
if (adapter == null) {
|
||||
initAdapter()
|
||||
binding.messagesListView.setAdapter(adapter)
|
||||
}
|
||||
|
||||
layoutManager = binding.messagesListView.layoutManager as LinearLayoutManager?
|
||||
|
||||
loadAvatarForStatusBar()
|
||||
setActionBarTitle()
|
||||
participantPermissions = ParticipantPermissions(conversationUser!!, currentConversation!!)
|
||||
|
||||
setupSwipeToReply()
|
||||
setupMentionAutocomplete()
|
||||
checkShowCallButtons()
|
||||
checkShowMessageInputView()
|
||||
checkLobbyState()
|
||||
|
||||
if (!validSessionId()) {
|
||||
joinRoomWithPassword()
|
||||
} else {
|
||||
Log.d(TAG, "already inConversation. joinRoomWithPassword is skipped")
|
||||
}
|
||||
|
||||
val delayForRecursiveCall = if (shouldShowLobby()) {
|
||||
GET_ROOM_INFO_DELAY_LOBBY
|
||||
} else {
|
||||
GET_ROOM_INFO_DELAY_NORMAL
|
||||
}
|
||||
|
||||
if (getRoomInfoTimerHandler == null) {
|
||||
getRoomInfoTimerHandler = Handler()
|
||||
}
|
||||
getRoomInfoTimerHandler?.postDelayed(
|
||||
{
|
||||
chatViewModel.getRoom(conversationUser!!, roomToken)
|
||||
},
|
||||
delayForRecursiveCall
|
||||
)
|
||||
}
|
||||
|
||||
is ChatViewModel.GetRoomErrorState -> {
|
||||
Toast.makeText(context, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
chatViewModel.joinRoomViewState.observe(this) { state ->
|
||||
when (state) {
|
||||
is ChatViewModel.JoinRoomSuccessState -> {
|
||||
currentConversation = state.conversationModel
|
||||
|
||||
sessionIdAfterRoomJoined = currentConversation!!.sessionId
|
||||
ApplicationWideCurrentRoomHolder.getInstance().session = currentConversation!!.sessionId
|
||||
ApplicationWideCurrentRoomHolder.getInstance().currentRoomId = currentConversation!!.roomId
|
||||
ApplicationWideCurrentRoomHolder.getInstance().currentRoomToken = currentConversation!!.token
|
||||
ApplicationWideCurrentRoomHolder.getInstance().userInRoom = conversationUser
|
||||
|
||||
logConversationInfos("joinRoomWithPassword#onNext")
|
||||
|
||||
if (isFirstMessagesProcessing) {
|
||||
pullChatMessages(false)
|
||||
} else {
|
||||
pullChatMessages(true, false)
|
||||
}
|
||||
|
||||
if (webSocketInstance != null) {
|
||||
webSocketInstance?.joinRoomWithRoomTokenAndSession(
|
||||
roomToken,
|
||||
sessionIdAfterRoomJoined
|
||||
)
|
||||
}
|
||||
if (startCallFromNotification != null && startCallFromNotification ?: false) {
|
||||
startCallFromNotification = false
|
||||
startACall(voiceOnly, false)
|
||||
}
|
||||
|
||||
if (startCallFromRoomSwitch) {
|
||||
startCallFromRoomSwitch = false
|
||||
startACall(voiceOnly, true)
|
||||
}
|
||||
}
|
||||
|
||||
is ChatViewModel.JoinRoomErrorState -> {
|
||||
Toast.makeText(context, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("Detekt.TooGenericExceptionCaught")
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
|
@ -484,18 +592,12 @@ class ChatActivity :
|
|||
|
||||
cancelNotificationsForCurrentConversation()
|
||||
|
||||
if (TextUtils.isEmpty(roomToken)) {
|
||||
handleFromNotification()
|
||||
} else {
|
||||
getRoomInfo()
|
||||
}
|
||||
chatViewModel.getRoom(conversationUser!!, roomToken)
|
||||
|
||||
actionBar?.show()
|
||||
|
||||
setupSwipeToReply()
|
||||
|
||||
layoutManager = binding?.messagesListView?.layoutManager as LinearLayoutManager?
|
||||
|
||||
binding?.popupBubbleView?.setRecyclerView(binding?.messagesListView)
|
||||
|
||||
binding?.popupBubbleView?.setPopupBubbleListener { context ->
|
||||
|
@ -632,10 +734,8 @@ class ChatActivity :
|
|||
|
||||
binding?.messageInputView?.button?.let { viewThemeUtils.platform.colorImageView(it, ColorRole.PRIMARY) }
|
||||
|
||||
if (currentConversation != null && currentConversation?.roomId != null) {
|
||||
loadAvatarForStatusBar()
|
||||
setActionBarTitle()
|
||||
}
|
||||
|
||||
viewThemeUtils.material.colorToolbarOverflowIcon(binding.chatToolbar)
|
||||
}
|
||||
|
@ -694,8 +794,11 @@ class ChatActivity :
|
|||
val messageHolders = MessageHolders()
|
||||
val profileBottomSheet = ProfileBottomSheet(ncApi, conversationUser!!)
|
||||
|
||||
val payload =
|
||||
MessagePayload(roomToken!!, currentConversation?.isParticipantOwnerOrModerator, profileBottomSheet)
|
||||
val payload = MessagePayload(
|
||||
roomToken,
|
||||
ConversationUtils.isParticipantOwnerOrModerator(currentConversation!!),
|
||||
profileBottomSheet
|
||||
)
|
||||
|
||||
messageHolders.setIncomingTextConfig(
|
||||
IncomingTextMessageViewHolder::class.java,
|
||||
|
@ -1080,68 +1183,6 @@ class ChatActivity :
|
|||
!CapabilitiesUtilNew.isTypingStatusPrivate(conversationUser!!)
|
||||
}
|
||||
|
||||
private fun getRoomInfo() {
|
||||
logConversationInfos("getRoomInfo")
|
||||
|
||||
conversationUser?.let {
|
||||
val apiVersion = ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.APIv4, 1))
|
||||
|
||||
val startNanoTime = System.nanoTime()
|
||||
Log.d(TAG, "getRoomInfo - getRoom - calling: $startNanoTime")
|
||||
ncApi.getRoom(credentials, ApiUtils.getUrlForRoom(apiVersion, it.baseUrl, roomToken))
|
||||
?.subscribeOn(Schedulers.io())
|
||||
?.observeOn(AndroidSchedulers.mainThread())
|
||||
?.subscribe(object : Observer<RoomOverall> {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
disposables.add(d)
|
||||
}
|
||||
|
||||
@Suppress("Detekt.TooGenericExceptionCaught")
|
||||
override fun onNext(roomOverall: RoomOverall) {
|
||||
Log.d(TAG, "getRoomInfo - getRoom - got response: $startNanoTime")
|
||||
currentConversation = roomOverall.ocs!!.data
|
||||
|
||||
logConversationInfos("getRoomInfo#onNext")
|
||||
|
||||
loadAvatarForStatusBar()
|
||||
setActionBarTitle()
|
||||
participantPermissions = ParticipantPermissions(it, currentConversation!!)
|
||||
|
||||
setupSwipeToReply()
|
||||
setupMentionAutocomplete()
|
||||
checkShowCallButtons()
|
||||
checkShowMessageInputView()
|
||||
checkLobbyState()
|
||||
|
||||
if (!validSessionId()) {
|
||||
joinRoomWithPassword()
|
||||
} else {
|
||||
Log.d(TAG, "already inConversation. joinRoomWithPassword is skipped")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
Log.e(TAG, "getRoomInfo - getRoom - ERROR", e)
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
Log.d(TAG, "getRoomInfo - getRoom - onComplete: $startNanoTime")
|
||||
|
||||
val delayForRecursiveCall = if (shouldShowLobby()) {
|
||||
GET_ROOM_INFO_DELAY_LOBBY
|
||||
} else {
|
||||
GET_ROOM_INFO_DELAY_NORMAL
|
||||
}
|
||||
|
||||
if (getRoomInfoTimerHandler == null) {
|
||||
getRoomInfoTimerHandler = Handler()
|
||||
}
|
||||
getRoomInfoTimerHandler?.postDelayed({ getRoomInfo() }, delayForRecursiveCall)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupSwipeToReply() {
|
||||
if (this::participantPermissions.isInitialized &&
|
||||
participantPermissions.hasChatPermission() &&
|
||||
|
@ -1162,46 +1203,11 @@ class ChatActivity :
|
|||
}
|
||||
}
|
||||
|
||||
private fun handleFromNotification() {
|
||||
var apiVersion = 1
|
||||
// FIXME Can this be called for guests?
|
||||
if (conversationUser != null) {
|
||||
apiVersion = ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.APIv4, 1))
|
||||
}
|
||||
|
||||
Log.d(TAG, "handleFromNotification - getRooms - calling")
|
||||
ncApi.getRooms(credentials, ApiUtils.getUrlForRooms(apiVersion, conversationUser?.baseUrl), false)
|
||||
?.subscribeOn(Schedulers.io())?.observeOn(AndroidSchedulers.mainThread())
|
||||
?.subscribe(object : Observer<RoomsOverall> {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
disposables.add(d)
|
||||
}
|
||||
|
||||
override fun onNext(roomsOverall: RoomsOverall) {
|
||||
Log.d(TAG, "handleFromNotification - getRooms - got response")
|
||||
for (conversation in roomsOverall.ocs!!.data!!) {
|
||||
if (roomId == conversation.roomId) {
|
||||
roomToken = conversation.token!!
|
||||
currentConversation = conversation
|
||||
participantPermissions = ParticipantPermissions(conversationUser!!, currentConversation!!)
|
||||
setActionBarTitle()
|
||||
getRoomInfo()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
Log.e(TAG, "handleFromNotification - getRooms - ERROR: ", e)
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
// unused atm
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun loadAvatarForStatusBar() {
|
||||
if (currentConversation == null) {
|
||||
return
|
||||
}
|
||||
|
||||
if (isOneToOneConversation()) {
|
||||
var url = ApiUtils.getUrlForAvatar(
|
||||
conversationUser!!.baseUrl,
|
||||
|
@ -1254,18 +1260,18 @@ class ChatActivity :
|
|||
}
|
||||
|
||||
fun isOneToOneConversation() = currentConversation != null && currentConversation?.type != null &&
|
||||
currentConversation?.type == Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL
|
||||
currentConversation?.type == ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL
|
||||
|
||||
private fun isGroupConversation() = currentConversation != null && currentConversation?.type != null &&
|
||||
currentConversation?.type == Conversation.ConversationType.ROOM_GROUP_CALL
|
||||
currentConversation?.type == ConversationType.ROOM_GROUP_CALL
|
||||
|
||||
private fun isPublicConversation() = currentConversation != null && currentConversation?.type != null &&
|
||||
currentConversation?.type == Conversation.ConversationType.ROOM_PUBLIC_CALL
|
||||
currentConversation?.type == ConversationType.ROOM_PUBLIC_CALL
|
||||
|
||||
private fun switchToRoom(token: String, startCallAfterRoomSwitch: Boolean, isVoiceOnlyCall: Boolean) {
|
||||
if (conversationUser != null) {
|
||||
runOnUiThread {
|
||||
if (currentConversation?.objectType == Conversation.ObjectType.ROOM) {
|
||||
if (currentConversation?.objectType == ObjectType.ROOM) {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.resources.getString(R.string.switch_to_main_room),
|
||||
|
@ -1281,7 +1287,6 @@ class ChatActivity :
|
|||
}
|
||||
|
||||
val bundle = Bundle()
|
||||
bundle.putParcelable(KEY_USER_ENTITY, conversationUser)
|
||||
bundle.putString(KEY_ROOM_TOKEN, token)
|
||||
|
||||
if (startCallAfterRoomSwitch) {
|
||||
|
@ -1697,8 +1702,8 @@ class ChatActivity :
|
|||
private fun shouldShowLobby(): Boolean {
|
||||
if (currentConversation != null) {
|
||||
return CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "webinary-lobby") &&
|
||||
currentConversation?.lobbyState == Conversation.LobbyState.LOBBY_STATE_MODERATORS_ONLY &&
|
||||
currentConversation?.canModerate(conversationUser!!) == false &&
|
||||
currentConversation?.lobbyState == LobbyState.LOBBY_STATE_MODERATORS_ONLY &&
|
||||
!ConversationUtils.canModerate(currentConversation!!, conversationUser!!) &&
|
||||
!participantPermissions.canIgnoreLobby()
|
||||
}
|
||||
return false
|
||||
|
@ -1733,12 +1738,12 @@ class ChatActivity :
|
|||
private fun isReadOnlyConversation(): Boolean {
|
||||
return currentConversation?.conversationReadOnlyState != null &&
|
||||
currentConversation?.conversationReadOnlyState ==
|
||||
Conversation.ConversationReadOnlyState.CONVERSATION_READ_ONLY
|
||||
ConversationReadOnlyState.CONVERSATION_READ_ONLY
|
||||
}
|
||||
|
||||
private fun checkLobbyState() {
|
||||
if (currentConversation != null &&
|
||||
currentConversation?.isLobbyViewApplicable(conversationUser!!) == true
|
||||
ConversationUtils.isLobbyViewApplicable(currentConversation!!, conversationUser!!)
|
||||
) {
|
||||
if (shouldShowLobby()) {
|
||||
binding?.lobby?.lobbyView?.visibility = View.VISIBLE
|
||||
|
@ -2119,7 +2124,7 @@ class ChatActivity :
|
|||
|
||||
private fun showConversationInfoScreen() {
|
||||
val bundle = Bundle()
|
||||
bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, conversationUser)
|
||||
|
||||
bundle.putString(KEY_ROOM_TOKEN, roomToken)
|
||||
bundle.putBoolean(BundleKeys.KEY_ROOM_ONE_TO_ONE, isOneToOneConversation())
|
||||
|
||||
|
@ -2274,65 +2279,8 @@ class ChatActivity :
|
|||
|
||||
val startNanoTime = System.nanoTime()
|
||||
Log.d(TAG, "joinRoomWithPassword - joinRoom - calling: $startNanoTime")
|
||||
ncApi.joinRoom(
|
||||
credentials,
|
||||
ApiUtils.getUrlForParticipantsActive(apiVersion, conversationUser?.baseUrl, roomToken),
|
||||
roomPassword
|
||||
)
|
||||
?.subscribeOn(Schedulers.io())
|
||||
?.observeOn(AndroidSchedulers.mainThread())
|
||||
?.retry(RETRIES)
|
||||
?.subscribe(object : Observer<RoomOverall> {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
disposables.add(d)
|
||||
}
|
||||
|
||||
@Suppress("Detekt.TooGenericExceptionCaught")
|
||||
override fun onNext(roomOverall: RoomOverall) {
|
||||
Log.d(TAG, "joinRoomWithPassword - joinRoom - got response: $startNanoTime")
|
||||
|
||||
val conversation = roomOverall.ocs!!.data!!
|
||||
currentConversation = conversation
|
||||
|
||||
sessionIdAfterRoomJoined = conversation.sessionId
|
||||
ApplicationWideCurrentRoomHolder.getInstance().session = conversation.sessionId
|
||||
ApplicationWideCurrentRoomHolder.getInstance().currentRoomId = conversation.roomId
|
||||
ApplicationWideCurrentRoomHolder.getInstance().currentRoomToken = conversation.token
|
||||
ApplicationWideCurrentRoomHolder.getInstance().userInRoom = conversationUser
|
||||
|
||||
logConversationInfos("joinRoomWithPassword#onNext")
|
||||
|
||||
if (isFirstMessagesProcessing) {
|
||||
pullChatMessages(false)
|
||||
} else {
|
||||
pullChatMessages(true, false)
|
||||
}
|
||||
|
||||
if (webSocketInstance != null) {
|
||||
webSocketInstance?.joinRoomWithRoomTokenAndSession(
|
||||
roomToken!!,
|
||||
sessionIdAfterRoomJoined
|
||||
)
|
||||
}
|
||||
if (startCallFromNotification != null && startCallFromNotification ?: false) {
|
||||
startCallFromNotification = false
|
||||
startACall(voiceOnly, false)
|
||||
}
|
||||
|
||||
if (startCallFromRoomSwitch) {
|
||||
startCallFromRoomSwitch = false
|
||||
startACall(voiceOnly, true)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
Log.e(TAG, "joinRoomWithPassword - joinRoom - ERROR", e)
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
// unused atm
|
||||
}
|
||||
})
|
||||
chatViewModel.joinRoom(conversationUser!!, roomToken, roomPassword)
|
||||
} else {
|
||||
Log.d(TAG, "sessionID was valid -> skip joinRoom")
|
||||
|
||||
|
@ -2801,9 +2749,9 @@ class ChatActivity :
|
|||
GROUPED_MESSAGES_SAME_AUTHOR_THRESHOLD > 0
|
||||
)
|
||||
chatMessage.isOneToOneConversation =
|
||||
(currentConversation?.type == Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL)
|
||||
(currentConversation?.type == ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL)
|
||||
chatMessage.isFormerOneToOneConversation =
|
||||
(currentConversation?.type == Conversation.ConversationType.FORMER_ONE_TO_ONE)
|
||||
(currentConversation?.type == ConversationType.FORMER_ONE_TO_ONE)
|
||||
it.addToStart(chatMessage, shouldScroll)
|
||||
}
|
||||
}
|
||||
|
@ -2845,9 +2793,9 @@ class ChatActivity :
|
|||
|
||||
val chatMessage = chatMessageList[i]
|
||||
chatMessage.isOneToOneConversation =
|
||||
currentConversation?.type == Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL
|
||||
currentConversation?.type == ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL
|
||||
chatMessage.isFormerOneToOneConversation =
|
||||
(currentConversation?.type == Conversation.ConversationType.FORMER_ONE_TO_ONE)
|
||||
(currentConversation?.type == ConversationType.FORMER_ONE_TO_ONE)
|
||||
chatMessage.activeUser = conversationUser
|
||||
}
|
||||
|
||||
|
@ -3032,10 +2980,9 @@ class ChatActivity :
|
|||
val intent = Intent(this, SharedItemsActivity::class.java)
|
||||
intent.putExtra(KEY_CONVERSATION_NAME, currentConversation?.displayName)
|
||||
intent.putExtra(KEY_ROOM_TOKEN, roomToken)
|
||||
intent.putExtra(KEY_USER_ENTITY, conversationUser as Parcelable)
|
||||
intent.putExtra(
|
||||
SharedItemsActivity.KEY_USER_IS_OWNER_OR_MODERATOR,
|
||||
currentConversation?.isParticipantOwnerOrModerator
|
||||
ConversationUtils.isParticipantOwnerOrModerator(currentConversation!!)
|
||||
)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
@ -3119,12 +3066,11 @@ class ChatActivity :
|
|||
val bundle = Bundle()
|
||||
bundle.putString(KEY_ROOM_TOKEN, roomToken)
|
||||
bundle.putString(KEY_ROOM_ID, roomId)
|
||||
bundle.putParcelable(KEY_USER_ENTITY, conversationUser)
|
||||
bundle.putString(BundleKeys.KEY_CONVERSATION_PASSWORD, roomPassword)
|
||||
bundle.putString(BundleKeys.KEY_MODIFIED_BASE_URL, conversationUser?.baseUrl)
|
||||
bundle.putString(KEY_CONVERSATION_NAME, it.displayName)
|
||||
bundle.putInt(KEY_RECORDING_STATE, it.callRecording)
|
||||
bundle.putBoolean(KEY_IS_MODERATOR, it.isParticipantOwnerOrModerator)
|
||||
bundle.putBoolean(KEY_IS_MODERATOR, ConversationUtils.isParticipantOwnerOrModerator(it))
|
||||
bundle.putBoolean(
|
||||
BundleKeys.KEY_PARTICIPANT_PERMISSION_CAN_PUBLISH_AUDIO,
|
||||
participantPermissions.canPublishAudio()
|
||||
|
@ -3141,7 +3087,7 @@ class ChatActivity :
|
|||
bundle.putBoolean(BundleKeys.KEY_CALL_WITHOUT_NOTIFICATION, true)
|
||||
}
|
||||
|
||||
if (it.objectType == Conversation.ObjectType.ROOM) {
|
||||
if (it.objectType == ObjectType.ROOM) {
|
||||
bundle.putBoolean(KEY_IS_BREAKOUT_ROOM, true)
|
||||
}
|
||||
|
||||
|
@ -3156,12 +3102,12 @@ class ChatActivity :
|
|||
override fun onClickReaction(chatMessage: ChatMessage, emoji: String) {
|
||||
VibrationUtils.vibrateShort(context)
|
||||
if (chatMessage.reactionsSelf?.contains(emoji) == true) {
|
||||
reactionsRepository.deleteReaction(currentConversation!!, chatMessage, emoji)
|
||||
reactionsRepository.deleteReaction(roomToken, chatMessage, emoji)
|
||||
.subscribeOn(Schedulers.io())
|
||||
?.observeOn(AndroidSchedulers.mainThread())
|
||||
?.subscribe(ReactionDeletedObserver())
|
||||
} else {
|
||||
reactionsRepository.addReaction(currentConversation!!, chatMessage, emoji)
|
||||
reactionsRepository.addReaction(roomToken, chatMessage, emoji)
|
||||
.subscribeOn(Schedulers.io())
|
||||
?.observeOn(AndroidSchedulers.mainThread())
|
||||
?.subscribe(ReactionAddedObserver())
|
||||
|
@ -3171,7 +3117,7 @@ class ChatActivity :
|
|||
override fun onLongClickReactions(chatMessage: ChatMessage) {
|
||||
ShowReactionsDialog(
|
||||
this,
|
||||
currentConversation,
|
||||
roomToken,
|
||||
chatMessage,
|
||||
conversationUser,
|
||||
participantPermissions.hasChatPermission(),
|
||||
|
@ -3339,32 +3285,9 @@ class ChatActivity :
|
|||
|
||||
override fun onNext(roomOverall: RoomOverall) {
|
||||
val bundle = Bundle()
|
||||
bundle.putParcelable(KEY_USER_ENTITY, conversationUser)
|
||||
bundle.putString(KEY_ROOM_TOKEN, roomOverall.ocs!!.data!!.token)
|
||||
bundle.putString(KEY_ROOM_ID, roomOverall.ocs!!.data!!.roomId)
|
||||
|
||||
// FIXME once APIv2+ is used only, the createRoom already returns all the data
|
||||
ncApi.getRoom(
|
||||
credentials,
|
||||
ApiUtils.getUrlForRoom(
|
||||
apiVersion,
|
||||
conversationUser?.baseUrl,
|
||||
roomOverall.ocs!!.data!!.token
|
||||
)
|
||||
)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : Observer<RoomOverall> {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
// unused atm
|
||||
}
|
||||
|
||||
override fun onNext(roomOverall: RoomOverall) {
|
||||
bundle.putParcelable(
|
||||
KEY_ACTIVE_CONVERSATION,
|
||||
Parcels.wrap(roomOverall.ocs!!.data!!)
|
||||
)
|
||||
|
||||
leaveRoom {
|
||||
val chatIntent = Intent(context, ChatActivity::class.java)
|
||||
chatIntent.putExtras(bundle)
|
||||
|
@ -3383,16 +3306,6 @@ class ChatActivity :
|
|||
})
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
Log.e(TAG, e.message, e)
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
// unused atm
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun forwardMessage(message: IMessage?) {
|
||||
val bundle = Bundle()
|
||||
bundle.putBoolean(BundleKeys.KEY_FORWARD_MSG_FLAG, true)
|
||||
|
@ -3471,7 +3384,7 @@ class ChatActivity :
|
|||
conversationUser?.userId?.isNotEmpty() == true && conversationUser!!.userId != "?" &&
|
||||
message.user.id.startsWith("users/") &&
|
||||
message.user.id.substring(ACTOR_LENGTH) != currentConversation?.actorId &&
|
||||
currentConversation?.type != Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL ||
|
||||
currentConversation?.type != ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL ||
|
||||
isShowMessageDeletionButton(message) || // delete
|
||||
ChatMessage.MessageType.REGULAR_TEXT_MESSAGE == message.getCalculateMessageType() || // forward
|
||||
message.previousMessageId > NO_PREVIOUS_MESSAGE_ID && // mark as unread
|
||||
|
@ -3540,7 +3453,7 @@ class ChatActivity :
|
|||
messageTemp.isDeleted = true
|
||||
|
||||
messageTemp.isOneToOneConversation =
|
||||
currentConversation?.type == Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL
|
||||
currentConversation?.type == ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL
|
||||
messageTemp.activeUser = conversationUser
|
||||
|
||||
adapter?.update(messageTemp)
|
||||
|
@ -3550,7 +3463,7 @@ class ChatActivity :
|
|||
val messageTemp = message as ChatMessage
|
||||
|
||||
messageTemp.isOneToOneConversation =
|
||||
currentConversation?.type == Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL
|
||||
currentConversation?.type == ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL
|
||||
messageTemp.activeUser = conversationUser
|
||||
|
||||
adapter?.update(messageTemp)
|
||||
|
@ -3598,7 +3511,7 @@ class ChatActivity :
|
|||
val isUserAllowedByPrivileges = if (message.actorId == conversationUser!!.userId) {
|
||||
true
|
||||
} else {
|
||||
currentConversation!!.canModerate(conversationUser!!)
|
||||
ConversationUtils.canModerate(currentConversation!!, conversationUser!!)
|
||||
}
|
||||
|
||||
val isOlderThanSixHours = message
|
||||
|
@ -3652,7 +3565,7 @@ class ChatActivity :
|
|||
|
||||
@Subscribe(threadMode = ThreadMode.BACKGROUND)
|
||||
fun onMessageEvent(userMentionClickEvent: UserMentionClickEvent) {
|
||||
if (currentConversation?.type != Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL ||
|
||||
if (currentConversation?.type != ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL ||
|
||||
currentConversation?.name != userMentionClickEvent.userId
|
||||
) {
|
||||
var apiVersion = 1
|
||||
|
@ -3683,19 +3596,9 @@ class ChatActivity :
|
|||
}
|
||||
|
||||
override fun onNext(roomOverall: RoomOverall) {
|
||||
val conversationIntent = Intent(context, CallActivity::class.java)
|
||||
val bundle = Bundle()
|
||||
bundle.putParcelable(KEY_USER_ENTITY, conversationUser)
|
||||
bundle.putString(KEY_ROOM_TOKEN, roomOverall.ocs!!.data!!.token)
|
||||
bundle.putString(KEY_ROOM_ID, roomOverall.ocs!!.data!!.roomId)
|
||||
bundle.putBoolean(KEY_IS_MODERATOR, roomOverall.ocs!!.data!!.isParticipantOwnerOrModerator)
|
||||
|
||||
if (conversationUser != null) {
|
||||
bundle.putParcelable(
|
||||
KEY_ACTIVE_CONVERSATION,
|
||||
Parcels.wrap(roomOverall.ocs!!.data)
|
||||
)
|
||||
conversationIntent.putExtras(bundle)
|
||||
|
||||
leaveRoom {
|
||||
val chatIntent = Intent(context, ChatActivity::class.java)
|
||||
|
@ -3703,22 +3606,11 @@ class ChatActivity :
|
|||
chatIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
startActivity(chatIntent)
|
||||
}
|
||||
} else {
|
||||
conversationIntent.putExtras(bundle)
|
||||
startActivity(conversationIntent)
|
||||
Handler().postDelayed(
|
||||
{
|
||||
if (!isDestroyed) {
|
||||
finish()
|
||||
}
|
||||
},
|
||||
POP_CURRENT_CONTROLLER_DELAY
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
// unused atm
|
||||
Log.e(TAG, "error after clicking on user mention chip", e)
|
||||
Toast.makeText(context, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Marcel Hibbe
|
||||
* Copyright (C) 2023 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.chat.data
|
||||
|
||||
import com.nextcloud.talk.data.user.model.User
|
||||
import com.nextcloud.talk.models.domain.ConversationModel
|
||||
|
||||
import io.reactivex.Observable
|
||||
|
||||
interface ChatRepository {
|
||||
fun getRoom(user: User, roomToken: String): Observable<ConversationModel>
|
||||
fun joinRoom(user: User, roomToken: String, roomPassword: String): Observable<ConversationModel>
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Marcel Hibbe
|
||||
* Copyright (C) 2023 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.chat.data
|
||||
|
||||
import com.nextcloud.talk.api.NcApi
|
||||
import com.nextcloud.talk.data.user.model.User
|
||||
import com.nextcloud.talk.models.domain.ConversationModel
|
||||
import com.nextcloud.talk.utils.ApiUtils
|
||||
import io.reactivex.Observable
|
||||
|
||||
class ChatRepositoryImpl(private val ncApi: NcApi) : ChatRepository {
|
||||
override fun getRoom(
|
||||
user: User,
|
||||
roomToken: String
|
||||
): Observable<ConversationModel> {
|
||||
val credentials: String = ApiUtils.getCredentials(user.username, user.token)
|
||||
val apiVersion = ApiUtils.getConversationApiVersion(user, intArrayOf(ApiUtils.APIv4, ApiUtils.APIv3, 1))
|
||||
|
||||
return ncApi.getRoom(
|
||||
credentials,
|
||||
ApiUtils.getUrlForRoom(apiVersion, user.baseUrl, roomToken)
|
||||
).map { ConversationModel.mapToConversationModel(it.ocs?.data!!) }
|
||||
}
|
||||
|
||||
override fun joinRoom(
|
||||
user: User,
|
||||
roomToken: String,
|
||||
roomPassword: String
|
||||
): Observable<ConversationModel> {
|
||||
val credentials: String = ApiUtils.getCredentials(user.username, user.token)
|
||||
val apiVersion = ApiUtils.getConversationApiVersion(user, intArrayOf(ApiUtils.APIv4, 1))
|
||||
|
||||
return ncApi.joinRoom(
|
||||
credentials,
|
||||
ApiUtils.getUrlForParticipantsActive(apiVersion, user.baseUrl, roomToken),
|
||||
roomPassword
|
||||
).map { ConversationModel.mapToConversationModel(it.ocs?.data!!) }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Marcel Hibbe
|
||||
* Copyright (C) 2023 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.chat.viewmodels
|
||||
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.nextcloud.talk.chat.data.ChatRepository
|
||||
import com.nextcloud.talk.data.user.model.User
|
||||
import com.nextcloud.talk.models.domain.ConversationModel
|
||||
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 ChatViewModel @Inject constructor(private val repository: ChatRepository) :
|
||||
ViewModel() {
|
||||
|
||||
sealed interface ViewState
|
||||
|
||||
object GetRoomStartState : ViewState
|
||||
object GetRoomErrorState : ViewState
|
||||
open class GetRoomSuccessState(val conversationModel: ConversationModel) : ViewState
|
||||
|
||||
private val _getRoomViewState: MutableLiveData<ViewState> = MutableLiveData(GetRoomStartState)
|
||||
val getRoomViewState: LiveData<ViewState>
|
||||
get() = _getRoomViewState
|
||||
|
||||
object JoinRoomStartState : ViewState
|
||||
object JoinRoomErrorState : ViewState
|
||||
open class JoinRoomSuccessState(val conversationModel: ConversationModel) : ViewState
|
||||
|
||||
private val _joinRoomViewState: MutableLiveData<ViewState> = MutableLiveData(JoinRoomStartState)
|
||||
val joinRoomViewState: LiveData<ViewState>
|
||||
get() = _joinRoomViewState
|
||||
|
||||
fun getRoom(user: User, token: String) {
|
||||
_getRoomViewState.value = GetRoomStartState
|
||||
repository.getRoom(user, token)
|
||||
.subscribeOn(Schedulers.io())
|
||||
?.observeOn(AndroidSchedulers.mainThread())
|
||||
?.subscribe(GetRoomObserver())
|
||||
}
|
||||
|
||||
fun joinRoom(user: User, token: String, roomPassword: String) {
|
||||
_getRoomViewState.value = JoinRoomStartState
|
||||
repository.joinRoom(user, token, roomPassword)
|
||||
.subscribeOn(Schedulers.io())
|
||||
?.observeOn(AndroidSchedulers.mainThread())
|
||||
?.retry(JOIN_ROOM_RETRY_COUNT)
|
||||
?.subscribe(JoinRoomObserver())
|
||||
}
|
||||
|
||||
inner class GetRoomObserver : Observer<ConversationModel> {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
// unused atm
|
||||
}
|
||||
|
||||
override fun onNext(conversationModel: ConversationModel) {
|
||||
_getRoomViewState.value = GetRoomSuccessState(conversationModel)
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
Log.e(TAG, "Error when fetching room")
|
||||
_getRoomViewState.value = GetRoomErrorState
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
// unused atm
|
||||
}
|
||||
}
|
||||
|
||||
inner class JoinRoomObserver : Observer<ConversationModel> {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
// unused atm
|
||||
}
|
||||
|
||||
override fun onNext(conversationModel: ConversationModel) {
|
||||
_joinRoomViewState.value = JoinRoomSuccessState(conversationModel)
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
Log.e(TAG, "Error when fetching room")
|
||||
_joinRoomViewState.value = JoinRoomErrorState
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
// unused atm
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG = ChatViewModel::class.simpleName
|
||||
const val JOIN_ROOM_RETRY_COUNT: Long = 3
|
||||
}
|
||||
}
|
|
@ -338,32 +338,9 @@ class ContactsActivity :
|
|||
|
||||
override fun onNext(roomOverall: RoomOverall) {
|
||||
val bundle = Bundle()
|
||||
bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, currentUser)
|
||||
bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomOverall.ocs!!.data!!.token)
|
||||
bundle.putString(BundleKeys.KEY_ROOM_ID, roomOverall.ocs!!.data!!.roomId)
|
||||
|
||||
// FIXME once APIv2 or later is used only, the createRoom already returns all the data
|
||||
ncApi.getRoom(
|
||||
credentials,
|
||||
ApiUtils.getUrlForRoom(
|
||||
apiVersion,
|
||||
currentUser!!.baseUrl,
|
||||
roomOverall.ocs!!.data!!.token
|
||||
)
|
||||
)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : Observer<RoomOverall> {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
// unused atm
|
||||
}
|
||||
|
||||
override fun onNext(roomOverall: RoomOverall) {
|
||||
bundle.putParcelable(
|
||||
BundleKeys.KEY_ACTIVE_CONVERSATION,
|
||||
Parcels.wrap(roomOverall.ocs!!.data!!)
|
||||
)
|
||||
|
||||
val chatIntent = Intent(context, ChatActivity::class.java)
|
||||
chatIntent.putExtras(bundle)
|
||||
chatIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
|
@ -380,16 +357,6 @@ class ContactsActivity :
|
|||
})
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
// unused atm
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
// unused atm
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun addParticipantsToConversation() {
|
||||
val userIdsArray: Array<String> = selectedUserIds.toTypedArray()
|
||||
val groupIdsArray: Array<String> = selectedGroupIds.toTypedArray()
|
||||
|
@ -818,13 +785,8 @@ class ContactsActivity :
|
|||
|
||||
override fun onNext(roomOverall: RoomOverall) {
|
||||
val bundle = Bundle()
|
||||
bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, currentUser)
|
||||
bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomOverall.ocs!!.data!!.token)
|
||||
bundle.putString(BundleKeys.KEY_ROOM_ID, roomOverall.ocs!!.data!!.roomId)
|
||||
bundle.putParcelable(
|
||||
BundleKeys.KEY_ACTIVE_CONVERSATION,
|
||||
Parcels.wrap(roomOverall.ocs!!.data!!)
|
||||
)
|
||||
|
||||
val chatIntent = Intent(context, ChatActivity::class.java)
|
||||
chatIntent.putExtras(bundle)
|
||||
|
|
|
@ -23,14 +23,13 @@
|
|||
*/
|
||||
package com.nextcloud.talk.controllers.bottomsheet
|
||||
|
||||
import android.content.Intent
|
||||
import android.content.res.ColorStateList
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import android.text.Editable
|
||||
import android.text.InputType
|
||||
import android.text.TextUtils
|
||||
import android.text.TextWatcher
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
|
@ -40,21 +39,28 @@ import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler
|
|||
import com.google.android.material.textfield.TextInputLayout
|
||||
import com.nextcloud.android.common.ui.theme.utils.ColorRole
|
||||
import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.api.NcApi
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
|
||||
import com.nextcloud.talk.controllers.base.BaseController
|
||||
import com.nextcloud.talk.controllers.util.viewBinding
|
||||
import com.nextcloud.talk.data.user.model.User
|
||||
import com.nextcloud.talk.databinding.ControllerEntryMenuBinding
|
||||
import com.nextcloud.talk.models.json.conversations.Conversation
|
||||
import com.nextcloud.talk.models.json.conversations.RoomOverall
|
||||
import com.nextcloud.talk.users.UserManager
|
||||
import com.nextcloud.talk.utils.ApiUtils
|
||||
import com.nextcloud.talk.utils.UriUtils
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN
|
||||
import com.nextcloud.talk.utils.singletons.ApplicationWideMessageHolder
|
||||
import com.vanniktech.emoji.EmojiPopup
|
||||
import io.reactivex.Observer
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import okhttp3.internal.immutableListOf
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.parceler.Parcels
|
||||
import org.parceler.Parcels.unwrap
|
||||
import javax.inject.Inject
|
||||
|
||||
@AutoInjector(NextcloudTalkApplication::class)
|
||||
|
@ -65,6 +71,9 @@ class EntryMenuController(args: Bundle) :
|
|||
) {
|
||||
private val binding: ControllerEntryMenuBinding? by viewBinding(ControllerEntryMenuBinding::bind)
|
||||
|
||||
@Inject
|
||||
lateinit var ncApi: NcApi
|
||||
|
||||
@Inject
|
||||
lateinit var eventBus: EventBus
|
||||
|
||||
|
@ -73,12 +82,13 @@ class EntryMenuController(args: Bundle) :
|
|||
|
||||
private val operation: ConversationOperationEnum
|
||||
private var conversation: Conversation? = null
|
||||
private var shareIntent: Intent? = null
|
||||
private val packageName: String
|
||||
private val name: String
|
||||
private val callUrl: String
|
||||
|
||||
private var emojiPopup: EmojiPopup? = null
|
||||
private val originalBundle: Bundle
|
||||
private var currentUser: User? = null
|
||||
private val roomToken: String
|
||||
|
||||
override val appBarLayoutType: AppBarLayoutType
|
||||
get() = AppBarLayoutType.SEARCH_BAR
|
||||
|
@ -100,6 +110,41 @@ class EntryMenuController(args: Bundle) :
|
|||
override fun onViewBound(view: View) {
|
||||
super.onViewBound(view)
|
||||
|
||||
currentUser = userManager.currentUser.blockingGet()
|
||||
|
||||
if (operation == ConversationOperationEnum.OPS_CODE_GET_AND_JOIN_ROOM) {
|
||||
var labelText = ""
|
||||
labelText = resources!!.getString(R.string.nc_conversation_link)
|
||||
binding?.textEdit?.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_URI
|
||||
|
||||
textEditAddChangedListener()
|
||||
|
||||
binding?.textInputLayout?.let { viewThemeUtils.material.colorTextInputLayout(it) }
|
||||
binding?.okButton?.let { viewThemeUtils.material.colorMaterialButtonText(it) }
|
||||
|
||||
binding?.textInputLayout?.hint = labelText
|
||||
binding?.textInputLayout?.requestFocus()
|
||||
|
||||
binding?.smileyButton?.setOnClickListener { onSmileyClick() }
|
||||
binding?.okButton?.setOnClickListener { onOkButtonClick() }
|
||||
} else {
|
||||
val apiVersion = ApiUtils.getConversationApiVersion(currentUser, intArrayOf(ApiUtils.APIv4, ApiUtils.APIv1))
|
||||
ncApi.getRoom(
|
||||
ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token),
|
||||
ApiUtils.getUrlForRoom(apiVersion, currentUser!!.baseUrl, roomToken)
|
||||
)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.retry(1)
|
||||
.subscribe(object : Observer<RoomOverall> {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
// unused atm
|
||||
}
|
||||
|
||||
@Suppress("Detekt.LongMethod")
|
||||
override fun onNext(roomOverall: RoomOverall) {
|
||||
conversation = roomOverall.ocs!!.data
|
||||
|
||||
if (conversation != null && operation === ConversationOperationEnum.OPS_CODE_RENAME_ROOM) {
|
||||
binding?.textEdit?.setText(conversation!!.name)
|
||||
}
|
||||
|
@ -117,7 +162,8 @@ class EntryMenuController(args: Bundle) :
|
|||
|
||||
var labelText = ""
|
||||
when (operation) {
|
||||
ConversationOperationEnum.OPS_CODE_INVITE_USERS, ConversationOperationEnum.OPS_CODE_RENAME_ROOM -> {
|
||||
ConversationOperationEnum.OPS_CODE_INVITE_USERS,
|
||||
ConversationOperationEnum.OPS_CODE_RENAME_ROOM -> {
|
||||
labelText = resources!!.getString(R.string.nc_call_name)
|
||||
binding?.textEdit?.inputType = InputType.TYPE_CLASS_TEXT
|
||||
binding?.smileyButton?.visibility = View.VISIBLE
|
||||
|
@ -130,7 +176,11 @@ class EntryMenuController(args: Bundle) :
|
|||
},
|
||||
onEmojiPopupDismissListener = {
|
||||
it.smileyButton.imageTintList = ColorStateList.valueOf(
|
||||
ResourcesCompat.getColor(resources!!, R.color.medium_emphasis_text, context.theme)
|
||||
ResourcesCompat.getColor(
|
||||
resources!!,
|
||||
R.color.medium_emphasis_text,
|
||||
context.theme
|
||||
)
|
||||
)
|
||||
},
|
||||
onEmojiClickListener = {
|
||||
|
@ -143,12 +193,8 @@ class EntryMenuController(args: Bundle) :
|
|||
ConversationOperationEnum.OPS_CODE_JOIN_ROOM -> {
|
||||
// 99 is joining a conversation via password
|
||||
labelText = resources!!.getString(R.string.nc_password)
|
||||
binding?.textEdit?.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD
|
||||
}
|
||||
|
||||
ConversationOperationEnum.OPS_CODE_GET_AND_JOIN_ROOM -> {
|
||||
labelText = resources!!.getString(R.string.nc_conversation_link)
|
||||
binding?.textEdit?.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_URI
|
||||
binding?.textEdit?.inputType =
|
||||
InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD
|
||||
}
|
||||
|
||||
else -> {
|
||||
|
@ -170,6 +216,17 @@ class EntryMenuController(args: Bundle) :
|
|||
binding?.okButton?.setOnClickListener { onOkButtonClick() }
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
Log.e("EntryMenuController", "error")
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
// unused atm
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun textEditAddChangedListener() {
|
||||
binding?.textEdit?.addTextChangedListener(object : TextWatcher {
|
||||
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
|
||||
|
@ -242,7 +299,8 @@ class EntryMenuController(args: Bundle) :
|
|||
) {
|
||||
val bundle = Bundle()
|
||||
conversation!!.name = binding?.textEdit?.text.toString()
|
||||
bundle.putParcelable(BundleKeys.KEY_ROOM, Parcels.wrap<Any>(conversation))
|
||||
bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomToken)
|
||||
bundle.putString(BundleKeys.KEY_NEW_ROOM_NAME, binding?.textEdit?.text.toString())
|
||||
bundle.putSerializable(BundleKeys.KEY_OPERATION_CODE, operation)
|
||||
router.pushController(
|
||||
RouterTransaction.with(OperationsMenuController(bundle))
|
||||
|
@ -274,16 +332,9 @@ class EntryMenuController(args: Bundle) :
|
|||
|
||||
private fun joinRoom() {
|
||||
val bundle = Bundle()
|
||||
bundle.putParcelable(BundleKeys.KEY_ROOM, Parcels.wrap<Any>(conversation))
|
||||
bundle.putString(BundleKeys.KEY_CALL_URL, callUrl)
|
||||
bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomToken)
|
||||
bundle.putString(BundleKeys.KEY_CONVERSATION_PASSWORD, binding?.textEdit?.text.toString())
|
||||
bundle.putSerializable(BundleKeys.KEY_OPERATION_CODE, operation)
|
||||
if (originalBundle.containsKey(BundleKeys.KEY_SERVER_CAPABILITIES)) {
|
||||
bundle.putParcelable(
|
||||
BundleKeys.KEY_SERVER_CAPABILITIES,
|
||||
originalBundle.getParcelable(BundleKeys.KEY_SERVER_CAPABILITIES)
|
||||
)
|
||||
}
|
||||
router.pushController(
|
||||
RouterTransaction.with(OperationsMenuController(bundle))
|
||||
.pushChangeHandler(HorizontalChangeHandler())
|
||||
|
@ -296,15 +347,10 @@ class EntryMenuController(args: Bundle) :
|
|||
|
||||
originalBundle = args
|
||||
operation = args.getSerializable(BundleKeys.KEY_OPERATION_CODE) as ConversationOperationEnum
|
||||
if (args.containsKey(BundleKeys.KEY_ROOM)) {
|
||||
conversation = unwrap<Conversation>(args.getParcelable<Parcelable>(BundleKeys.KEY_ROOM))
|
||||
}
|
||||
if (args.containsKey(BundleKeys.KEY_SHARE_INTENT)) {
|
||||
shareIntent = unwrap<Intent>(args.getParcelable<Parcelable>(BundleKeys.KEY_SHARE_INTENT))
|
||||
}
|
||||
roomToken = args.getString(KEY_ROOM_TOKEN, "")
|
||||
name = args.getString(BundleKeys.KEY_APP_ITEM_NAME, "")
|
||||
packageName = args.getString(BundleKeys.KEY_APP_ITEM_PACKAGE_NAME, "")
|
||||
callUrl = args.getString(BundleKeys.KEY_CALL_URL, "")
|
||||
// callUrl = args.getString(BundleKeys.KEY_CALL_URL, "")
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -21,8 +21,6 @@
|
|||
*/
|
||||
package com.nextcloud.talk.controllers.bottomsheet
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.text.TextUtils
|
||||
import android.util.Log
|
||||
|
@ -42,7 +40,6 @@ import com.nextcloud.talk.databinding.ControllerOperationsMenuBinding
|
|||
import com.nextcloud.talk.events.ConversationsListFetchDataEvent
|
||||
import com.nextcloud.talk.events.OpenConversationEvent
|
||||
import com.nextcloud.talk.models.RetrofitBucket
|
||||
import com.nextcloud.talk.models.json.capabilities.Capabilities
|
||||
import com.nextcloud.talk.models.json.capabilities.CapabilitiesOverall
|
||||
import com.nextcloud.talk.models.json.conversations.Conversation
|
||||
import com.nextcloud.talk.models.json.conversations.Conversation.ConversationType
|
||||
|
@ -53,19 +50,17 @@ import com.nextcloud.talk.users.UserManager
|
|||
import com.nextcloud.talk.utils.ApiUtils
|
||||
import com.nextcloud.talk.utils.DisplayUtils
|
||||
import com.nextcloud.talk.utils.NoSupportedApiException
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ACTIVE_CONVERSATION
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CALL_URL
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CONVERSATION_NAME
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CONVERSATION_PASSWORD
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CONVERSATION_TYPE
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_INVITED_GROUP
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_INVITED_PARTICIPANTS
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_NEW_ROOM_NAME
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_OPERATION_CODE
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_ID
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_SERVER_CAPABILITIES
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_USER_ENTITY
|
||||
import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew
|
||||
import com.nextcloud.talk.utils.singletons.ApplicationWideMessageHolder
|
||||
import io.reactivex.Observer
|
||||
|
@ -100,13 +95,14 @@ class OperationsMenuController(args: Bundle) : BaseController(
|
|||
private var currentUser: User? = null
|
||||
private val callPassword: String
|
||||
private val callUrl: String
|
||||
private var roomToken: String
|
||||
private val roomNameNew: String
|
||||
private var baseUrl: String? = null
|
||||
private var conversationToken: String? = null
|
||||
private var disposable: Disposable? = null
|
||||
private var conversationType: ConversationType? = null
|
||||
private var invitedUsers: ArrayList<String>? = ArrayList()
|
||||
private var invitedGroups: ArrayList<String>? = ArrayList()
|
||||
private var serverCapabilities: Capabilities? = null
|
||||
private var credentials: String? = null
|
||||
private val conversationName: String
|
||||
|
||||
|
@ -128,39 +124,41 @@ class OperationsMenuController(args: Bundle) : BaseController(
|
|||
baseUrl = callUrl.substring(0, callUrl.indexOf("/call"))
|
||||
}
|
||||
}
|
||||
|
||||
if (roomToken.isNotEmpty()) {
|
||||
val apiVersion = apiVersion()
|
||||
ncApi.getRoom(
|
||||
credentials,
|
||||
ApiUtils.getUrlForRoom(apiVersion, currentUser!!.baseUrl, roomToken)
|
||||
)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.retry(1)
|
||||
.subscribe(object : Observer<RoomOverall> {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
disposable = d
|
||||
}
|
||||
|
||||
override fun onNext(roomOverall: RoomOverall) {
|
||||
conversation = roomOverall.ocs!!.data
|
||||
|
||||
if (!TextUtils.isEmpty(baseUrl) && baseUrl != currentUser!!.baseUrl) {
|
||||
if (serverCapabilities != null) {
|
||||
try {
|
||||
useBundledCapabilitiesForGuest()
|
||||
} catch (e: IOException) {
|
||||
// Fall back to fetching capabilities again
|
||||
fetchCapabilitiesForGuest()
|
||||
}
|
||||
} else {
|
||||
fetchCapabilitiesForGuest()
|
||||
}
|
||||
} else {
|
||||
processOperation()
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
private fun useBundledCapabilitiesForGuest() {
|
||||
currentUser = User()
|
||||
currentUser!!.baseUrl = baseUrl
|
||||
currentUser!!.userId = "?"
|
||||
try {
|
||||
currentUser!!.capabilities = serverCapabilities
|
||||
} catch (e: IOException) {
|
||||
Log.e("OperationsMenu", "Failed to serialize capabilities")
|
||||
throw e
|
||||
override fun onError(e: Throwable) {
|
||||
Log.e(TAG, "error while fetching room", e)
|
||||
}
|
||||
try {
|
||||
checkCapabilities(currentUser!!)
|
||||
|
||||
override fun onComplete() {
|
||||
// unused atm
|
||||
}
|
||||
})
|
||||
} else {
|
||||
processOperation()
|
||||
} catch (e: NoSupportedApiException) {
|
||||
showResultImage(everythingOK = false, isGuestSupportError = false)
|
||||
Log.d(TAG, "No supported server version found", e)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -218,6 +216,7 @@ class OperationsMenuController(args: Bundle) : BaseController(
|
|||
ConversationOperationEnum.OPS_CODE_MARK_AS_UNREAD -> operationMarkAsUnread()
|
||||
ConversationOperationEnum.OPS_CODE_REMOVE_FAVORITE,
|
||||
ConversationOperationEnum.OPS_CODE_ADD_FAVORITE -> operationToggleFavorite()
|
||||
|
||||
ConversationOperationEnum.OPS_CODE_JOIN_ROOM -> operationJoinRoom()
|
||||
else -> {
|
||||
}
|
||||
|
@ -287,7 +286,7 @@ class OperationsMenuController(args: Bundle) : BaseController(
|
|||
currentUser!!.baseUrl,
|
||||
conversation!!.token
|
||||
),
|
||||
conversation!!.name
|
||||
roomNameNew
|
||||
)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
|
@ -427,17 +426,7 @@ class OperationsMenuController(args: Bundle) : BaseController(
|
|||
if (conversation!!.hasPassword && conversation!!.isGuest) {
|
||||
eventBus.post(ConversationsListFetchDataEvent())
|
||||
val bundle = Bundle()
|
||||
bundle.putParcelable(KEY_ROOM, Parcels.wrap(conversation))
|
||||
bundle.putString(KEY_CALL_URL, callUrl)
|
||||
try {
|
||||
bundle.putParcelable(
|
||||
KEY_SERVER_CAPABILITIES,
|
||||
Parcels.wrap<Capabilities>(currentUser!!.capabilities)
|
||||
)
|
||||
} catch (e: IOException) {
|
||||
Log.e(TAG, "Failed to parse capabilities for guest")
|
||||
showResultImage(everythingOK = false, isGuestSupportError = false)
|
||||
}
|
||||
bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomToken)
|
||||
bundle.putSerializable(KEY_OPERATION_CODE, ConversationOperationEnum.OPS_CODE_JOIN_ROOM)
|
||||
router.pushController(
|
||||
RouterTransaction.with(EntryMenuController(bundle))
|
||||
|
@ -519,16 +508,7 @@ class OperationsMenuController(args: Bundle) : BaseController(
|
|||
binding?.resultTextView?.setText(R.string.nc_all_ok_operation)
|
||||
} else {
|
||||
binding?.resultTextView?.setTextColor(resources!!.getColor(R.color.nc_darkRed, null))
|
||||
if (!isGuestSupportError) {
|
||||
binding?.resultTextView?.setText(R.string.nc_failed_to_perform_operation)
|
||||
} else {
|
||||
binding?.resultTextView?.setText(R.string.nc_failed_signaling_settings)
|
||||
binding?.webButton?.setOnClickListener {
|
||||
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(callUrl))
|
||||
startActivity(browserIntent)
|
||||
}
|
||||
binding?.webButton?.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
binding?.resultTextView?.visibility = View.VISIBLE
|
||||
if (everythingOK) {
|
||||
|
@ -608,6 +588,7 @@ class OperationsMenuController(args: Bundle) : BaseController(
|
|||
override fun onSubscribe(d: Disposable) {
|
||||
// unused atm
|
||||
}
|
||||
|
||||
override fun onNext(addParticipantOverall: AddParticipantOverall) {
|
||||
// unused atm
|
||||
}
|
||||
|
@ -653,6 +634,7 @@ class OperationsMenuController(args: Bundle) : BaseController(
|
|||
override fun onSubscribe(d: Disposable) {
|
||||
// unused atm
|
||||
}
|
||||
|
||||
override fun onNext(addParticipantOverall: AddParticipantOverall) {
|
||||
// unused atm
|
||||
}
|
||||
|
@ -679,8 +661,6 @@ class OperationsMenuController(args: Bundle) : BaseController(
|
|||
bundle.putString(KEY_ROOM_TOKEN, conversation!!.token)
|
||||
bundle.putString(KEY_ROOM_ID, conversation!!.roomId)
|
||||
bundle.putString(KEY_CONVERSATION_NAME, conversation!!.displayName)
|
||||
bundle.putParcelable(KEY_USER_ENTITY, currentUser)
|
||||
bundle.putParcelable(KEY_ACTIVE_CONVERSATION, Parcels.wrap(conversation))
|
||||
bundle.putString(KEY_CONVERSATION_PASSWORD, callPassword)
|
||||
eventBus.post(OpenConversationEvent(conversation, bundle))
|
||||
}
|
||||
|
@ -755,11 +735,10 @@ class OperationsMenuController(args: Bundle) : BaseController(
|
|||
|
||||
init {
|
||||
operation = args.getSerializable(KEY_OPERATION_CODE) as ConversationOperationEnum?
|
||||
if (args.containsKey(KEY_ROOM)) {
|
||||
conversation = Parcels.unwrap(args.getParcelable(KEY_ROOM))
|
||||
}
|
||||
callPassword = args.getString(KEY_CONVERSATION_PASSWORD, "")
|
||||
callUrl = args.getString(KEY_CALL_URL, "")
|
||||
roomToken = args.getString(KEY_ROOM_TOKEN, "")
|
||||
roomNameNew = args.getString(KEY_NEW_ROOM_NAME, "")
|
||||
if (args.containsKey(KEY_INVITED_PARTICIPANTS)) {
|
||||
invitedUsers = args.getStringArrayList(KEY_INVITED_PARTICIPANTS)
|
||||
}
|
||||
|
@ -769,9 +748,6 @@ class OperationsMenuController(args: Bundle) : BaseController(
|
|||
if (args.containsKey(KEY_CONVERSATION_TYPE)) {
|
||||
conversationType = Parcels.unwrap(args.getParcelable(KEY_CONVERSATION_TYPE))
|
||||
}
|
||||
if (args.containsKey(KEY_SERVER_CAPABILITIES)) {
|
||||
serverCapabilities = Parcels.unwrap(args.getParcelable(KEY_SERVER_CAPABILITIES))
|
||||
}
|
||||
conversationName = args.getString(KEY_CONVERSATION_NAME, "")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,6 @@ import android.annotation.SuppressLint
|
|||
import android.content.Intent
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import android.text.TextUtils
|
||||
import android.util.Log
|
||||
import android.view.Menu
|
||||
|
@ -86,6 +85,7 @@ import com.nextcloud.talk.utils.DateConstants
|
|||
import com.nextcloud.talk.utils.DateUtils
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys
|
||||
import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew
|
||||
import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew
|
||||
import com.nextcloud.talk.utils.preferences.preferencestorage.DatabaseStorageModule
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
|
||||
|
@ -95,7 +95,6 @@ import io.reactivex.disposables.Disposable
|
|||
import io.reactivex.schedulers.Schedulers
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
import org.parceler.Parcels
|
||||
import java.util.Calendar
|
||||
import java.util.Collections
|
||||
import java.util.Locale
|
||||
|
@ -110,6 +109,9 @@ class ConversationInfoActivity :
|
|||
@Inject
|
||||
lateinit var ncApi: NcApi
|
||||
|
||||
@Inject
|
||||
lateinit var currentUserProvider: CurrentUserProviderNew
|
||||
|
||||
@Inject
|
||||
lateinit var conversationsRepository: ConversationsRepository
|
||||
|
||||
|
@ -152,7 +154,8 @@ class ConversationInfoActivity :
|
|||
setContentView(binding.root)
|
||||
setupSystemColors()
|
||||
|
||||
conversationUser = intent.getParcelableExtra(BundleKeys.KEY_USER_ENTITY)!!
|
||||
conversationUser = currentUserProvider.currentUser.blockingGet()
|
||||
|
||||
conversationToken = intent.getStringExtra(BundleKeys.KEY_ROOM_TOKEN)!!
|
||||
hasAvatarSpacing = intent.getBooleanExtra(BundleKeys.KEY_ROOM_ONE_TO_ONE, false)
|
||||
credentials = ApiUtils.getCredentials(conversationUser.username, conversationUser.token)
|
||||
|
@ -225,11 +228,6 @@ class ConversationInfoActivity :
|
|||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
if (item.itemId == R.id.edit) {
|
||||
val bundle = Bundle()
|
||||
bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, conversationUser)
|
||||
bundle.putParcelable(
|
||||
BundleKeys.KEY_ACTIVE_CONVERSATION,
|
||||
Parcels.wrap(conversation)
|
||||
)
|
||||
bundle.putString(BundleKeys.KEY_ROOM_TOKEN, conversationToken)
|
||||
|
||||
val intent = Intent(this, ConversationInfoEditActivity::class.java)
|
||||
|
@ -270,7 +268,6 @@ class ConversationInfoActivity :
|
|||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
intent.putExtra(BundleKeys.KEY_CONVERSATION_NAME, conversation?.displayName)
|
||||
intent.putExtra(BundleKeys.KEY_ROOM_TOKEN, conversationToken)
|
||||
intent.putExtra(BundleKeys.KEY_USER_ENTITY, conversationUser as Parcelable)
|
||||
intent.putExtra(SharedItemsActivity.KEY_USER_IS_OWNER_OR_MODERATOR, conversation?.isParticipantOwnerOrModerator)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
|
|
@ -34,34 +34,31 @@ import android.view.View
|
|||
import android.widget.Toast
|
||||
import androidx.core.net.toFile
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import autodagger.AutoInjector
|
||||
import com.github.dhaval2404.imagepicker.ImagePicker
|
||||
import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.activities.BaseActivity
|
||||
import com.nextcloud.talk.api.NcApi
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||
import com.nextcloud.talk.conversationinfoedit.viewmodel.ConversationInfoEditViewModel
|
||||
import com.nextcloud.talk.data.user.model.User
|
||||
import com.nextcloud.talk.databinding.ActivityConversationInfoEditBinding
|
||||
import com.nextcloud.talk.extensions.loadConversationAvatar
|
||||
import com.nextcloud.talk.extensions.loadSystemAvatar
|
||||
import com.nextcloud.talk.extensions.loadUserAvatar
|
||||
import com.nextcloud.talk.models.json.conversations.Conversation
|
||||
import com.nextcloud.talk.models.json.conversations.RoomOverall
|
||||
import com.nextcloud.talk.models.domain.ConversationModel
|
||||
import com.nextcloud.talk.models.domain.ConversationType
|
||||
import com.nextcloud.talk.models.json.generic.GenericOverall
|
||||
import com.nextcloud.talk.repositories.conversations.ConversationsRepository
|
||||
import com.nextcloud.talk.utils.ApiUtils
|
||||
import com.nextcloud.talk.utils.Mimetype
|
||||
import com.nextcloud.talk.utils.PickImage
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys
|
||||
import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew
|
||||
import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew
|
||||
import io.reactivex.Observer
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||
import okhttp3.MultipartBody
|
||||
import okhttp3.RequestBody.Companion.asRequestBody
|
||||
import org.parceler.Parcels
|
||||
import java.io.File
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -75,13 +72,18 @@ class ConversationInfoEditActivity :
|
|||
lateinit var ncApi: NcApi
|
||||
|
||||
@Inject
|
||||
lateinit var conversationsRepository: ConversationsRepository
|
||||
lateinit var currentUserProvider: CurrentUserProviderNew
|
||||
|
||||
private lateinit var conversationToken: String
|
||||
@Inject
|
||||
lateinit var viewModelFactory: ViewModelProvider.Factory
|
||||
|
||||
lateinit var conversationInfoEditViewModel: ConversationInfoEditViewModel
|
||||
|
||||
private lateinit var roomToken: String
|
||||
private lateinit var conversationUser: User
|
||||
private lateinit var credentials: String
|
||||
|
||||
private var conversation: Conversation? = null
|
||||
private var conversation: ConversationModel? = null
|
||||
|
||||
private lateinit var pickImage: PickImage
|
||||
|
||||
|
@ -96,12 +98,14 @@ class ConversationInfoEditActivity :
|
|||
|
||||
val extras: Bundle? = intent.extras
|
||||
|
||||
conversationUser = extras?.getParcelable(BundleKeys.KEY_USER_ENTITY)!!
|
||||
conversationToken = extras.getString(BundleKeys.KEY_ROOM_TOKEN)!!
|
||||
conversationUser = currentUserProvider.currentUser.blockingGet()
|
||||
|
||||
if (conversation == null && intent.hasExtra(BundleKeys.KEY_ACTIVE_CONVERSATION)) {
|
||||
conversation = Parcels.unwrap<Conversation>(extras.getParcelable(BundleKeys.KEY_ACTIVE_CONVERSATION))
|
||||
}
|
||||
roomToken = extras?.getString(BundleKeys.KEY_ROOM_TOKEN)!!
|
||||
|
||||
conversationInfoEditViewModel =
|
||||
ViewModelProvider(this, viewModelFactory)[ConversationInfoEditViewModel::class.java]
|
||||
|
||||
conversationInfoEditViewModel.getRoom(conversationUser, roomToken)
|
||||
|
||||
viewThemeUtils.material.colorTextInputLayout(binding.conversationNameInputLayout)
|
||||
viewThemeUtils.material.colorTextInputLayout(binding.conversationDescriptionInputLayout)
|
||||
|
@ -109,12 +113,19 @@ class ConversationInfoEditActivity :
|
|||
credentials = ApiUtils.getCredentials(conversationUser.username, conversationUser.token)
|
||||
|
||||
pickImage = PickImage(this, conversationUser)
|
||||
|
||||
initObservers()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
}
|
||||
|
||||
loadConversationAvatar()
|
||||
private fun initObservers() {
|
||||
conversationInfoEditViewModel.viewState.observe(this) { state ->
|
||||
when (state) {
|
||||
is ConversationInfoEditViewModel.GetRoomSuccessState -> {
|
||||
conversation = state.conversationModel
|
||||
|
||||
binding.conversationName.setText(conversation!!.displayName)
|
||||
|
||||
|
@ -125,6 +136,35 @@ class ConversationInfoEditActivity :
|
|||
if (!CapabilitiesUtilNew.isConversationDescriptionEndpointAvailable(conversationUser)) {
|
||||
binding.conversationDescription.isEnabled = false
|
||||
}
|
||||
|
||||
loadConversationAvatar()
|
||||
}
|
||||
|
||||
is ConversationInfoEditViewModel.GetRoomErrorState -> {
|
||||
Toast.makeText(context, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
is ConversationInfoEditViewModel.UploadAvatarSuccessState -> {
|
||||
conversation = state.conversationModel
|
||||
loadConversationAvatar()
|
||||
}
|
||||
|
||||
is ConversationInfoEditViewModel.UploadAvatarErrorState -> {
|
||||
Toast.makeText(context, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
is ConversationInfoEditViewModel.DeleteAvatarSuccessState -> {
|
||||
conversation = state.conversationModel
|
||||
loadConversationAvatar()
|
||||
}
|
||||
|
||||
is ConversationInfoEditViewModel.DeleteAvatarErrorState -> {
|
||||
Toast.makeText(context, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupAvatarOptions() {
|
||||
|
@ -284,98 +324,27 @@ class ConversationInfoEditActivity :
|
|||
}
|
||||
}
|
||||
|
||||
private fun uploadAvatar(file: File?) {
|
||||
val builder = MultipartBody.Builder()
|
||||
builder.setType(MultipartBody.FORM)
|
||||
builder.addFormDataPart(
|
||||
"file",
|
||||
file!!.name,
|
||||
file.asRequestBody(Mimetype.IMAGE_PREFIX_GENERIC.toMediaTypeOrNull())
|
||||
)
|
||||
val filePart: MultipartBody.Part = MultipartBody.Part.createFormData(
|
||||
"file",
|
||||
file.name,
|
||||
file.asRequestBody(Mimetype.IMAGE_JPG.toMediaTypeOrNull())
|
||||
)
|
||||
|
||||
// upload file
|
||||
ncApi.uploadConversationAvatar(
|
||||
credentials,
|
||||
ApiUtils.getUrlForConversationAvatar(1, conversationUser.baseUrl, conversation!!.token),
|
||||
filePart
|
||||
)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : Observer<RoomOverall> {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
// unused atm
|
||||
}
|
||||
|
||||
override fun onNext(roomOverall: RoomOverall) {
|
||||
conversation = roomOverall.ocs!!.data
|
||||
loadConversationAvatar()
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
Toast.makeText(
|
||||
applicationContext,
|
||||
context.getString(R.string.default_error_msg),
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
Log.e(TAG, "Error uploading avatar", e)
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
// unused atm
|
||||
}
|
||||
})
|
||||
private fun uploadAvatar(file: File) {
|
||||
conversationInfoEditViewModel.uploadConversationAvatar(conversationUser, file, roomToken)
|
||||
}
|
||||
|
||||
private fun deleteAvatar() {
|
||||
ncApi.deleteConversationAvatar(
|
||||
credentials,
|
||||
ApiUtils.getUrlForConversationAvatar(1, conversationUser.baseUrl, conversationToken)
|
||||
)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : Observer<RoomOverall> {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
// unused atm
|
||||
}
|
||||
|
||||
override fun onNext(roomOverall: RoomOverall) {
|
||||
conversation = roomOverall.ocs!!.data
|
||||
loadConversationAvatar()
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
Toast.makeText(
|
||||
applicationContext,
|
||||
context.getString(R.string.default_error_msg),
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
Log.e(TAG, "Failed to delete avatar", e)
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
// unused atm
|
||||
}
|
||||
})
|
||||
conversationInfoEditViewModel.deleteConversationAvatar(conversationUser, roomToken)
|
||||
}
|
||||
|
||||
private fun loadConversationAvatar() {
|
||||
setupAvatarOptions()
|
||||
|
||||
when (conversation!!.type) {
|
||||
Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL -> if (!TextUtils.isEmpty(conversation!!.name)) {
|
||||
ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL -> if (!TextUtils.isEmpty(conversation!!.name)) {
|
||||
conversation!!.name?.let { binding.avatarImage.loadUserAvatar(conversationUser, it, true, false) }
|
||||
}
|
||||
|
||||
Conversation.ConversationType.ROOM_GROUP_CALL, Conversation.ConversationType.ROOM_PUBLIC_CALL -> {
|
||||
ConversationType.ROOM_GROUP_CALL, ConversationType.ROOM_PUBLIC_CALL -> {
|
||||
binding.avatarImage.loadConversationAvatar(conversationUser, conversation!!, false, viewThemeUtils)
|
||||
}
|
||||
|
||||
Conversation.ConversationType.ROOM_SYSTEM -> {
|
||||
ConversationType.ROOM_SYSTEM -> {
|
||||
binding.avatarImage.loadSystemAvatar()
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Marcel Hibbe
|
||||
* Copyright (C) 2023 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.conversationinfoedit.data
|
||||
|
||||
import com.nextcloud.talk.data.user.model.User
|
||||
import com.nextcloud.talk.models.domain.ConversationModel
|
||||
import io.reactivex.Observable
|
||||
import java.io.File
|
||||
|
||||
interface ConversationInfoEditRepository {
|
||||
|
||||
fun uploadConversationAvatar(user: User, file: File, roomToken: String): Observable<ConversationModel>
|
||||
|
||||
fun deleteConversationAvatar(user: User, roomToken: String): Observable<ConversationModel>
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Marcel Hibbe
|
||||
* Copyright (C) 2023 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.conversationinfoedit.data
|
||||
|
||||
import com.nextcloud.talk.api.NcApi
|
||||
import com.nextcloud.talk.data.user.model.User
|
||||
import com.nextcloud.talk.models.domain.ConversationModel
|
||||
import com.nextcloud.talk.utils.ApiUtils
|
||||
import com.nextcloud.talk.utils.Mimetype
|
||||
import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew
|
||||
import io.reactivex.Observable
|
||||
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||
import okhttp3.MultipartBody
|
||||
import okhttp3.RequestBody.Companion.asRequestBody
|
||||
import java.io.File
|
||||
|
||||
class ConversationInfoEditRepositoryImpl(private val ncApi: NcApi, currentUserProvider: CurrentUserProviderNew) :
|
||||
ConversationInfoEditRepository {
|
||||
|
||||
val currentUser: User = currentUserProvider.currentUser.blockingGet()
|
||||
val credentials: String = ApiUtils.getCredentials(currentUser.username, currentUser.token)
|
||||
|
||||
val apiVersion = ApiUtils.getConversationApiVersion(currentUser, intArrayOf(ApiUtils.APIv4, ApiUtils.APIv3, 1))
|
||||
|
||||
override fun uploadConversationAvatar(user: User, file: File, roomToken: String): Observable<ConversationModel> {
|
||||
val builder = MultipartBody.Builder()
|
||||
builder.setType(MultipartBody.FORM)
|
||||
builder.addFormDataPart(
|
||||
"file",
|
||||
file!!.name,
|
||||
file.asRequestBody(Mimetype.IMAGE_PREFIX_GENERIC.toMediaTypeOrNull())
|
||||
)
|
||||
val filePart: MultipartBody.Part = MultipartBody.Part.createFormData(
|
||||
"file",
|
||||
file.name,
|
||||
file.asRequestBody(Mimetype.IMAGE_JPG.toMediaTypeOrNull())
|
||||
)
|
||||
|
||||
return ncApi.uploadConversationAvatar(
|
||||
credentials,
|
||||
ApiUtils.getUrlForConversationAvatar(1, user.baseUrl, roomToken),
|
||||
filePart
|
||||
).map { ConversationModel.mapToConversationModel(it.ocs?.data!!) }
|
||||
}
|
||||
|
||||
override fun deleteConversationAvatar(user: User, roomToken: String): Observable<ConversationModel> {
|
||||
return ncApi.deleteConversationAvatar(
|
||||
credentials,
|
||||
ApiUtils.getUrlForConversationAvatar(1, user.baseUrl, roomToken)
|
||||
).map { ConversationModel.mapToConversationModel(it.ocs?.data!!) }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,141 @@
|
|||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Marcel Hibbe
|
||||
* Copyright (C) 2023 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.conversationinfoedit.viewmodel
|
||||
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.nextcloud.talk.chat.data.ChatRepository
|
||||
import com.nextcloud.talk.conversationinfoedit.data.ConversationInfoEditRepository
|
||||
import com.nextcloud.talk.data.user.model.User
|
||||
import com.nextcloud.talk.models.domain.ConversationModel
|
||||
import io.reactivex.Observer
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import java.io.File
|
||||
import javax.inject.Inject
|
||||
|
||||
class ConversationInfoEditViewModel @Inject constructor(
|
||||
private val repository: ChatRepository,
|
||||
private val conversationInfoEditRepository: ConversationInfoEditRepository
|
||||
) : ViewModel() {
|
||||
|
||||
sealed interface ViewState
|
||||
|
||||
object GetRoomStartState : ViewState
|
||||
object GetRoomErrorState : ViewState
|
||||
open class GetRoomSuccessState(val conversationModel: ConversationModel) : ViewState
|
||||
|
||||
object UploadAvatarErrorState : ViewState
|
||||
open class UploadAvatarSuccessState(val conversationModel: ConversationModel) : ViewState
|
||||
|
||||
object DeleteAvatarErrorState : ViewState
|
||||
open class DeleteAvatarSuccessState(val conversationModel: ConversationModel) : ViewState
|
||||
|
||||
private val _viewState: MutableLiveData<ViewState> = MutableLiveData(GetRoomStartState)
|
||||
val viewState: LiveData<ViewState>
|
||||
get() = _viewState
|
||||
|
||||
fun getRoom(user: User, token: String) {
|
||||
_viewState.value = GetRoomStartState
|
||||
repository.getRoom(user, token)
|
||||
.subscribeOn(Schedulers.io())
|
||||
?.observeOn(AndroidSchedulers.mainThread())
|
||||
?.subscribe(GetRoomObserver())
|
||||
}
|
||||
|
||||
fun uploadConversationAvatar(user: User, file: File, roomToken: String) {
|
||||
conversationInfoEditRepository.uploadConversationAvatar(user, file, roomToken)
|
||||
.subscribeOn(Schedulers.io())
|
||||
?.observeOn(AndroidSchedulers.mainThread())
|
||||
?.subscribe(UploadConversationAvatarObserver())
|
||||
}
|
||||
|
||||
fun deleteConversationAvatar(user: User, roomToken: String) {
|
||||
conversationInfoEditRepository.deleteConversationAvatar(user, roomToken)
|
||||
.subscribeOn(Schedulers.io())
|
||||
?.observeOn(AndroidSchedulers.mainThread())
|
||||
?.subscribe(DeleteConversationAvatarObserver())
|
||||
}
|
||||
|
||||
inner class GetRoomObserver : Observer<ConversationModel> {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
// unused atm
|
||||
}
|
||||
|
||||
override fun onNext(conversationModel: ConversationModel) {
|
||||
_viewState.value = GetRoomSuccessState(conversationModel)
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
Log.e(TAG, "Error when fetching room")
|
||||
_viewState.value = GetRoomErrorState
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
// unused atm
|
||||
}
|
||||
}
|
||||
|
||||
inner class UploadConversationAvatarObserver : Observer<ConversationModel> {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
// unused atm
|
||||
}
|
||||
|
||||
override fun onNext(conversationModel: ConversationModel) {
|
||||
_viewState.value = UploadAvatarSuccessState(conversationModel)
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
Log.e(TAG, "Error when uploading avatar")
|
||||
_viewState.value = UploadAvatarErrorState
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
// unused atm
|
||||
}
|
||||
}
|
||||
|
||||
inner class DeleteConversationAvatarObserver : Observer<ConversationModel> {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
// unused atm
|
||||
}
|
||||
|
||||
override fun onNext(conversationModel: ConversationModel) {
|
||||
_viewState.value = DeleteAvatarSuccessState(conversationModel)
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
Log.e(TAG, "Error when deleting avatar")
|
||||
_viewState.value = DeleteAvatarErrorState
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
// unused atm
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG = ConversationInfoEditViewModel::class.simpleName
|
||||
}
|
||||
}
|
|
@ -106,17 +106,14 @@ import com.nextcloud.talk.utils.FileUtils
|
|||
import com.nextcloud.talk.utils.Mimetype
|
||||
import com.nextcloud.talk.utils.ParticipantPermissions
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ACTIVE_CONVERSATION
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_FORWARD_HIDE_SOURCE_ROOM
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_FORWARD_MSG_FLAG
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_FORWARD_MSG_TEXT
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_INTERNAL_USER_ID
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_NEW_CONVERSATION
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_ID
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_SHARED_TEXT
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_USER_ENTITY
|
||||
import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew.hasSpreedFeatureCapability
|
||||
import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew.isServerEOL
|
||||
import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew.isUnifiedSearchAvailable
|
||||
|
@ -134,7 +131,6 @@ import io.reactivex.schedulers.Schedulers
|
|||
import org.apache.commons.lang3.builder.CompareToBuilder
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
import org.parceler.Parcels
|
||||
import retrofit2.HttpException
|
||||
import java.util.Objects
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
@ -1074,7 +1070,6 @@ class ConversationsListActivity :
|
|||
if (clickedItem != null) {
|
||||
val conversation = (clickedItem as ConversationItem).model
|
||||
conversationsListBottomDialog = ConversationsListBottomDialog(
|
||||
this,
|
||||
this,
|
||||
userManager.currentUser.blockingGet(),
|
||||
conversation
|
||||
|
@ -1185,8 +1180,6 @@ class ConversationsListActivity :
|
|||
}
|
||||
|
||||
val bundle = Bundle()
|
||||
bundle.putParcelable(KEY_USER_ENTITY, currentUser)
|
||||
bundle.putParcelable(KEY_ACTIVE_CONVERSATION, Parcels.wrap(selectedConversation))
|
||||
bundle.putString(KEY_ROOM_TOKEN, selectedConversation!!.token)
|
||||
bundle.putString(KEY_ROOM_ID, selectedConversation!!.roomId)
|
||||
bundle.putString(KEY_SHARED_TEXT, textToPaste)
|
||||
|
@ -1229,8 +1222,6 @@ class ConversationsListActivity :
|
|||
if (conversationMenuBundle != null &&
|
||||
isInternalUserEqualsCurrentUser(currentUser, conversationMenuBundle)
|
||||
) {
|
||||
val conversation = Parcels.unwrap<Conversation>(conversationMenuBundle!!.getParcelable(KEY_ROOM))
|
||||
if (conversation != null) {
|
||||
binding?.floatingActionButton?.let {
|
||||
val dialogBuilder = MaterialAlertDialogBuilder(it.context)
|
||||
.setIcon(
|
||||
|
@ -1245,7 +1236,7 @@ class ConversationsListActivity :
|
|||
KEY_INTERNAL_USER_ID,
|
||||
conversationMenuBundle!!.getLong(KEY_INTERNAL_USER_ID)
|
||||
)
|
||||
data.putString(KEY_ROOM_TOKEN, conversation.token)
|
||||
data.putString(KEY_ROOM_TOKEN, bundle.getString(KEY_ROOM_TOKEN))
|
||||
conversationMenuBundle = null
|
||||
deleteConversation(data.build())
|
||||
}
|
||||
|
@ -1263,7 +1254,6 @@ class ConversationsListActivity :
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun isInternalUserEqualsCurrentUser(currentUser: User?, conversationMenuBundle: Bundle?): Boolean {
|
||||
return currentUser != null && conversationMenuBundle!!.getLong(KEY_INTERNAL_USER_ID) == currentUser.id
|
||||
|
|
|
@ -26,6 +26,10 @@
|
|||
package com.nextcloud.talk.dagger.modules
|
||||
|
||||
import com.nextcloud.talk.api.NcApi
|
||||
import com.nextcloud.talk.chat.data.ChatRepository
|
||||
import com.nextcloud.talk.chat.data.ChatRepositoryImpl
|
||||
import com.nextcloud.talk.conversationinfoedit.data.ConversationInfoEditRepository
|
||||
import com.nextcloud.talk.conversationinfoedit.data.ConversationInfoEditRepositoryImpl
|
||||
import com.nextcloud.talk.data.source.local.TalkDatabase
|
||||
import com.nextcloud.talk.data.storage.ArbitraryStoragesRepository
|
||||
import com.nextcloud.talk.data.storage.ArbitraryStoragesRepositoryImpl
|
||||
|
@ -123,4 +127,16 @@ class RepositoryModule {
|
|||
TranslateRepository {
|
||||
return TranslateRepositoryImpl(ncApi)
|
||||
}
|
||||
|
||||
@Provides
|
||||
fun provideChatRepository(ncApi: NcApi):
|
||||
ChatRepository {
|
||||
return ChatRepositoryImpl(ncApi)
|
||||
}
|
||||
|
||||
@Provides
|
||||
fun provideConversationInfoEditRepository(ncApi: NcApi, userProvider: CurrentUserProviderNew):
|
||||
ConversationInfoEditRepository {
|
||||
return ConversationInfoEditRepositoryImpl(ncApi, userProvider)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,9 @@ package com.nextcloud.talk.dagger.modules
|
|||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.nextcloud.talk.callnotification.viewmodel.CallNotificationViewModel
|
||||
import com.nextcloud.talk.chat.viewmodels.ChatViewModel
|
||||
import com.nextcloud.talk.conversationinfoedit.viewmodel.ConversationInfoEditViewModel
|
||||
import com.nextcloud.talk.messagesearch.MessageSearchViewModel
|
||||
import com.nextcloud.talk.openconversations.viewmodels.OpenConversationsViewModel
|
||||
import com.nextcloud.talk.polls.viewmodels.PollCreateViewModel
|
||||
|
@ -112,5 +115,20 @@ abstract class ViewModelModule {
|
|||
@Binds
|
||||
@IntoMap
|
||||
@ViewModelKey(OpenConversationsViewModel::class)
|
||||
abstract fun openConversationsViewModelModel(viewModel: OpenConversationsViewModel): ViewModel
|
||||
abstract fun openConversationsViewModel(viewModel: OpenConversationsViewModel): ViewModel
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ViewModelKey(ChatViewModel::class)
|
||||
abstract fun chatViewModel(viewModel: ChatViewModel): ViewModel
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ViewModelKey(CallNotificationViewModel::class)
|
||||
abstract fun callNotificationViewModel(viewModel: CallNotificationViewModel): ViewModel
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ViewModelKey(ConversationInfoEditViewModel::class)
|
||||
abstract fun conversationInfoEditViewModel(viewModel: ConversationInfoEditViewModel): ViewModel
|
||||
}
|
||||
|
|
|
@ -94,6 +94,7 @@ abstract class TalkDatabase : RoomDatabase() {
|
|||
|
||||
return Room
|
||||
.databaseBuilder(context.applicationContext, TalkDatabase::class.java, dbName)
|
||||
// comment out openHelperFactory to view the database entries in Android Studio for debugging
|
||||
.openHelperFactory(factory)
|
||||
.addMigrations(Migrations.MIGRATION_6_8, Migrations.MIGRATION_7_8)
|
||||
.allowMainThreadQueries()
|
||||
|
|
|
@ -44,6 +44,8 @@ import coil.transform.RoundedCornersTransformation
|
|||
import com.amulyakhare.textdrawable.TextDrawable
|
||||
import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.data.user.model.User
|
||||
import com.nextcloud.talk.models.domain.ConversationModel
|
||||
import com.nextcloud.talk.models.domain.ConversationType
|
||||
import com.nextcloud.talk.models.json.conversations.Conversation
|
||||
import com.nextcloud.talk.ui.theme.ViewThemeUtils
|
||||
import com.nextcloud.talk.utils.ApiUtils
|
||||
|
@ -52,11 +54,26 @@ import com.nextcloud.talk.utils.DisplayUtils
|
|||
private const val ROUNDING_PIXEL = 16f
|
||||
private const val TAG = "ImageViewExtensions"
|
||||
|
||||
@Deprecated("use other constructor that expects com.nextcloud.talk.models.domain.ConversationModel")
|
||||
fun ImageView.loadConversationAvatar(
|
||||
user: User,
|
||||
conversation: Conversation,
|
||||
ignoreCache: Boolean,
|
||||
viewThemeUtils: ViewThemeUtils?
|
||||
): io.reactivex.disposables.Disposable {
|
||||
return loadConversationAvatar(
|
||||
user,
|
||||
ConversationModel.mapToConversationModel(conversation),
|
||||
ignoreCache,
|
||||
viewThemeUtils
|
||||
)
|
||||
}
|
||||
|
||||
fun ImageView.loadConversationAvatar(
|
||||
user: User,
|
||||
conversation: ConversationModel,
|
||||
ignoreCache: Boolean,
|
||||
viewThemeUtils: ViewThemeUtils?
|
||||
): io.reactivex.disposables.Disposable {
|
||||
val imageRequestUri = ApiUtils.getUrlForConversationAvatarWithVersion(
|
||||
1,
|
||||
|
@ -68,10 +85,10 @@ fun ImageView.loadConversationAvatar(
|
|||
|
||||
if (conversation.avatarVersion.isNullOrEmpty() && viewThemeUtils != null) {
|
||||
when (conversation.type) {
|
||||
Conversation.ConversationType.ROOM_GROUP_CALL ->
|
||||
ConversationType.ROOM_GROUP_CALL ->
|
||||
return loadDefaultGroupCallAvatar(viewThemeUtils)
|
||||
|
||||
Conversation.ConversationType.ROOM_PUBLIC_CALL ->
|
||||
ConversationType.ROOM_PUBLIC_CALL ->
|
||||
return loadDefaultPublicCallAvatar(viewThemeUtils)
|
||||
|
||||
else -> {}
|
||||
|
@ -82,10 +99,10 @@ fun ImageView.loadConversationAvatar(
|
|||
// when no own images are set. (although these default avatars can not be themed for the android app..)
|
||||
val errorPlaceholder =
|
||||
when (conversation.type) {
|
||||
Conversation.ConversationType.ROOM_GROUP_CALL ->
|
||||
ConversationType.ROOM_GROUP_CALL ->
|
||||
ContextCompat.getDrawable(context, R.drawable.ic_circular_group)
|
||||
|
||||
Conversation.ConversationType.ROOM_PUBLIC_CALL ->
|
||||
ConversationType.ROOM_PUBLIC_CALL ->
|
||||
ContextCompat.getDrawable(context, R.drawable.ic_circular_link)
|
||||
|
||||
else -> ContextCompat.getDrawable(context, R.drawable.account_circle_96dp)
|
||||
|
|
|
@ -55,12 +55,12 @@ import autodagger.AutoInjector
|
|||
import com.bluelinelabs.logansquare.LoganSquare
|
||||
import com.nextcloud.talk.BuildConfig
|
||||
import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.activities.CallNotificationActivity
|
||||
import com.nextcloud.talk.activities.MainActivity
|
||||
import com.nextcloud.talk.api.NcApi
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
|
||||
import com.nextcloud.talk.arbitrarystorage.ArbitraryStorageManager
|
||||
import com.nextcloud.talk.callnotification.CallNotificationActivity
|
||||
import com.nextcloud.talk.models.SignatureVerification
|
||||
import com.nextcloud.talk.models.json.chat.ChatUtils.Companion.getParsedMessage
|
||||
import com.nextcloud.talk.models.json.conversations.RoomOverall
|
||||
|
@ -94,7 +94,6 @@ import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_NOTIFICATION_TIMESTAMP
|
|||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_SHARE_RECORDING_TO_CHAT_URL
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_SYSTEM_NOTIFICATION_ID
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_USER_ENTITY
|
||||
import com.nextcloud.talk.utils.preferences.AppPreferences
|
||||
import com.nextcloud.talk.utils.singletons.ApplicationWideCurrentRoomHolder
|
||||
import io.reactivex.Observable
|
||||
|
@ -197,7 +196,7 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor
|
|||
val bundle = Bundle()
|
||||
bundle.putString(KEY_ROOM_TOKEN, pushMessage.id)
|
||||
bundle.putInt(KEY_NOTIFICATION_TIMESTAMP, pushMessage.timestamp.toInt())
|
||||
bundle.putParcelable(KEY_USER_ENTITY, signatureVerification.user)
|
||||
bundle.putLong(KEY_INTERNAL_USER_ID, signatureVerification.user!!.id!!)
|
||||
bundle.putBoolean(KEY_FROM_NOTIFICATION_START_CALL, true)
|
||||
fullScreenIntent.putExtras(bundle)
|
||||
fullScreenIntent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
|
@ -680,7 +679,6 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor
|
|||
shareRecordingIntent.putExtra(KEY_SYSTEM_NOTIFICATION_ID, systemNotificationId)
|
||||
shareRecordingIntent.putExtra(KEY_SHARE_RECORDING_TO_CHAT_URL, shareToChatUrl)
|
||||
shareRecordingIntent.putExtra(KEY_ROOM_TOKEN, pushMessage.id)
|
||||
shareRecordingIntent.putExtra(KEY_USER_ENTITY, signatureVerification.user)
|
||||
|
||||
val intentFlag: Int = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
|
||||
|
@ -924,7 +922,7 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor
|
|||
intent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
val bundle = Bundle()
|
||||
bundle.putString(KEY_ROOM_TOKEN, pushMessage.id)
|
||||
bundle.putParcelable(KEY_USER_ENTITY, signatureVerification.user)
|
||||
bundle.putLong(KEY_INTERNAL_USER_ID, signatureVerification.user!!.id!!)
|
||||
bundle.putBoolean(KEY_FROM_NOTIFICATION_START_CALL, false)
|
||||
intent.putExtras(bundle)
|
||||
return intent
|
||||
|
@ -935,7 +933,7 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor
|
|||
intent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
val bundle = Bundle()
|
||||
bundle.putString(KEY_ROOM_TOKEN, pushMessage.id)
|
||||
bundle.putParcelable(KEY_USER_ENTITY, signatureVerification.user)
|
||||
bundle.putLong(KEY_INTERNAL_USER_ID, signatureVerification.user!!.id!!)
|
||||
bundle.putBoolean(KEY_FROM_NOTIFICATION_START_CALL, false)
|
||||
intent.putExtras(bundle)
|
||||
|
||||
|
|
|
@ -53,8 +53,8 @@ import com.nextcloud.talk.utils.FileUtils
|
|||
import com.nextcloud.talk.utils.NotificationUtils
|
||||
import com.nextcloud.talk.utils.RemoteFileUtils
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_FROM_NOTIFICATION_START_CALL
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_INTERNAL_USER_ID
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_USER_ENTITY
|
||||
import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew
|
||||
import com.nextcloud.talk.utils.permissions.PlatformPermissionUtil
|
||||
import com.nextcloud.talk.utils.preferences.AppPreferences
|
||||
|
@ -244,7 +244,7 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa
|
|||
intent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
|
||||
bundle.putString(KEY_ROOM_TOKEN, roomToken)
|
||||
bundle.putParcelable(KEY_USER_ENTITY, currentUser)
|
||||
bundle.putLong(KEY_INTERNAL_USER_ID, currentUser.id!!)
|
||||
bundle.putBoolean(KEY_FROM_NOTIFICATION_START_CALL, false)
|
||||
|
||||
intent.putExtras(bundle)
|
||||
|
|
|
@ -0,0 +1,136 @@
|
|||
package com.nextcloud.talk.models.domain
|
||||
|
||||
import com.nextcloud.talk.models.json.conversations.Conversation
|
||||
|
||||
class ConversationModel(
|
||||
var roomId: String?,
|
||||
var token: String? = null,
|
||||
var name: String? = null,
|
||||
var displayName: String? = null,
|
||||
var description: String? = null,
|
||||
var type: ConversationType? = null,
|
||||
var lastPing: Long = 0,
|
||||
var participantType: ParticipantType? = null,
|
||||
var hasPassword: Boolean = false,
|
||||
var sessionId: String? = null,
|
||||
var actorId: String? = null,
|
||||
var actorType: String? = null,
|
||||
var password: String? = null,
|
||||
var favorite: Boolean = false,
|
||||
var lastActivity: Long = 0,
|
||||
var unreadMessages: Int = 0,
|
||||
var unreadMention: Boolean = false,
|
||||
// var lastMessage: .....? = null,
|
||||
var objectType: ObjectType? = null,
|
||||
var notificationLevel: NotificationLevel? = null,
|
||||
var conversationReadOnlyState: ConversationReadOnlyState? = null,
|
||||
var lobbyState: LobbyState? = null,
|
||||
var lobbyTimer: Long? = null,
|
||||
var lastReadMessage: Int = 0,
|
||||
var hasCall: Boolean = false,
|
||||
var callFlag: Int = 0,
|
||||
var canStartCall: Boolean = false,
|
||||
var canLeaveConversation: Boolean? = null,
|
||||
var canDeleteConversation: Boolean? = null,
|
||||
var unreadMentionDirect: Boolean? = null,
|
||||
var notificationCalls: Int? = null,
|
||||
var permissions: Int = 0,
|
||||
var messageExpiration: Int = 0,
|
||||
var status: String? = null,
|
||||
var statusIcon: String? = null,
|
||||
var statusMessage: String? = null,
|
||||
var statusClearAt: Long? = 0,
|
||||
var callRecording: Int = 0,
|
||||
var avatarVersion: String? = null,
|
||||
var hasCustomAvatar: Boolean? = null
|
||||
) {
|
||||
|
||||
companion object {
|
||||
fun mapToConversationModel(
|
||||
conversation: Conversation
|
||||
): ConversationModel {
|
||||
return ConversationModel(
|
||||
roomId = conversation.roomId,
|
||||
token = conversation.token,
|
||||
name = conversation.name,
|
||||
displayName = conversation.displayName,
|
||||
description = conversation.description,
|
||||
type = conversation.type?.let { ConversationType.valueOf(it.name) },
|
||||
lastPing = conversation.lastPing,
|
||||
participantType = conversation.participantType?.let { ParticipantType.valueOf(it.name) },
|
||||
hasPassword = conversation.hasPassword,
|
||||
sessionId = conversation.sessionId,
|
||||
actorId = conversation.actorId,
|
||||
actorType = conversation.actorType,
|
||||
password = conversation.password,
|
||||
favorite = conversation.favorite,
|
||||
lastActivity = conversation.lastActivity,
|
||||
unreadMessages = conversation.unreadMessages,
|
||||
unreadMention = conversation.unreadMention,
|
||||
// lastMessage = conversation.lastMessage, to do...
|
||||
objectType = conversation.objectType?.let { ObjectType.valueOf(it.name) },
|
||||
notificationLevel = conversation.notificationLevel?.let {
|
||||
NotificationLevel.valueOf(
|
||||
it.name
|
||||
)
|
||||
},
|
||||
conversationReadOnlyState = conversation.conversationReadOnlyState?.let {
|
||||
ConversationReadOnlyState.valueOf(
|
||||
it.name
|
||||
)
|
||||
},
|
||||
lobbyState = conversation.lobbyState?.let { LobbyState.valueOf(it.name) },
|
||||
lobbyTimer = conversation.lobbyTimer,
|
||||
lastReadMessage = conversation.lastReadMessage,
|
||||
hasCall = conversation.hasCall,
|
||||
callFlag = conversation.callFlag,
|
||||
canStartCall = conversation.canStartCall,
|
||||
canLeaveConversation = conversation.canLeaveConversation,
|
||||
canDeleteConversation = conversation.canDeleteConversation,
|
||||
unreadMentionDirect = conversation.unreadMentionDirect,
|
||||
notificationCalls = conversation.notificationCalls,
|
||||
permissions = conversation.permissions,
|
||||
messageExpiration = conversation.messageExpiration,
|
||||
status = conversation.status,
|
||||
statusIcon = conversation.statusIcon,
|
||||
statusMessage = conversation.statusMessage,
|
||||
statusClearAt = conversation.statusClearAt,
|
||||
callRecording = conversation.callRecording,
|
||||
avatarVersion = conversation.avatarVersion,
|
||||
hasCustomAvatar = conversation.hasCustomAvatar
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class ConversationType {
|
||||
DUMMY,
|
||||
ROOM_TYPE_ONE_TO_ONE_CALL,
|
||||
ROOM_GROUP_CALL,
|
||||
ROOM_PUBLIC_CALL,
|
||||
ROOM_SYSTEM,
|
||||
FORMER_ONE_TO_ONE
|
||||
}
|
||||
|
||||
enum class ParticipantType {
|
||||
DUMMY, OWNER, MODERATOR, USER, GUEST, USER_FOLLOWING_LINK, GUEST_MODERATOR
|
||||
}
|
||||
|
||||
enum class ObjectType {
|
||||
DEFAULT,
|
||||
SHARE_PASSWORD,
|
||||
FILE,
|
||||
ROOM
|
||||
}
|
||||
|
||||
enum class NotificationLevel {
|
||||
DEFAULT, ALWAYS, MENTION, NEVER
|
||||
}
|
||||
|
||||
enum class ConversationReadOnlyState {
|
||||
CONVERSATION_READ_WRITE, CONVERSATION_READ_ONLY
|
||||
}
|
||||
|
||||
enum class LobbyState {
|
||||
LOBBY_STATE_ALL_PARTICIPANTS, LOBBY_STATE_MODERATORS_ONLY
|
||||
}
|
|
@ -158,39 +158,47 @@ data class Conversation(
|
|||
// This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject'
|
||||
constructor() : this(null, null)
|
||||
|
||||
@Deprecated("Use ConversationUtil")
|
||||
val isPublic: Boolean
|
||||
get() = ConversationType.ROOM_PUBLIC_CALL == type
|
||||
|
||||
@Deprecated("Use ConversationUtil")
|
||||
val isGuest: Boolean
|
||||
get() = ParticipantType.GUEST == participantType ||
|
||||
ParticipantType.GUEST_MODERATOR == participantType ||
|
||||
ParticipantType.USER_FOLLOWING_LINK == participantType
|
||||
|
||||
@Deprecated("Use ConversationUtil")
|
||||
val isParticipantOwnerOrModerator: Boolean
|
||||
get() = ParticipantType.OWNER == participantType ||
|
||||
ParticipantType.GUEST_MODERATOR == participantType ||
|
||||
ParticipantType.MODERATOR == participantType
|
||||
|
||||
@Deprecated("Use ConversationUtil")
|
||||
private fun isLockedOneToOne(conversationUser: User): Boolean {
|
||||
return type == ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL &&
|
||||
CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "locked-one-to-one-rooms")
|
||||
}
|
||||
|
||||
@Deprecated("Use ConversationUtil")
|
||||
fun canModerate(conversationUser: User): Boolean {
|
||||
return isParticipantOwnerOrModerator &&
|
||||
!isLockedOneToOne(conversationUser) &&
|
||||
type != ConversationType.FORMER_ONE_TO_ONE
|
||||
}
|
||||
|
||||
@Deprecated("Use ConversationUtil")
|
||||
fun isLobbyViewApplicable(conversationUser: User): Boolean {
|
||||
return !canModerate(conversationUser) &&
|
||||
(type == ConversationType.ROOM_GROUP_CALL || type == ConversationType.ROOM_PUBLIC_CALL)
|
||||
}
|
||||
|
||||
@Deprecated("Use ConversationUtil")
|
||||
fun isNameEditable(conversationUser: User): Boolean {
|
||||
return canModerate(conversationUser) && ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL != type
|
||||
}
|
||||
|
||||
@Deprecated("Use ConversationUtil")
|
||||
fun canLeave(): Boolean {
|
||||
return if (canLeaveConversation != null) {
|
||||
// Available since APIv2
|
||||
|
@ -200,6 +208,7 @@ data class Conversation(
|
|||
}
|
||||
}
|
||||
|
||||
@Deprecated("Use ConversationUtil")
|
||||
fun canDelete(conversationUser: User): Boolean {
|
||||
return if (canDeleteConversation != null) {
|
||||
// Available since APIv2
|
||||
|
|
|
@ -79,10 +79,7 @@ class ListOpenConversationsActivity : BaseActivity() {
|
|||
}
|
||||
|
||||
private fun adapterOnClick(conversation: OpenConversation) {
|
||||
val user = userProvider.currentUser.blockingGet()
|
||||
|
||||
val bundle = Bundle()
|
||||
bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, user)
|
||||
bundle.putString(BundleKeys.KEY_ROOM_TOKEN, conversation.roomToken)
|
||||
|
||||
val chatIntent = Intent(context, ChatActivity::class.java)
|
||||
|
|
|
@ -37,6 +37,7 @@ import com.nextcloud.talk.data.user.model.User
|
|||
import com.nextcloud.talk.databinding.DialogPollMainBinding
|
||||
import com.nextcloud.talk.polls.viewmodels.PollMainViewModel
|
||||
import com.nextcloud.talk.ui.theme.ViewThemeUtils
|
||||
import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew
|
||||
import javax.inject.Inject
|
||||
|
||||
@AutoInjector(NextcloudTalkApplication::class)
|
||||
|
@ -48,16 +49,22 @@ class PollMainDialogFragment : DialogFragment() {
|
|||
@Inject
|
||||
lateinit var viewThemeUtils: ViewThemeUtils
|
||||
|
||||
var currentUserProvider: CurrentUserProviderNew? = null
|
||||
@Inject set
|
||||
|
||||
private lateinit var binding: DialogPollMainBinding
|
||||
private lateinit var viewModel: PollMainViewModel
|
||||
|
||||
lateinit var user: User
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
|
||||
|
||||
viewModel = ViewModelProvider(this, viewModelFactory)[PollMainViewModel::class.java]
|
||||
|
||||
val user: User = arguments?.getParcelable(KEY_USER_ENTITY)!!
|
||||
user = currentUserProvider?.currentUser?.blockingGet()!!
|
||||
|
||||
val roomToken = arguments?.getString(KEY_ROOM_TOKEN)!!
|
||||
val isOwnerOrModerator = arguments?.getBoolean(KEY_OWNER_OR_MODERATOR)!!
|
||||
val pollId = arguments?.getString(KEY_POLL_ID)!!
|
||||
|
|
|
@ -56,8 +56,6 @@ class ShareRecordingToChatReceiver : BroadcastReceiver() {
|
|||
lateinit var currentUser: User
|
||||
private var systemNotificationId: Int? = null
|
||||
private var link: String? = null
|
||||
var roomToken: String? = null
|
||||
var conversationOfShareTarget: User? = null
|
||||
|
||||
init {
|
||||
NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
|
||||
|
@ -68,9 +66,6 @@ class ShareRecordingToChatReceiver : BroadcastReceiver() {
|
|||
systemNotificationId = intent!!.getIntExtra(KEY_SYSTEM_NOTIFICATION_ID, 0)
|
||||
link = intent.getStringExtra(BundleKeys.KEY_SHARE_RECORDING_TO_CHAT_URL)
|
||||
|
||||
roomToken = intent.getStringExtra(BundleKeys.KEY_ROOM_TOKEN)
|
||||
conversationOfShareTarget = intent.getParcelableExtra<User>(BundleKeys.KEY_USER_ENTITY)
|
||||
|
||||
val id = intent.getLongExtra(KEY_INTERNAL_USER_ID, userManager.currentUser.blockingGet().id!!)
|
||||
currentUser = userManager.getUserWithId(id).blockingGet()
|
||||
|
||||
|
|
|
@ -23,19 +23,18 @@ package com.nextcloud.talk.repositories.reactions
|
|||
import com.nextcloud.talk.models.domain.ReactionAddedModel
|
||||
import com.nextcloud.talk.models.domain.ReactionDeletedModel
|
||||
import com.nextcloud.talk.models.json.chat.ChatMessage
|
||||
import com.nextcloud.talk.models.json.conversations.Conversation
|
||||
import io.reactivex.Observable
|
||||
|
||||
interface ReactionsRepository {
|
||||
|
||||
fun addReaction(
|
||||
currentConversation: Conversation,
|
||||
roomToken: String,
|
||||
message: ChatMessage,
|
||||
emoji: String
|
||||
): Observable<ReactionAddedModel>
|
||||
|
||||
fun deleteReaction(
|
||||
currentConversation: Conversation,
|
||||
roomToken: String,
|
||||
message: ChatMessage,
|
||||
emoji: String
|
||||
): Observable<ReactionDeletedModel>
|
||||
|
|
|
@ -25,7 +25,6 @@ import com.nextcloud.talk.data.user.model.User
|
|||
import com.nextcloud.talk.models.domain.ReactionAddedModel
|
||||
import com.nextcloud.talk.models.domain.ReactionDeletedModel
|
||||
import com.nextcloud.talk.models.json.chat.ChatMessage
|
||||
import com.nextcloud.talk.models.json.conversations.Conversation
|
||||
import com.nextcloud.talk.models.json.generic.GenericMeta
|
||||
import com.nextcloud.talk.utils.ApiUtils
|
||||
import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew
|
||||
|
@ -38,7 +37,7 @@ class ReactionsRepositoryImpl(private val ncApi: NcApi, currentUserProvider: Cur
|
|||
val credentials: String = ApiUtils.getCredentials(currentUser.username, currentUser.token)
|
||||
|
||||
override fun addReaction(
|
||||
currentConversation: Conversation,
|
||||
roomToken: String,
|
||||
message: ChatMessage,
|
||||
emoji: String
|
||||
): Observable<ReactionAddedModel> {
|
||||
|
@ -46,7 +45,7 @@ class ReactionsRepositoryImpl(private val ncApi: NcApi, currentUserProvider: Cur
|
|||
credentials,
|
||||
ApiUtils.getUrlForMessageReaction(
|
||||
currentUser.baseUrl,
|
||||
currentConversation.token,
|
||||
roomToken,
|
||||
message.id
|
||||
),
|
||||
emoji
|
||||
|
@ -54,7 +53,7 @@ class ReactionsRepositoryImpl(private val ncApi: NcApi, currentUserProvider: Cur
|
|||
}
|
||||
|
||||
override fun deleteReaction(
|
||||
currentConversation: Conversation,
|
||||
roomToken: String,
|
||||
message: ChatMessage,
|
||||
emoji: String
|
||||
): Observable<ReactionDeletedModel> {
|
||||
|
@ -62,7 +61,7 @@ class ReactionsRepositoryImpl(private val ncApi: NcApi, currentUserProvider: Cur
|
|||
credentials,
|
||||
ApiUtils.getUrlForMessageReaction(
|
||||
currentUser.baseUrl,
|
||||
currentConversation.token,
|
||||
roomToken,
|
||||
message.id
|
||||
),
|
||||
emoji
|
||||
|
|
|
@ -49,12 +49,15 @@ import com.nextcloud.talk.ui.theme.ViewThemeUtils
|
|||
import com.nextcloud.talk.utils.DisplayUtils
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CONVERSATION_NAME
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_USER_ENTITY
|
||||
import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew
|
||||
import javax.inject.Inject
|
||||
|
||||
@AutoInjector(NextcloudTalkApplication::class)
|
||||
class SharedItemsActivity : AppCompatActivity() {
|
||||
|
||||
@Inject
|
||||
lateinit var currentUserProvider: CurrentUserProviderNew
|
||||
|
||||
@Inject
|
||||
lateinit var viewModelFactory: ViewModelProvider.Factory
|
||||
|
||||
|
@ -70,7 +73,9 @@ class SharedItemsActivity : AppCompatActivity() {
|
|||
|
||||
val roomToken = intent.getStringExtra(KEY_ROOM_TOKEN)!!
|
||||
val conversationName = intent.getStringExtra(KEY_CONVERSATION_NAME)
|
||||
val user = intent.getParcelableExtra<User>(KEY_USER_ENTITY)!!
|
||||
|
||||
val user = currentUserProvider.currentUser.blockingGet()
|
||||
|
||||
val isUserConversationOwnerOrModerator = intent.getBooleanExtra(KEY_USER_IS_OWNER_OR_MODERATOR, false)
|
||||
|
||||
binding = ActivitySharedItemsBinding.inflate(layoutInflater)
|
||||
|
|
|
@ -46,7 +46,6 @@ import io.reactivex.Observer
|
|||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import org.parceler.Parcels
|
||||
|
||||
private const val TAG = "ProfileBottomSheet"
|
||||
|
||||
|
@ -144,32 +143,9 @@ class ProfileBottomSheet(val ncApi: NcApi, val userModel: User) {
|
|||
|
||||
override fun onNext(roomOverall: RoomOverall) {
|
||||
val bundle = Bundle()
|
||||
bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, userModel)
|
||||
bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomOverall.ocs!!.data!!.token)
|
||||
bundle.putString(BundleKeys.KEY_ROOM_ID, roomOverall.ocs!!.data!!.roomId)
|
||||
|
||||
// FIXME once APIv2+ is used only, the createRoom already returns all the data
|
||||
ncApi.getRoom(
|
||||
credentials,
|
||||
ApiUtils.getUrlForRoom(
|
||||
apiVersion,
|
||||
userModel.baseUrl,
|
||||
roomOverall.ocs!!.data!!.token
|
||||
)
|
||||
)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : Observer<RoomOverall> {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
// unused atm
|
||||
}
|
||||
|
||||
override fun onNext(roomOverall: RoomOverall) {
|
||||
bundle.putParcelable(
|
||||
BundleKeys.KEY_ACTIVE_CONVERSATION,
|
||||
Parcels.wrap(roomOverall.ocs!!.data)
|
||||
)
|
||||
|
||||
val chatIntent = Intent(context, ChatActivity::class.java)
|
||||
chatIntent.putExtras(bundle)
|
||||
chatIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
|
@ -186,16 +162,6 @@ class ProfileBottomSheet(val ncApi: NcApi, val userModel: User) {
|
|||
})
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
Log.e(TAG, e.message, e)
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
// unused atm
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun composeEmail(address: String, context: Context) {
|
||||
val addresses = arrayListOf(address)
|
||||
val intent = Intent(Intent.ACTION_SENDTO).apply {
|
||||
|
|
|
@ -203,7 +203,7 @@ public class ChooseAccountDialogFragment extends DialogFragment {
|
|||
dismiss();
|
||||
|
||||
if (status != null) {
|
||||
SetStatusDialogFragment setStatusDialog = SetStatusDialogFragment.newInstance(user, status);
|
||||
SetStatusDialogFragment setStatusDialog = SetStatusDialogFragment.newInstance(status);
|
||||
setStatusDialog.show(getActivity().getSupportFragmentManager(), "fragment_set_status");
|
||||
} else {
|
||||
Log.w(TAG, "status was null");
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
|
||||
package com.nextcloud.talk.ui.dialog
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Bundle
|
||||
import android.text.TextUtils
|
||||
import android.view.View
|
||||
|
@ -55,16 +54,13 @@ import com.nextcloud.talk.ui.theme.ViewThemeUtils
|
|||
import com.nextcloud.talk.users.UserManager
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_INTERNAL_USER_ID
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_OPERATION_CODE
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN
|
||||
import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew
|
||||
import org.parceler.Parcels
|
||||
import javax.inject.Inject
|
||||
|
||||
@AutoInjector(NextcloudTalkApplication::class)
|
||||
class ConversationsListBottomDialog(
|
||||
val activity: Activity,
|
||||
val controller: ConversationsListActivity,
|
||||
val activity: ConversationsListActivity,
|
||||
val currentUser: User,
|
||||
val conversation: Conversation
|
||||
) : BottomSheetDialog(activity) {
|
||||
|
@ -175,9 +171,8 @@ class ConversationsListBottomDialog(
|
|||
if (!TextUtils.isEmpty(conversation.token)) {
|
||||
val bundle = Bundle()
|
||||
bundle.putLong(KEY_INTERNAL_USER_ID, currentUser.id!!)
|
||||
bundle.putParcelable(KEY_ROOM, Parcels.wrap(conversation))
|
||||
|
||||
controller.showDeleteConversationDialog(bundle)
|
||||
bundle.putString(KEY_ROOM_TOKEN, conversation.token)
|
||||
activity.showDeleteConversationDialog(bundle)
|
||||
}
|
||||
|
||||
dismiss()
|
||||
|
@ -198,8 +193,8 @@ class ConversationsListBottomDialog(
|
|||
|
||||
private fun executeOperationsMenuController(operation: ConversationOperationEnum) {
|
||||
val bundle = Bundle()
|
||||
bundle.putParcelable(KEY_ROOM, Parcels.wrap(conversation))
|
||||
bundle.putSerializable(KEY_OPERATION_CODE, operation)
|
||||
bundle.putString(KEY_ROOM_TOKEN, conversation.token)
|
||||
|
||||
binding.operationItemsLayout.visibility = View.GONE
|
||||
|
||||
|
@ -211,13 +206,13 @@ class ConversationsListBottomDialog(
|
|||
.popChangeHandler(HorizontalChangeHandler())
|
||||
)
|
||||
|
||||
controller.fetchRooms()
|
||||
activity.fetchRooms()
|
||||
}
|
||||
|
||||
private fun executeEntryMenuController(operation: ConversationOperationEnum) {
|
||||
val bundle = Bundle()
|
||||
bundle.putParcelable(KEY_ROOM, Parcels.wrap(conversation))
|
||||
bundle.putSerializable(KEY_OPERATION_CODE, operation)
|
||||
bundle.putString(KEY_ROOM_TOKEN, conversation.token)
|
||||
|
||||
binding.operationItemsLayout.visibility = View.GONE
|
||||
|
||||
|
|
|
@ -40,10 +40,12 @@ import com.nextcloud.talk.application.NextcloudTalkApplication
|
|||
import com.nextcloud.talk.chat.ChatActivity
|
||||
import com.nextcloud.talk.data.user.model.User
|
||||
import com.nextcloud.talk.databinding.DialogMessageActionsBinding
|
||||
import com.nextcloud.talk.models.domain.ConversationModel
|
||||
import com.nextcloud.talk.models.domain.ConversationReadOnlyState
|
||||
import com.nextcloud.talk.models.domain.ConversationType
|
||||
import com.nextcloud.talk.models.domain.ReactionAddedModel
|
||||
import com.nextcloud.talk.models.domain.ReactionDeletedModel
|
||||
import com.nextcloud.talk.models.json.chat.ChatMessage
|
||||
import com.nextcloud.talk.models.json.conversations.Conversation
|
||||
import com.nextcloud.talk.repositories.reactions.ReactionsRepository
|
||||
import com.nextcloud.talk.ui.theme.ViewThemeUtils
|
||||
import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew
|
||||
|
@ -63,7 +65,7 @@ class MessageActionsDialog(
|
|||
private val chatActivity: ChatActivity,
|
||||
private val message: ChatMessage,
|
||||
private val user: User?,
|
||||
private val currentConversation: Conversation?,
|
||||
private val currentConversation: ConversationModel?,
|
||||
private val showMessageDeletionButton: Boolean,
|
||||
private val hasChatPermission: Boolean
|
||||
) : BottomSheetDialog(chatActivity) {
|
||||
|
@ -100,7 +102,7 @@ class MessageActionsDialog(
|
|||
message.replyable &&
|
||||
hasUserId(user) &&
|
||||
hasUserActorId(message) &&
|
||||
currentConversation?.type != Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL
|
||||
currentConversation?.type != ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL
|
||||
)
|
||||
initMenuDeleteMessage(showMessageDeletionButton)
|
||||
initMenuForwardMessage(
|
||||
|
@ -226,7 +228,7 @@ class MessageActionsDialog(
|
|||
}
|
||||
|
||||
private fun isPermitted(hasChatPermission: Boolean): Boolean {
|
||||
return hasChatPermission && Conversation.ConversationReadOnlyState.CONVERSATION_READ_ONLY !=
|
||||
return hasChatPermission && ConversationReadOnlyState.CONVERSATION_READ_ONLY !=
|
||||
currentConversation?.conversationReadOnlyState
|
||||
}
|
||||
|
||||
|
@ -338,12 +340,12 @@ class MessageActionsDialog(
|
|||
|
||||
private fun clickOnEmoji(message: ChatMessage, emoji: String) {
|
||||
if (message.reactionsSelf?.contains(emoji) == true) {
|
||||
reactionsRepository.deleteReaction(currentConversation!!, message, emoji)
|
||||
reactionsRepository.deleteReaction(currentConversation!!.token!!, message, emoji)
|
||||
.subscribeOn(Schedulers.io())
|
||||
?.observeOn(AndroidSchedulers.mainThread())
|
||||
?.subscribe(ReactionDeletedObserver())
|
||||
} else {
|
||||
reactionsRepository.addReaction(currentConversation!!, message, emoji)
|
||||
reactionsRepository.addReaction(currentConversation!!.token!!, message, emoji)
|
||||
.subscribeOn(Schedulers.io())
|
||||
?.observeOn(AndroidSchedulers.mainThread())
|
||||
?.subscribe(ReactionAddedObserver())
|
||||
|
|
|
@ -60,6 +60,7 @@ import com.nextcloud.talk.models.json.status.predefined.PredefinedStatusOverall
|
|||
import com.nextcloud.talk.ui.theme.ViewThemeUtils
|
||||
import com.nextcloud.talk.utils.ApiUtils
|
||||
import com.nextcloud.talk.utils.DisplayUtils
|
||||
import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew
|
||||
import com.vanniktech.emoji.EmojiPopup
|
||||
import com.vanniktech.emoji.installDisableKeyboardInput
|
||||
import com.vanniktech.emoji.installForceSingleEmoji
|
||||
|
@ -113,6 +114,9 @@ class SetStatusDialogFragment :
|
|||
@Inject
|
||||
lateinit var viewThemeUtils: ViewThemeUtils
|
||||
|
||||
var currentUserProvider: CurrentUserProviderNew? = null
|
||||
@Inject set
|
||||
|
||||
lateinit var credentials: String
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
|
@ -121,7 +125,7 @@ class SetStatusDialogFragment :
|
|||
NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
|
||||
|
||||
arguments?.let {
|
||||
currentUser = it.getParcelable(ARG_CURRENT_USER_PARAM)
|
||||
currentUser = currentUserProvider?.currentUser?.blockingGet()
|
||||
currentStatus = it.getParcelable(ARG_CURRENT_STATUS_PARAM)
|
||||
|
||||
credentials = ApiUtils.getCredentials(currentUser?.username, currentUser?.token)
|
||||
|
@ -559,9 +563,8 @@ class SetStatusDialogFragment :
|
|||
private val TAG = SetStatusDialogFragment::class.simpleName
|
||||
|
||||
@JvmStatic
|
||||
fun newInstance(user: User, status: Status): SetStatusDialogFragment {
|
||||
fun newInstance(status: Status): SetStatusDialogFragment {
|
||||
val args = Bundle()
|
||||
args.putParcelable(ARG_CURRENT_USER_PARAM, user)
|
||||
args.putParcelable(ARG_CURRENT_STATUS_PARAM, status)
|
||||
|
||||
val dialogFragment = SetStatusDialogFragment()
|
||||
|
|
|
@ -44,7 +44,6 @@ import com.nextcloud.talk.data.user.model.User
|
|||
import com.nextcloud.talk.databinding.DialogMessageReactionsBinding
|
||||
import com.nextcloud.talk.databinding.ItemReactionsTabBinding
|
||||
import com.nextcloud.talk.models.json.chat.ChatMessage
|
||||
import com.nextcloud.talk.models.json.conversations.Conversation
|
||||
import com.nextcloud.talk.models.json.generic.GenericOverall
|
||||
import com.nextcloud.talk.models.json.reactions.ReactionsOverall
|
||||
import com.nextcloud.talk.ui.theme.ViewThemeUtils
|
||||
|
@ -59,7 +58,7 @@ import javax.inject.Inject
|
|||
@AutoInjector(NextcloudTalkApplication::class)
|
||||
class ShowReactionsDialog(
|
||||
activity: Activity,
|
||||
private val currentConversation: Conversation?,
|
||||
private val roomToken: String,
|
||||
private val chatMessage: ChatMessage,
|
||||
private val user: User?,
|
||||
private val hasChatPermission: Boolean,
|
||||
|
@ -156,7 +155,7 @@ class ShowReactionsDialog(
|
|||
credentials,
|
||||
ApiUtils.getUrlForMessageReaction(
|
||||
user?.baseUrl,
|
||||
currentConversation!!.token,
|
||||
roomToken,
|
||||
chatMessage.id
|
||||
),
|
||||
emoji
|
||||
|
@ -211,7 +210,7 @@ class ShowReactionsDialog(
|
|||
credentials,
|
||||
ApiUtils.getUrlForMessageReaction(
|
||||
user?.baseUrl,
|
||||
currentConversation!!.token,
|
||||
roomToken,
|
||||
message.id
|
||||
),
|
||||
emoji
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
package com.nextcloud.talk.utils
|
||||
|
||||
import com.nextcloud.talk.data.user.model.User
|
||||
import com.nextcloud.talk.models.domain.ConversationModel
|
||||
import com.nextcloud.talk.models.domain.ConversationType
|
||||
import com.nextcloud.talk.models.domain.ParticipantType
|
||||
import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew
|
||||
|
||||
/*
|
||||
* Nextcloud Talk application
|
||||
* @author Marcel Hibbe
|
||||
* Copyright (C) 2023 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
object ConversationUtils {
|
||||
private val TAG = ConversationUtils::class.java.simpleName
|
||||
|
||||
fun isPublic(conversation: ConversationModel): Boolean {
|
||||
return ConversationType.ROOM_PUBLIC_CALL == conversation.type
|
||||
}
|
||||
|
||||
fun isGuest(conversation: ConversationModel): Boolean {
|
||||
return ParticipantType.GUEST == conversation.participantType ||
|
||||
ParticipantType.GUEST_MODERATOR == conversation.participantType ||
|
||||
ParticipantType.USER_FOLLOWING_LINK == conversation.participantType
|
||||
}
|
||||
|
||||
fun isParticipantOwnerOrModerator(conversation: ConversationModel): Boolean {
|
||||
return ParticipantType.OWNER == conversation.participantType ||
|
||||
ParticipantType.GUEST_MODERATOR == conversation.participantType ||
|
||||
ParticipantType.MODERATOR == conversation.participantType
|
||||
}
|
||||
|
||||
private fun isLockedOneToOne(conversation: ConversationModel, conversationUser: User): Boolean {
|
||||
return conversation.type == ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL &&
|
||||
CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "locked-one-to-one-rooms")
|
||||
}
|
||||
|
||||
fun canModerate(conversation: ConversationModel, conversationUser: User): Boolean {
|
||||
return isParticipantOwnerOrModerator(conversation) &&
|
||||
!isLockedOneToOne(conversation, conversationUser) &&
|
||||
conversation.type != ConversationType.FORMER_ONE_TO_ONE
|
||||
}
|
||||
|
||||
fun isLobbyViewApplicable(conversation: ConversationModel, conversationUser: User): Boolean {
|
||||
return !canModerate(conversation, conversationUser) &&
|
||||
(
|
||||
conversation.type == ConversationType.ROOM_GROUP_CALL ||
|
||||
conversation.type == ConversationType.ROOM_PUBLIC_CALL
|
||||
)
|
||||
}
|
||||
|
||||
fun isNameEditable(conversation: ConversationModel, conversationUser: User): Boolean {
|
||||
return canModerate(conversation, conversationUser) &&
|
||||
ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL != conversation.type
|
||||
}
|
||||
|
||||
fun canLeave(conversation: ConversationModel): Boolean {
|
||||
return if (conversation.canLeaveConversation != null) {
|
||||
// Available since APIv2
|
||||
conversation.canLeaveConversation!!
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
fun canDelete(conversation: ConversationModel, conversationUser: User): Boolean {
|
||||
return if (conversation.canDeleteConversation != null) {
|
||||
// Available since APIv2
|
||||
conversation.canDeleteConversation!!
|
||||
} else {
|
||||
canModerate(conversation, conversationUser)
|
||||
// Fallback for APIv1
|
||||
}
|
||||
}
|
||||
}
|
|
@ -23,6 +23,7 @@
|
|||
package com.nextcloud.talk.utils
|
||||
|
||||
import com.nextcloud.talk.data.user.model.User
|
||||
import com.nextcloud.talk.models.domain.ConversationModel
|
||||
import com.nextcloud.talk.models.json.conversations.Conversation
|
||||
import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew
|
||||
|
||||
|
@ -31,9 +32,15 @@ import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew
|
|||
*/
|
||||
class ParticipantPermissions(
|
||||
private val user: User,
|
||||
private val conversation: Conversation
|
||||
private val conversation: ConversationModel
|
||||
) {
|
||||
|
||||
@Deprecated("Use ChatRepository.ConversationModel")
|
||||
constructor(user: User, conversation: Conversation) : this(
|
||||
user,
|
||||
ConversationModel.mapToConversationModel(conversation)
|
||||
)
|
||||
|
||||
val isDefault = (conversation.permissions and DEFAULT) == DEFAULT
|
||||
val isCustom = (conversation.permissions and CUSTOM) == CUSTOM
|
||||
private val canStartCall = (conversation.permissions and START_CALL) == START_CALL
|
||||
|
|
|
@ -33,19 +33,17 @@ object BundleKeys {
|
|||
const val KEY_BASE_URL = "KEY_BASE_URL"
|
||||
const val KEY_IS_ACCOUNT_IMPORT = "KEY_IS_ACCOUNT_IMPORT"
|
||||
const val KEY_ORIGINAL_PROTOCOL = "KEY_ORIGINAL_PROTOCOL"
|
||||
const val KEY_ROOM = "KEY_CONVERSATION"
|
||||
const val KEY_OPERATION_CODE = "KEY_OPERATION_CODE"
|
||||
const val KEY_SHARE_INTENT = "KEY_SHARE_INTENT"
|
||||
const val KEY_APP_ITEM_PACKAGE_NAME = "KEY_APP_ITEM_PACKAGE_NAME"
|
||||
const val KEY_APP_ITEM_NAME = "KEY_APP_ITEM_NAME"
|
||||
const val KEY_CONVERSATION_PASSWORD = "KEY_CONVERSATION_PASSWORD"
|
||||
const val KEY_ROOM_TOKEN = "KEY_ROOM_TOKEN"
|
||||
const val KEY_ROOM_ONE_TO_ONE = "KEY_ROOM_ONE_TO_ONE"
|
||||
const val KEY_USER_ENTITY = "KEY_USER_ENTITY"
|
||||
const val KEY_NEW_CONVERSATION = "KEY_NEW_CONVERSATION"
|
||||
const val KEY_ADD_PARTICIPANTS = "KEY_ADD_PARTICIPANTS"
|
||||
const val KEY_EXISTING_PARTICIPANTS = "KEY_EXISTING_PARTICIPANTS"
|
||||
const val KEY_CALL_URL = "KEY_CALL_URL"
|
||||
const val KEY_NEW_ROOM_NAME = "KEY_NEW_ROOM_NAME"
|
||||
const val KEY_MODIFIED_BASE_URL = "KEY_MODIFIED_BASE_URL"
|
||||
const val KEY_NOTIFICATION_SUBJECT = "KEY_NOTIFICATION_SUBJECT"
|
||||
const val KEY_NOTIFICATION_SIGNATURE = "KEY_NOTIFICATION_SIGNATURE"
|
||||
|
@ -59,8 +57,6 @@ object BundleKeys {
|
|||
const val KEY_RECORDING_STATE = "KEY_RECORDING_STATE"
|
||||
const val KEY_CALL_VOICE_ONLY = "KEY_CALL_VOICE_ONLY"
|
||||
const val KEY_CALL_WITHOUT_NOTIFICATION = "KEY_CALL_WITHOUT_NOTIFICATION"
|
||||
const val KEY_ACTIVE_CONVERSATION = "KEY_ACTIVE_CONVERSATION"
|
||||
const val KEY_SERVER_CAPABILITIES = "KEY_SERVER_CAPABILITIES"
|
||||
const val KEY_FROM_NOTIFICATION_START_CALL = "KEY_FROM_NOTIFICATION_START_CALL"
|
||||
const val KEY_ROOM_ID = "KEY_ROOM_ID"
|
||||
const val KEY_ARE_CALL_SOUNDS = "KEY_ARE_CALL_SOUNDS"
|
||||
|
|
Loading…
Reference in a new issue