mirror of
https://github.com/nextcloud/talk-android
synced 2025-03-11 18:10:44 +00:00
commit
5a7c45ef27
@ -13,6 +13,7 @@ import com.nextcloud.talk.models.json.generic.GenericOverall
|
|||||||
import com.nextcloud.talk.models.json.participants.AddParticipantOverall
|
import com.nextcloud.talk.models.json.participants.AddParticipantOverall
|
||||||
import com.nextcloud.talk.models.json.participants.TalkBan
|
import com.nextcloud.talk.models.json.participants.TalkBan
|
||||||
import com.nextcloud.talk.models.json.participants.TalkBanOverall
|
import com.nextcloud.talk.models.json.participants.TalkBanOverall
|
||||||
|
import com.nextcloud.talk.models.json.userAbsence.UserAbsenceOverall
|
||||||
import okhttp3.MultipartBody
|
import okhttp3.MultipartBody
|
||||||
import okhttp3.RequestBody
|
import okhttp3.RequestBody
|
||||||
import retrofit2.http.Body
|
import retrofit2.http.Body
|
||||||
@ -197,4 +198,10 @@ interface NcApiCoroutines {
|
|||||||
@Url url: String,
|
@Url url: String,
|
||||||
@Field("seconds") seconds: Int
|
@Field("seconds") seconds: Int
|
||||||
): GenericOverall
|
): GenericOverall
|
||||||
|
|
||||||
|
@GET
|
||||||
|
suspend fun getOutOfOfficeStatusForUser(
|
||||||
|
@Header("Authorization") authorization: String,
|
||||||
|
@Url url: String
|
||||||
|
): UserAbsenceOverall
|
||||||
}
|
}
|
||||||
|
@ -45,15 +45,18 @@ import android.view.animation.AccelerateDecelerateInterpolator
|
|||||||
import android.widget.AbsListView
|
import android.widget.AbsListView
|
||||||
import android.widget.FrameLayout
|
import android.widget.FrameLayout
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
|
import android.widget.LinearLayout
|
||||||
import android.widget.PopupMenu
|
import android.widget.PopupMenu
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.activity.OnBackPressedCallback
|
import androidx.activity.OnBackPressedCallback
|
||||||
import androidx.activity.result.ActivityResult
|
import androidx.activity.result.ActivityResult
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.appcompat.view.ContextThemeWrapper
|
import androidx.appcompat.view.ContextThemeWrapper
|
||||||
|
import androidx.cardview.widget.CardView
|
||||||
import androidx.core.content.FileProvider
|
import androidx.core.content.FileProvider
|
||||||
import androidx.core.content.PermissionChecker
|
import androidx.core.content.PermissionChecker
|
||||||
import androidx.core.content.PermissionChecker.PERMISSION_GRANTED
|
import androidx.core.content.PermissionChecker.PERMISSION_GRANTED
|
||||||
|
import androidx.core.graphics.ColorUtils
|
||||||
import androidx.core.graphics.drawable.toBitmap
|
import androidx.core.graphics.drawable.toBitmap
|
||||||
import androidx.core.text.bold
|
import androidx.core.text.bold
|
||||||
import androidx.emoji2.text.EmojiCompat
|
import androidx.emoji2.text.EmojiCompat
|
||||||
@ -70,11 +73,13 @@ import androidx.work.WorkInfo
|
|||||||
import androidx.work.WorkManager
|
import androidx.work.WorkManager
|
||||||
import autodagger.AutoInjector
|
import autodagger.AutoInjector
|
||||||
import coil.imageLoader
|
import coil.imageLoader
|
||||||
|
import coil.load
|
||||||
import coil.request.CachePolicy
|
import coil.request.CachePolicy
|
||||||
import coil.request.ImageRequest
|
import coil.request.ImageRequest
|
||||||
import coil.target.Target
|
import coil.target.Target
|
||||||
import coil.transform.CircleCropTransformation
|
import coil.transform.CircleCropTransformation
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
|
import com.nextcloud.android.common.ui.color.ColorUtil
|
||||||
import com.nextcloud.android.common.ui.theme.utils.ColorRole
|
import com.nextcloud.android.common.ui.theme.utils.ColorRole
|
||||||
import com.nextcloud.talk.BuildConfig
|
import com.nextcloud.talk.BuildConfig
|
||||||
import com.nextcloud.talk.R
|
import com.nextcloud.talk.R
|
||||||
@ -209,7 +214,6 @@ import java.util.Date
|
|||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import java.util.concurrent.ExecutionException
|
import java.util.concurrent.ExecutionException
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlin.String
|
|
||||||
import kotlin.collections.set
|
import kotlin.collections.set
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
@ -240,6 +244,9 @@ class ChatActivity :
|
|||||||
@Inject
|
@Inject
|
||||||
lateinit var dateUtils: DateUtils
|
lateinit var dateUtils: DateUtils
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var colorUtil: ColorUtil
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var viewModelFactory: ViewModelProvider.Factory
|
lateinit var viewModelFactory: ViewModelProvider.Factory
|
||||||
|
|
||||||
@ -568,7 +575,7 @@ class ChatActivity :
|
|||||||
this.lifecycle.removeObserver(chatViewModel)
|
this.lifecycle.removeObserver(chatViewModel)
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("NotifyDataSetChanged")
|
@SuppressLint("NotifyDataSetChanged", "SetTextI18n", "ResourceAsColor")
|
||||||
@Suppress("LongMethod")
|
@Suppress("LongMethod")
|
||||||
private fun initObservers() {
|
private fun initObservers() {
|
||||||
Log.d(TAG, "initObservers Called")
|
Log.d(TAG, "initObservers Called")
|
||||||
@ -684,9 +691,21 @@ class ChatActivity :
|
|||||||
loadAvatarForStatusBar()
|
loadAvatarForStatusBar()
|
||||||
setupSwipeToReply()
|
setupSwipeToReply()
|
||||||
setActionBarTitle()
|
setActionBarTitle()
|
||||||
|
|
||||||
checkShowCallButtons()
|
checkShowCallButtons()
|
||||||
checkLobbyState()
|
checkLobbyState()
|
||||||
|
if (currentConversation?.type == ConversationEnums.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL &&
|
||||||
|
currentConversation?.status == "dnd"
|
||||||
|
) {
|
||||||
|
conversationUser?.let { user ->
|
||||||
|
val credentials = ApiUtils.getCredentials(user.username, user.token)
|
||||||
|
chatViewModel.outOfOfficeStatusOfUser(
|
||||||
|
credentials!!,
|
||||||
|
user.baseUrl!!,
|
||||||
|
currentConversation!!.name
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
updateRoomTimerHandler()
|
updateRoomTimerHandler()
|
||||||
|
|
||||||
val urlForChatting =
|
val urlForChatting =
|
||||||
@ -1053,6 +1072,99 @@ class ChatActivity :
|
|||||||
chatViewModel.recordTouchObserver.observe(this) { y ->
|
chatViewModel.recordTouchObserver.observe(this) { y ->
|
||||||
binding.voiceRecordingLock.y -= y
|
binding.voiceRecordingLock.y -= y
|
||||||
}
|
}
|
||||||
|
|
||||||
|
chatViewModel.outOfOfficeViewState.observe(this) { uiState ->
|
||||||
|
when (uiState) {
|
||||||
|
is ChatViewModel.OutOfOfficeUIState.Error -> {
|
||||||
|
Log.e(TAG, "Error fetching/ no user absence data", uiState.exception)
|
||||||
|
}
|
||||||
|
ChatViewModel.OutOfOfficeUIState.None -> {
|
||||||
|
}
|
||||||
|
is ChatViewModel.OutOfOfficeUIState.Success -> {
|
||||||
|
binding.outOfOfficeContainer.visibility = View.VISIBLE
|
||||||
|
|
||||||
|
val backgroundColor = colorUtil.getNullSafeColorWithFallbackRes(
|
||||||
|
conversationUser!!.capabilities!!.themingCapability!!.color,
|
||||||
|
R.color.colorPrimary
|
||||||
|
)
|
||||||
|
|
||||||
|
binding.outOfOfficeContainer.findViewById<View>(
|
||||||
|
R.id.verticalLine
|
||||||
|
).setBackgroundColor(backgroundColor)
|
||||||
|
val setAlpha = ColorUtils.setAlphaComponent(backgroundColor, OUT_OF_OFFICE_ALPHA)
|
||||||
|
binding.outOfOfficeContainer.setCardBackgroundColor(setAlpha)
|
||||||
|
|
||||||
|
val startDateTimestamp: Long = uiState.userAbsence.startDate.toLong()
|
||||||
|
val endDateTimestamp: Long = uiState.userAbsence.endDate.toLong()
|
||||||
|
|
||||||
|
val startDate = Date(startDateTimestamp * ONE_SECOND_IN_MILLIS)
|
||||||
|
val endDate = Date(endDateTimestamp * ONE_SECOND_IN_MILLIS)
|
||||||
|
|
||||||
|
if (dateUtils.isSameDate(startDate, endDate)) {
|
||||||
|
binding.outOfOfficeContainer.findViewById<TextView>(R.id.userAbsenceShortMessage).text =
|
||||||
|
String.format(
|
||||||
|
context.resources.getString(R.string.user_absence_for_one_day),
|
||||||
|
uiState.userAbsence.userId
|
||||||
|
)
|
||||||
|
binding.outOfOfficeContainer.findViewById<TextView>(R.id.userAbsencePeriod).visibility =
|
||||||
|
View.GONE
|
||||||
|
} else {
|
||||||
|
val dateFormatter = SimpleDateFormat("MMM d, yyyy", Locale.getDefault())
|
||||||
|
val startDateString = dateFormatter.format(startDate)
|
||||||
|
val endDateString = dateFormatter.format(endDate)
|
||||||
|
binding.outOfOfficeContainer.findViewById<TextView>(R.id.userAbsenceShortMessage).text =
|
||||||
|
String.format(
|
||||||
|
context.resources.getString(R.string.user_absence),
|
||||||
|
uiState.userAbsence.userId
|
||||||
|
)
|
||||||
|
|
||||||
|
binding.outOfOfficeContainer.findViewById<TextView>(R.id.userAbsencePeriod).text =
|
||||||
|
"$startDateString - $endDateString"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uiState.userAbsence.replacementUserDisplayName != null) {
|
||||||
|
var imageUri = Uri.parse(
|
||||||
|
ApiUtils.getUrlForAvatar(
|
||||||
|
conversationUser?.baseUrl,
|
||||||
|
uiState.userAbsence
|
||||||
|
.replacementUserId,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if (DisplayUtils.isDarkModeOn(context)) {
|
||||||
|
imageUri = Uri.parse(
|
||||||
|
ApiUtils.getUrlForAvatarDarkTheme(
|
||||||
|
conversationUser?.baseUrl,
|
||||||
|
uiState
|
||||||
|
.userAbsence
|
||||||
|
.replacementUserId,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
binding.outOfOfficeContainer.findViewById<TextView>(R.id.absenceReplacement).text =
|
||||||
|
context.resources.getString(R.string.user_absence_replacement)
|
||||||
|
binding.outOfOfficeContainer.findViewById<ImageView>(R.id.replacement_user_avatar)
|
||||||
|
.load(imageUri) {
|
||||||
|
transformations(CircleCropTransformation())
|
||||||
|
placeholder(R.drawable.account_circle_96dp)
|
||||||
|
error(R.drawable.account_circle_96dp)
|
||||||
|
crossfade(true)
|
||||||
|
}
|
||||||
|
binding.outOfOfficeContainer.findViewById<TextView>(R.id.replacement_user_name).text =
|
||||||
|
uiState.userAbsence.replacementUserDisplayName
|
||||||
|
} else {
|
||||||
|
binding.outOfOfficeContainer.findViewById<LinearLayout>(R.id.userAbsenceReplacement)
|
||||||
|
.visibility = View.GONE
|
||||||
|
}
|
||||||
|
binding.outOfOfficeContainer.findViewById<TextView>(R.id.userAbsenceLongMessage).text =
|
||||||
|
uiState.userAbsence.message
|
||||||
|
binding.outOfOfficeContainer.findViewById<CardView>(R.id.avatar_chip).setOnClickListener {
|
||||||
|
joinOneToOneConversation(uiState.userAbsence.replacementUserId!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun removeUnreadMessagesMarker() {
|
private fun removeUnreadMessagesMarker() {
|
||||||
@ -3819,6 +3931,24 @@ class ChatActivity :
|
|||||||
startActivity(shareIntent)
|
startActivity(shareIntent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun joinOneToOneConversation(userId: String) {
|
||||||
|
val apiVersion =
|
||||||
|
ApiUtils.getConversationApiVersion(conversationUser!!, intArrayOf(ApiUtils.API_V4, 1))
|
||||||
|
val retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(
|
||||||
|
apiVersion,
|
||||||
|
conversationUser?.baseUrl!!,
|
||||||
|
ROOM_TYPE_ONE_TO_ONE,
|
||||||
|
ACTOR_TYPE,
|
||||||
|
userId,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
chatViewModel.createRoom(
|
||||||
|
credentials!!,
|
||||||
|
retrofitBucket.url!!,
|
||||||
|
retrofitBucket.queryMap!!
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val TAG = ChatActivity::class.simpleName
|
val TAG = ChatActivity::class.simpleName
|
||||||
private const val CONTENT_TYPE_CALL_STARTED: Byte = 1
|
private const val CONTENT_TYPE_CALL_STARTED: Byte = 1
|
||||||
@ -3871,7 +4001,10 @@ class ChatActivity :
|
|||||||
private const val FIVE_MINUTES_IN_SECONDS: Long = 300
|
private const val FIVE_MINUTES_IN_SECONDS: Long = 300
|
||||||
private const val TEMPORARY_MESSAGE_ID_INT: Int = -3
|
private const val TEMPORARY_MESSAGE_ID_INT: Int = -3
|
||||||
private const val TEMPORARY_MESSAGE_ID_STRING: String = "-3"
|
private const val TEMPORARY_MESSAGE_ID_STRING: String = "-3"
|
||||||
|
private const val ROOM_TYPE_ONE_TO_ONE = "1"
|
||||||
|
private const val ACTOR_TYPE = "users"
|
||||||
const val CONVERSATION_INTERNAL_ID = "CONVERSATION_INTERNAL_ID"
|
const val CONVERSATION_INTERNAL_ID = "CONVERSATION_INTERNAL_ID"
|
||||||
const val NO_OFFLINE_MESSAGES_FOUND = "NO_OFFLINE_MESSAGES_FOUND"
|
const val NO_OFFLINE_MESSAGES_FOUND = "NO_OFFLINE_MESSAGES_FOUND"
|
||||||
|
const val OUT_OF_OFFICE_ALPHA = 76
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ import com.nextcloud.talk.models.json.conversations.RoomOverall
|
|||||||
import com.nextcloud.talk.models.json.conversations.RoomsOverall
|
import com.nextcloud.talk.models.json.conversations.RoomsOverall
|
||||||
import com.nextcloud.talk.models.json.generic.GenericOverall
|
import com.nextcloud.talk.models.json.generic.GenericOverall
|
||||||
import com.nextcloud.talk.models.json.reminder.Reminder
|
import com.nextcloud.talk.models.json.reminder.Reminder
|
||||||
|
import com.nextcloud.talk.models.json.userAbsence.UserAbsenceOverall
|
||||||
import io.reactivex.Observable
|
import io.reactivex.Observable
|
||||||
import retrofit2.Response
|
import retrofit2.Response
|
||||||
|
|
||||||
@ -63,4 +64,5 @@ interface ChatNetworkDataSource {
|
|||||||
fun createRoom(credentials: String, url: String, map: Map<String, String>): Observable<RoomOverall>
|
fun createRoom(credentials: String, url: String, map: Map<String, String>): Observable<RoomOverall>
|
||||||
fun setChatReadMarker(credentials: String, url: String, previousMessageId: Int): Observable<GenericOverall>
|
fun setChatReadMarker(credentials: String, url: String, previousMessageId: Int): Observable<GenericOverall>
|
||||||
fun editChatMessage(credentials: String, url: String, text: String): Observable<ChatOverallSingleMessage>
|
fun editChatMessage(credentials: String, url: String, text: String): Observable<ChatOverallSingleMessage>
|
||||||
|
suspend fun getOutOfOfficeStatusForUser(credentials: String, baseUrl: String, userId: String): UserAbsenceOverall
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
package com.nextcloud.talk.chat.data.network
|
package com.nextcloud.talk.chat.data.network
|
||||||
|
|
||||||
import com.nextcloud.talk.api.NcApi
|
import com.nextcloud.talk.api.NcApi
|
||||||
|
import com.nextcloud.talk.api.NcApiCoroutines
|
||||||
import com.nextcloud.talk.data.user.model.User
|
import com.nextcloud.talk.data.user.model.User
|
||||||
import com.nextcloud.talk.models.domain.ConversationModel
|
import com.nextcloud.talk.models.domain.ConversationModel
|
||||||
import com.nextcloud.talk.models.json.capabilities.SpreedCapability
|
import com.nextcloud.talk.models.json.capabilities.SpreedCapability
|
||||||
@ -15,11 +16,15 @@ import com.nextcloud.talk.models.json.conversations.RoomOverall
|
|||||||
import com.nextcloud.talk.models.json.conversations.RoomsOverall
|
import com.nextcloud.talk.models.json.conversations.RoomsOverall
|
||||||
import com.nextcloud.talk.models.json.generic.GenericOverall
|
import com.nextcloud.talk.models.json.generic.GenericOverall
|
||||||
import com.nextcloud.talk.models.json.reminder.Reminder
|
import com.nextcloud.talk.models.json.reminder.Reminder
|
||||||
|
import com.nextcloud.talk.models.json.userAbsence.UserAbsenceOverall
|
||||||
import com.nextcloud.talk.utils.ApiUtils
|
import com.nextcloud.talk.utils.ApiUtils
|
||||||
import io.reactivex.Observable
|
import io.reactivex.Observable
|
||||||
import retrofit2.Response
|
import retrofit2.Response
|
||||||
|
|
||||||
class RetrofitChatNetwork(private val ncApi: NcApi) : ChatNetworkDataSource {
|
class RetrofitChatNetwork(
|
||||||
|
private val ncApi: NcApi,
|
||||||
|
private val ncApiCoroutines: NcApiCoroutines
|
||||||
|
) : ChatNetworkDataSource {
|
||||||
override fun getRoom(user: User, roomToken: String): Observable<ConversationModel> {
|
override fun getRoom(user: User, roomToken: String): Observable<ConversationModel> {
|
||||||
val credentials: String = ApiUtils.getCredentials(user.username, user.token)!!
|
val credentials: String = ApiUtils.getCredentials(user.username, user.token)!!
|
||||||
val apiVersion = ApiUtils.getConversationApiVersion(user, intArrayOf(ApiUtils.API_V4, ApiUtils.API_V3, 1))
|
val apiVersion = ApiUtils.getConversationApiVersion(user, intArrayOf(ApiUtils.API_V4, ApiUtils.API_V3, 1))
|
||||||
@ -178,4 +183,15 @@ class RetrofitChatNetwork(private val ncApi: NcApi) : ChatNetworkDataSource {
|
|||||||
override fun editChatMessage(credentials: String, url: String, text: String): Observable<ChatOverallSingleMessage> {
|
override fun editChatMessage(credentials: String, url: String, text: String): Observable<ChatOverallSingleMessage> {
|
||||||
return ncApi.editChatMessage(credentials, url, text).map { it }
|
return ncApi.editChatMessage(credentials, url, text).map { it }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun getOutOfOfficeStatusForUser(
|
||||||
|
credentials: String,
|
||||||
|
baseUrl: String,
|
||||||
|
userId: String
|
||||||
|
): UserAbsenceOverall {
|
||||||
|
return ncApiCoroutines.getOutOfOfficeStatusForUser(
|
||||||
|
credentials,
|
||||||
|
ApiUtils.getUrlForOutOfOffice(baseUrl, userId)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ import androidx.lifecycle.LifecycleOwner
|
|||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.nextcloud.talk.chat.data.ChatMessageRepository
|
import com.nextcloud.talk.chat.data.ChatMessageRepository
|
||||||
import com.nextcloud.talk.chat.data.io.AudioFocusRequestManager
|
import com.nextcloud.talk.chat.data.io.AudioFocusRequestManager
|
||||||
import com.nextcloud.talk.chat.data.io.MediaRecorderManager
|
import com.nextcloud.talk.chat.data.io.MediaRecorderManager
|
||||||
@ -33,6 +34,7 @@ import com.nextcloud.talk.models.json.conversations.RoomOverall
|
|||||||
import com.nextcloud.talk.models.json.conversations.RoomsOverall
|
import com.nextcloud.talk.models.json.conversations.RoomsOverall
|
||||||
import com.nextcloud.talk.models.json.generic.GenericOverall
|
import com.nextcloud.talk.models.json.generic.GenericOverall
|
||||||
import com.nextcloud.talk.models.json.reminder.Reminder
|
import com.nextcloud.talk.models.json.reminder.Reminder
|
||||||
|
import com.nextcloud.talk.models.json.userAbsence.UserAbsenceData
|
||||||
import com.nextcloud.talk.repositories.reactions.ReactionsRepository
|
import com.nextcloud.talk.repositories.reactions.ReactionsRepository
|
||||||
import com.nextcloud.talk.ui.PlaybackSpeed
|
import com.nextcloud.talk.ui.PlaybackSpeed
|
||||||
import com.nextcloud.talk.utils.ConversationUtils
|
import com.nextcloud.talk.utils.ConversationUtils
|
||||||
@ -47,6 +49,7 @@ import kotlinx.coroutines.flow.catch
|
|||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.flow.flow
|
import kotlinx.coroutines.flow.flow
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ -109,6 +112,10 @@ class ChatViewModel @Inject constructor(
|
|||||||
val getVoiceRecordingLocked: LiveData<Boolean>
|
val getVoiceRecordingLocked: LiveData<Boolean>
|
||||||
get() = _getVoiceRecordingLocked
|
get() = _getVoiceRecordingLocked
|
||||||
|
|
||||||
|
private val _outOfOfficeViewState = MutableLiveData<OutOfOfficeUIState>(OutOfOfficeUIState.None)
|
||||||
|
val outOfOfficeViewState: LiveData<OutOfOfficeUIState>
|
||||||
|
get() = _outOfOfficeViewState
|
||||||
|
|
||||||
private val _voiceMessagePlaybackSpeedPreferences: MutableLiveData<Map<String, PlaybackSpeed>> = MutableLiveData()
|
private val _voiceMessagePlaybackSpeedPreferences: MutableLiveData<Map<String, PlaybackSpeed>> = MutableLiveData()
|
||||||
val voiceMessagePlaybackSpeedPreferences: LiveData<Map<String, PlaybackSpeed>>
|
val voiceMessagePlaybackSpeedPreferences: LiveData<Map<String, PlaybackSpeed>>
|
||||||
get() = _voiceMessagePlaybackSpeedPreferences
|
get() = _voiceMessagePlaybackSpeedPreferences
|
||||||
@ -764,8 +771,26 @@ class ChatViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("Detekt.TooGenericExceptionCaught")
|
||||||
|
fun outOfOfficeStatusOfUser(credentials: String, baseUrl: String, userId: String) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
try {
|
||||||
|
val response = chatNetworkDataSource.getOutOfOfficeStatusForUser(credentials, baseUrl, userId)
|
||||||
|
_outOfOfficeViewState.value = OutOfOfficeUIState.Success(response.ocs?.data!!)
|
||||||
|
} catch (exception: Exception) {
|
||||||
|
_outOfOfficeViewState.value = OutOfOfficeUIState.Error(exception)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val TAG = ChatViewModel::class.simpleName
|
private val TAG = ChatViewModel::class.simpleName
|
||||||
const val JOIN_ROOM_RETRY_COUNT: Long = 3
|
const val JOIN_ROOM_RETRY_COUNT: Long = 3
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sealed class OutOfOfficeUIState {
|
||||||
|
data object None : OutOfOfficeUIState()
|
||||||
|
data class Success(val userAbsence: UserAbsenceData) : OutOfOfficeUIState()
|
||||||
|
data class Error(val exception: Exception) : OutOfOfficeUIState()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -147,8 +147,8 @@ class RepositoryModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
fun provideChatNetworkDataSource(ncApi: NcApi): ChatNetworkDataSource {
|
fun provideChatNetworkDataSource(ncApi: NcApi, ncApiCoroutines: NcApiCoroutines): ChatNetworkDataSource {
|
||||||
return RetrofitChatNetwork(ncApi)
|
return RetrofitChatNetwork(ncApi, ncApiCoroutines)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Nextcloud Talk - Android Client
|
||||||
|
*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Sowjanya Kota <sowjanya.kch@gmail.com>
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.nextcloud.talk.models.json.userAbsence
|
||||||
|
|
||||||
|
import android.os.Parcelable
|
||||||
|
import com.bluelinelabs.logansquare.annotation.JsonField
|
||||||
|
import com.bluelinelabs.logansquare.annotation.JsonObject
|
||||||
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
|
@Parcelize
|
||||||
|
@JsonObject
|
||||||
|
data class UserAbsenceData(
|
||||||
|
@JsonField(name = ["id"])
|
||||||
|
var id: String,
|
||||||
|
@JsonField(name = ["userId"])
|
||||||
|
var userId: String,
|
||||||
|
@JsonField(name = ["startDate"])
|
||||||
|
var startDate: Int,
|
||||||
|
@JsonField(name = ["endDate"])
|
||||||
|
var endDate: Int,
|
||||||
|
@JsonField(name = ["shortMessage"])
|
||||||
|
var shortMessage: String,
|
||||||
|
@JsonField(name = ["message"])
|
||||||
|
var message: String,
|
||||||
|
@JsonField(name = ["replacementUserId"])
|
||||||
|
var replacementUserId: String?,
|
||||||
|
@JsonField(name = ["replacementUserDisplayName"])
|
||||||
|
var replacementUserDisplayName: String?
|
||||||
|
) : Parcelable {
|
||||||
|
// This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject'
|
||||||
|
constructor() :
|
||||||
|
this("", "", 0, 0, "", "", null, null)
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* Nextcloud Talk - Android Client
|
||||||
|
*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Sowjanya Kota <sowjanya.kch@gmail.com>
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.nextcloud.talk.models.json.userAbsence
|
||||||
|
|
||||||
|
import android.os.Parcelable
|
||||||
|
import com.bluelinelabs.logansquare.annotation.JsonField
|
||||||
|
import com.bluelinelabs.logansquare.annotation.JsonObject
|
||||||
|
import com.nextcloud.talk.models.json.generic.GenericMeta
|
||||||
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
|
@Parcelize
|
||||||
|
@JsonObject
|
||||||
|
data class UserAbsenceOCS(
|
||||||
|
@JsonField(name = ["meta"])
|
||||||
|
var meta: GenericMeta?,
|
||||||
|
@JsonField(name = ["data"])
|
||||||
|
var data: UserAbsenceData?
|
||||||
|
) : Parcelable {
|
||||||
|
// This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject'
|
||||||
|
constructor() : this(null, null)
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* Nextcloud Talk - Android Client
|
||||||
|
*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Sowjanya Kota <sowjanya.kch@gmail.com>
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.nextcloud.talk.models.json.userAbsence
|
||||||
|
|
||||||
|
import android.os.Parcelable
|
||||||
|
import com.bluelinelabs.logansquare.annotation.JsonField
|
||||||
|
import com.bluelinelabs.logansquare.annotation.JsonObject
|
||||||
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
|
@Parcelize
|
||||||
|
@JsonObject
|
||||||
|
data class UserAbsenceOverall(
|
||||||
|
@JsonField(name = ["ocs"])
|
||||||
|
var ocs: UserAbsenceOCS?
|
||||||
|
) : Parcelable {
|
||||||
|
// This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject'
|
||||||
|
constructor() : this(null)
|
||||||
|
}
|
@ -372,6 +372,12 @@ object ApiUtils {
|
|||||||
return baseUrl + "/index.php/avatar/" + Uri.encode(name) + "/" + avatarSize
|
return baseUrl + "/index.php/avatar/" + Uri.encode(name) + "/" + avatarSize
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun getUrlForAvatarDarkTheme(baseUrl: String?, name: String?, requestBigSize: Boolean): String {
|
||||||
|
val avatarSize = if (requestBigSize) AVATAR_SIZE_BIG else AVATAR_SIZE_SMALL
|
||||||
|
return baseUrl + "/index.php/avatar/" + Uri.encode(name) + "/" + avatarSize + "/dark"
|
||||||
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun getUrlForFederatedAvatar(
|
fun getUrlForFederatedAvatar(
|
||||||
baseUrl: String,
|
baseUrl: String,
|
||||||
@ -601,4 +607,8 @@ object ApiUtils {
|
|||||||
fun getUrlForArchive(version: Int, baseUrl: String?, token: String?): String {
|
fun getUrlForArchive(version: Int, baseUrl: String?, token: String?): String {
|
||||||
return "${getUrlForRoom(version, baseUrl, token)}/archive"
|
return "${getUrlForRoom(version, baseUrl, token)}/archive"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getUrlForOutOfOffice(baseUrl: String, userId: String): String {
|
||||||
|
return "$baseUrl$OCS_API_VERSION/apps/dav/api/v1/outOfOffice/$userId/now"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,6 +51,14 @@ class DateUtils(val context: Context) {
|
|||||||
return formatTime.format(Date(timestampSeconds * DateConstants.SECOND_DIVIDER))
|
return formatTime.format(Date(timestampSeconds * DateConstants.SECOND_DIVIDER))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun isSameDate(date1: Date, date2: Date): Boolean {
|
||||||
|
val startDateCalendar = Calendar.getInstance().apply { time = date1 }
|
||||||
|
val endDateCalendar = Calendar.getInstance().apply { time = date2 }
|
||||||
|
val isSameDay = startDateCalendar.get(Calendar.YEAR) == endDateCalendar.get(Calendar.YEAR) &&
|
||||||
|
startDateCalendar.get(Calendar.DAY_OF_YEAR) == endDateCalendar.get(Calendar.DAY_OF_YEAR)
|
||||||
|
return isSameDay
|
||||||
|
}
|
||||||
|
|
||||||
fun getTimeDifferenceInSeconds(time2: Long, time1: Long): Long {
|
fun getTimeDifferenceInSeconds(time2: Long, time1: Long): Long {
|
||||||
val difference = (time2 - time1)
|
val difference = (time2 - time1)
|
||||||
return abs(difference)
|
return abs(difference)
|
||||||
|
@ -127,6 +127,18 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:id="@+id/out_of_office_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible"
|
||||||
|
android:layout_margin="8dp"
|
||||||
|
app:cardCornerRadius="12dp">
|
||||||
|
|
||||||
|
<include layout="@layout/out_of_office_view" />
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
<com.stfalcon.chatkit.messages.MessagesList
|
<com.stfalcon.chatkit.messages.MessagesList
|
||||||
android:id="@+id/messagesListView"
|
android:id="@+id/messagesListView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@ -134,6 +146,7 @@
|
|||||||
android:clipToPadding="false"
|
android:clipToPadding="false"
|
||||||
android:paddingBottom="20dp"
|
android:paddingBottom="20dp"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
|
android:layout_below= "@id/out_of_office_container"
|
||||||
app:dateHeaderTextSize="13sp"
|
app:dateHeaderTextSize="13sp"
|
||||||
app:incomingBubblePaddingBottom="@dimen/message_bubble_corners_vertical_padding"
|
app:incomingBubblePaddingBottom="@dimen/message_bubble_corners_vertical_padding"
|
||||||
app:incomingBubblePaddingLeft="@dimen/message_bubble_corners_horizontal_padding"
|
app:incomingBubblePaddingLeft="@dimen/message_bubble_corners_horizontal_padding"
|
||||||
@ -182,7 +195,9 @@
|
|||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
tools:visibility="visible"
|
tools:visibility="visible"
|
||||||
app:background="@color/colorPrimary"
|
app:background="@color/colorPrimary"
|
||||||
|
android:clipToPadding="false"
|
||||||
app:cornerRadius="@dimen/button_corner_radius"
|
app:cornerRadius="@dimen/button_corner_radius"
|
||||||
|
|
||||||
app:icon="@drawable/ic_baseline_arrow_downward_24px" />
|
app:icon="@drawable/ic_baseline_arrow_downward_24px" />
|
||||||
|
|
||||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
|
113
app/src/main/res/layout/out_of_office_view.xml
Normal file
113
app/src/main/res/layout/out_of_office_view.xml
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Nextcloud Talk - Android Client
|
||||||
|
~
|
||||||
|
~ SPDX-FileCopyrightText: 2024 Sowjanya Kota <sowjanya.kch@gmail.com>
|
||||||
|
~ SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
-->
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
android:id="@+id/out_of_office_view"
|
||||||
|
app:layout_constraintHeight_min="0dp"
|
||||||
|
app:layout_constraintHeight_max="150dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/verticalLine"
|
||||||
|
android:layout_width="6dp"
|
||||||
|
android:layout_height="match_parent" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/userAbsenceShortMessage"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
tools:text="Jane is out of office"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/userAbsencePeriod"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop ="8dp"
|
||||||
|
android:textSize="14sp"
|
||||||
|
tools:text="Dec 5, 2024 - Dec 15, 2024"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/userAbsenceReplacement"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_marginTop ="8dp"
|
||||||
|
android:gravity="center_vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/absenceReplacement"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textSize="14sp"
|
||||||
|
tools:text="Replacement: "/>
|
||||||
|
|
||||||
|
<androidx.cardview.widget.CardView
|
||||||
|
android:id="@+id/avatar_chip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="28dp"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
app:cardCornerRadius="16dp"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
app:cardElevation="2dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding = "4dp"
|
||||||
|
android:gravity="center_vertical">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/replacement_user_avatar"
|
||||||
|
android:layout_width="20dp"
|
||||||
|
android:layout_height="20dp"
|
||||||
|
android:layout_marginEnd="4dp"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
android:contentDescription="@null" />
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/replacement_user_name"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
tools:text="Bob" />
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/userAbsenceLongMessage"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop ="8dp"
|
||||||
|
android:textSize="14sp"
|
||||||
|
tools:text="Hi, I am out of office this week. Please contact ....., ..........write very very very very very very very very very very very very very very very long message..................................................................................................................................if you have any issues."/>
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
</ScrollView>
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -839,4 +839,8 @@ How to translate with transifex:
|
|||||||
<string name="conversation_read_only_failed">Failed to set conversation Read-only</string>
|
<string name="conversation_read_only_failed">Failed to set conversation Read-only</string>
|
||||||
<string name="status_reverted">Status Reverted</string>
|
<string name="status_reverted">Status Reverted</string>
|
||||||
<string name="automatic_status_set">Your status was set automatically</string>
|
<string name="automatic_status_set">Your status was set automatically</string>
|
||||||
|
|
||||||
|
<string name="user_absence">%1$s is out of office and might not respond</string>
|
||||||
|
<string name="user_absence_for_one_day">%1$s is out of office today</string>
|
||||||
|
<string name="user_absence_replacement">Replacement: </string>
|
||||||
</resources>
|
</resources>
|
||||||
|
Loading…
Reference in New Issue
Block a user