mirror of
https://github.com/nextcloud/talk-android
synced 2025-03-07 06:39:45 +00:00
Merge branch 'master' into master
Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
This commit is contained in:
commit
c86769d0e3
@ -167,7 +167,7 @@ ext {
|
||||
espressoVersion = "3.6.1"
|
||||
androidxTestVersion = "1.5.0"
|
||||
media3_version = "1.4.1"
|
||||
coroutines_version = "1.9.0"
|
||||
coroutines_version = "1.10.0"
|
||||
mockitoKotlinVersion = "5.4.0"
|
||||
}
|
||||
|
||||
@ -327,7 +327,7 @@ dependencies {
|
||||
|
||||
androidTestImplementation "androidx.test:core:1.6.1"
|
||||
|
||||
androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:1.9.0"
|
||||
androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:1.10.0"
|
||||
androidTestImplementation 'androidx.test:core-ktx:1.6.1'
|
||||
androidTestImplementation 'org.mockito:mockito-android:5.14.2'
|
||||
androidTestImplementation "androidx.work:work-testing:${workVersion}"
|
||||
|
@ -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.TalkBan
|
||||
import com.nextcloud.talk.models.json.participants.TalkBanOverall
|
||||
import com.nextcloud.talk.models.json.userAbsence.UserAbsenceOverall
|
||||
import okhttp3.MultipartBody
|
||||
import okhttp3.RequestBody
|
||||
import retrofit2.http.Body
|
||||
@ -197,4 +198,10 @@ interface NcApiCoroutines {
|
||||
@Url url: String,
|
||||
@Field("seconds") seconds: Int
|
||||
): GenericOverall
|
||||
|
||||
@GET
|
||||
suspend fun getOutOfOfficeStatusForUser(
|
||||
@Header("Authorization") authorization: String,
|
||||
@Url url: String
|
||||
): UserAbsenceOverall
|
||||
}
|
||||
|
@ -46,15 +46,18 @@ import android.view.animation.AccelerateDecelerateInterpolator
|
||||
import android.widget.AbsListView
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.PopupMenu
|
||||
import android.widget.TextView
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.activity.result.ActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.view.ContextThemeWrapper
|
||||
import androidx.cardview.widget.CardView
|
||||
import androidx.core.content.FileProvider
|
||||
import androidx.core.content.PermissionChecker
|
||||
import androidx.core.content.PermissionChecker.PERMISSION_GRANTED
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import androidx.core.graphics.drawable.toBitmap
|
||||
import androidx.core.text.bold
|
||||
import androidx.emoji2.text.EmojiCompat
|
||||
@ -71,11 +74,13 @@ import androidx.work.WorkInfo
|
||||
import androidx.work.WorkManager
|
||||
import autodagger.AutoInjector
|
||||
import coil.imageLoader
|
||||
import coil.load
|
||||
import coil.request.CachePolicy
|
||||
import coil.request.ImageRequest
|
||||
import coil.target.Target
|
||||
import coil.transform.CircleCropTransformation
|
||||
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.talk.BuildConfig
|
||||
import com.nextcloud.talk.R
|
||||
@ -210,7 +215,6 @@ import java.util.Date
|
||||
import java.util.Locale
|
||||
import java.util.concurrent.ExecutionException
|
||||
import javax.inject.Inject
|
||||
import kotlin.String
|
||||
import kotlin.collections.set
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
@ -241,6 +245,9 @@ class ChatActivity :
|
||||
@Inject
|
||||
lateinit var dateUtils: DateUtils
|
||||
|
||||
@Inject
|
||||
lateinit var colorUtil: ColorUtil
|
||||
|
||||
@Inject
|
||||
lateinit var viewModelFactory: ViewModelProvider.Factory
|
||||
|
||||
@ -569,7 +576,7 @@ class ChatActivity :
|
||||
this.lifecycle.removeObserver(chatViewModel)
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
@SuppressLint("NotifyDataSetChanged", "SetTextI18n", "ResourceAsColor")
|
||||
@Suppress("LongMethod")
|
||||
private fun initObservers() {
|
||||
Log.d(TAG, "initObservers Called")
|
||||
@ -685,9 +692,21 @@ class ChatActivity :
|
||||
loadAvatarForStatusBar()
|
||||
setupSwipeToReply()
|
||||
setActionBarTitle()
|
||||
|
||||
checkShowCallButtons()
|
||||
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()
|
||||
|
||||
val urlForChatting =
|
||||
@ -1054,6 +1073,99 @@ class ChatActivity :
|
||||
chatViewModel.recordTouchObserver.observe(this) { 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() {
|
||||
@ -3916,6 +4028,24 @@ class ChatActivity :
|
||||
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 {
|
||||
val TAG = ChatActivity::class.simpleName
|
||||
private const val CONTENT_TYPE_CALL_STARTED: Byte = 1
|
||||
@ -3968,11 +4098,14 @@ class ChatActivity :
|
||||
private const val FIVE_MINUTES_IN_SECONDS: Long = 300
|
||||
private const val TEMPORARY_MESSAGE_ID_INT: Int = -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 NO_OFFLINE_MESSAGES_FOUND = "NO_OFFLINE_MESSAGES_FOUND"
|
||||
const val VOICE_MESSAGE_CONTINUOUS_BEFORE = -5
|
||||
const val VOICE_MESSAGE_CONTINUOUS_AFTER = 5
|
||||
const val VOICE_MESSAGE_PLAY_ADD_THRESHOLD = 0.1
|
||||
const val VOICE_MESSAGE_MARK_PLAYED_FACTOR = 20
|
||||
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.generic.GenericOverall
|
||||
import com.nextcloud.talk.models.json.reminder.Reminder
|
||||
import com.nextcloud.talk.models.json.userAbsence.UserAbsenceOverall
|
||||
import io.reactivex.Observable
|
||||
import retrofit2.Response
|
||||
|
||||
@ -63,4 +64,5 @@ interface ChatNetworkDataSource {
|
||||
fun createRoom(credentials: String, url: String, map: Map<String, String>): Observable<RoomOverall>
|
||||
fun setChatReadMarker(credentials: String, url: String, previousMessageId: Int): Observable<GenericOverall>
|
||||
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
|
||||
|
||||
import com.nextcloud.talk.api.NcApi
|
||||
import com.nextcloud.talk.api.NcApiCoroutines
|
||||
import com.nextcloud.talk.data.user.model.User
|
||||
import com.nextcloud.talk.models.domain.ConversationModel
|
||||
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.generic.GenericOverall
|
||||
import com.nextcloud.talk.models.json.reminder.Reminder
|
||||
import com.nextcloud.talk.models.json.userAbsence.UserAbsenceOverall
|
||||
import com.nextcloud.talk.utils.ApiUtils
|
||||
import io.reactivex.Observable
|
||||
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> {
|
||||
val credentials: String = ApiUtils.getCredentials(user.username, user.token)!!
|
||||
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> {
|
||||
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.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.nextcloud.talk.chat.data.ChatMessageRepository
|
||||
import com.nextcloud.talk.chat.data.io.AudioFocusRequestManager
|
||||
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.generic.GenericOverall
|
||||
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.ui.PlaybackSpeed
|
||||
import com.nextcloud.talk.utils.ConversationUtils
|
||||
@ -47,6 +49,7 @@ import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.File
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -109,6 +112,10 @@ class ChatViewModel @Inject constructor(
|
||||
val getVoiceRecordingLocked: LiveData<Boolean>
|
||||
get() = _getVoiceRecordingLocked
|
||||
|
||||
private val _outOfOfficeViewState = MutableLiveData<OutOfOfficeUIState>(OutOfOfficeUIState.None)
|
||||
val outOfOfficeViewState: LiveData<OutOfOfficeUIState>
|
||||
get() = _outOfOfficeViewState
|
||||
|
||||
private val _voiceMessagePlaybackSpeedPreferences: MutableLiveData<Map<String, PlaybackSpeed>> = MutableLiveData()
|
||||
val voiceMessagePlaybackSpeedPreferences: LiveData<Map<String, PlaybackSpeed>>
|
||||
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 {
|
||||
private val TAG = ChatViewModel::class.simpleName
|
||||
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
|
||||
fun provideChatNetworkDataSource(ncApi: NcApi): ChatNetworkDataSource {
|
||||
return RetrofitChatNetwork(ncApi)
|
||||
fun provideChatNetworkDataSource(ncApi: NcApi, ncApiCoroutines: NcApiCoroutines): ChatNetworkDataSource {
|
||||
return RetrofitChatNetwork(ncApi, ncApiCoroutines)
|
||||
}
|
||||
|
||||
@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
|
||||
}
|
||||
|
||||
@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
|
||||
fun getUrlForFederatedAvatar(
|
||||
baseUrl: String,
|
||||
@ -601,4 +607,8 @@ object ApiUtils {
|
||||
fun getUrlForArchive(version: Int, baseUrl: String?, token: String?): String {
|
||||
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))
|
||||
}
|
||||
|
||||
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 {
|
||||
val difference = (time2 - time1)
|
||||
return abs(difference)
|
||||
|
@ -127,6 +127,18 @@
|
||||
|
||||
</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
|
||||
android:id="@+id/messagesListView"
|
||||
android:layout_width="match_parent"
|
||||
@ -134,6 +146,7 @@
|
||||
android:clipToPadding="false"
|
||||
android:paddingBottom="20dp"
|
||||
android:visibility="gone"
|
||||
android:layout_below= "@id/out_of_office_container"
|
||||
app:dateHeaderTextSize="13sp"
|
||||
app:incomingBubblePaddingBottom="@dimen/message_bubble_corners_vertical_padding"
|
||||
app:incomingBubblePaddingLeft="@dimen/message_bubble_corners_horizontal_padding"
|
||||
@ -182,7 +195,9 @@
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
app:background="@color/colorPrimary"
|
||||
android:clipToPadding="false"
|
||||
app:cornerRadius="@dimen/button_corner_radius"
|
||||
|
||||
app:icon="@drawable/ic_baseline_arrow_downward_24px" />
|
||||
|
||||
<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="status_reverted">Status Reverted</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>
|
||||
|
@ -24,7 +24,7 @@ buildscript {
|
||||
classpath 'com.android.tools.build:gradle:8.7.3'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}"
|
||||
classpath "org.jetbrains.kotlin:kotlin-serialization:${kotlinVersion}"
|
||||
classpath 'com.github.spotbugs.snom:spotbugs-gradle-plugin:6.0.26'
|
||||
classpath 'com.github.spotbugs.snom:spotbugs-gradle-plugin:6.0.27'
|
||||
classpath "io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.23.7"
|
||||
classpath "org.jlleitschuh.gradle:ktlint-gradle:12.1.2"
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
|
@ -149,6 +149,7 @@
|
||||
<trusted-key id="77A45740C23880C7F81B9D4D5C504E1210E49773">
|
||||
<trusting group="com.mebigfatguy.fb-contrib" name="fb-contrib" version="7.6.5"/>
|
||||
<trusting group="com.mebigfatguy.fb-contrib" name="fb-contrib" version="7.6.8"/>
|
||||
<trusting group="com.mebigfatguy.fb-contrib" name="fb-contrib" version="7.6.9"/>
|
||||
</trusted-key>
|
||||
<trusted-key id="7B121B76A7ED6CE6E60AD51784E913A8E3A748C0" group="org.bouncycastle"/>
|
||||
<trusted-key id="7E22D50A7EBD9D2CD269B2D4056ACA74D46000BF" group="io.netty"/>
|
||||
|
Loading…
Reference in New Issue
Block a user