mirror of
https://github.com/nextcloud/talk-android
synced 2025-06-19 11:39:42 +01:00
Merge pull request #2307 from nextcloud/feature/2281/expiringMessages
handle expiring messages
This commit is contained in:
commit
dc5e7f9f3c
@ -550,4 +550,10 @@ public interface NcApi {
|
|||||||
@DELETE
|
@DELETE
|
||||||
Observable<PollOverall> closePoll(@Header("Authorization") String authorization,
|
Observable<PollOverall> closePoll(@Header("Authorization") String authorization,
|
||||||
@Url String url);
|
@Url String url);
|
||||||
|
|
||||||
|
@FormUrlEncoded
|
||||||
|
@POST
|
||||||
|
Observable<GenericOverall> setMessageExpiration(@Header("Authorization") String authorization,
|
||||||
|
@Url String url,
|
||||||
|
@Field("seconds") Integer seconds);
|
||||||
}
|
}
|
||||||
|
@ -2216,6 +2216,8 @@ class ChatController(args: Bundle) :
|
|||||||
// since this is called asynchrously and UI might have been destroyed in the meantime
|
// since this is called asynchrously and UI might have been destroyed in the meantime
|
||||||
Log.i(TAG, "UI destroyed - view binding already gone")
|
Log.i(TAG, "UI destroyed - view binding already gone")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
processExpiredMessages()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onError(e: Throwable) {
|
override fun onError(e: Throwable) {
|
||||||
@ -2256,6 +2258,8 @@ class ChatController(args: Bundle) :
|
|||||||
// since this is called asynchrously and UI might have been destroyed in the meantime
|
// since this is called asynchrously and UI might have been destroyed in the meantime
|
||||||
Log.i(TAG, "UI destroyed - view binding already gone", e)
|
Log.i(TAG, "UI destroyed - view binding already gone", e)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
processExpiredMessages()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onError(e: Throwable) {
|
override fun onError(e: Throwable) {
|
||||||
@ -2270,6 +2274,28 @@ class ChatController(args: Bundle) :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun processExpiredMessages() {
|
||||||
|
fun deleteExpiredMessages() {
|
||||||
|
val messagesToDelete: ArrayList<ChatMessage> = ArrayList()
|
||||||
|
val systemTime = System.currentTimeMillis() / ONE_SECOND_IN_MILLIS
|
||||||
|
|
||||||
|
for (itemWrapper in adapter?.items!!) {
|
||||||
|
if (itemWrapper.item is ChatMessage) {
|
||||||
|
val chatMessage = itemWrapper.item as ChatMessage
|
||||||
|
if (chatMessage.expirationTimestamp != 0 && chatMessage.expirationTimestamp < systemTime) {
|
||||||
|
messagesToDelete.add(chatMessage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
adapter!!.delete(messagesToDelete)
|
||||||
|
adapter!!.notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "message-expiration")) {
|
||||||
|
deleteExpiredMessages()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun processMessages(response: Response<*>, isFromTheFuture: Boolean, timeout: Int) {
|
private fun processMessages(response: Response<*>, isFromTheFuture: Boolean, timeout: Int) {
|
||||||
val xChatLastGivenHeader: String? = response.headers().get("X-Chat-Last-Given")
|
val xChatLastGivenHeader: String? = response.headers().get("X-Chat-Last-Given")
|
||||||
val xChatLastCommonRead = response.headers().get("X-Chat-Last-Common-Read")?.let {
|
val xChatLastCommonRead = response.headers().get("X-Chat-Last-Common-Read")?.let {
|
||||||
@ -3252,5 +3278,6 @@ class ChatController(args: Bundle) :
|
|||||||
private const val RETRIES: Long = 3
|
private const val RETRIES: Long = 3
|
||||||
private const val LOOKING_INTO_FUTURE_TIMEOUT = 30
|
private const val LOOKING_INTO_FUTURE_TIMEOUT = 30
|
||||||
private const val CHUNK_SIZE: Int = 10
|
private const val CHUNK_SIZE: Int = 10
|
||||||
|
private const val ONE_SECOND_IN_MILLIS = 1000
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -204,6 +204,7 @@ class ConversationInfoController(args: Bundle) :
|
|||||||
participantsListCategory,
|
participantsListCategory,
|
||||||
ownOptions,
|
ownOptions,
|
||||||
categorySharedItems,
|
categorySharedItems,
|
||||||
|
categoryConversationSettings,
|
||||||
binding.webinarInfoView.conversationInfoWebinar,
|
binding.webinarInfoView.conversationInfoWebinar,
|
||||||
binding.notificationSettingsView.notificationSettingsCategory
|
binding.notificationSettingsView.notificationSettingsCategory
|
||||||
).forEach(viewThemeUtils::colorPreferenceCategory)
|
).forEach(viewThemeUtils::colorPreferenceCategory)
|
||||||
@ -677,6 +678,7 @@ class ConversationInfoController(args: Bundle) :
|
|||||||
|
|
||||||
loadConversationAvatar()
|
loadConversationAvatar()
|
||||||
adjustNotificationLevelUI()
|
adjustNotificationLevelUI()
|
||||||
|
initExpiringMessageOption()
|
||||||
|
|
||||||
binding.notificationSettingsView.notificationSettings.visibility = View.VISIBLE
|
binding.notificationSettingsView.notificationSettings.visibility = View.VISIBLE
|
||||||
}
|
}
|
||||||
@ -697,6 +699,19 @@ class ConversationInfoController(args: Bundle) :
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun initExpiringMessageOption() {
|
||||||
|
if (conversation!!.isParticipantOwnerOrModerator &&
|
||||||
|
CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "message-expiration")
|
||||||
|
) {
|
||||||
|
databaseStorageModule?.setMessageExpiration(conversation!!.messageExpiration)
|
||||||
|
binding.conversationInfoExpireMessages.setStorageModule(databaseStorageModule)
|
||||||
|
binding.conversationInfoExpireMessages.visibility = View.VISIBLE
|
||||||
|
binding.conversationInfoExpireMessagesExplanation.visibility = View.VISIBLE
|
||||||
|
} else {
|
||||||
|
binding.categoryConversationSettings.visibility = View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun adjustNotificationLevelUI() {
|
private fun adjustNotificationLevelUI() {
|
||||||
if (conversation != null) {
|
if (conversation != null) {
|
||||||
if (
|
if (
|
||||||
|
@ -112,6 +112,9 @@ data class ChatMessage(
|
|||||||
@JsonField(name = ["reactionsSelf"])
|
@JsonField(name = ["reactionsSelf"])
|
||||||
var reactionsSelf: ArrayList<String>? = null,
|
var reactionsSelf: ArrayList<String>? = null,
|
||||||
|
|
||||||
|
@JsonField(name = ["expirationTimestamp"])
|
||||||
|
var expirationTimestamp: Int = 0,
|
||||||
|
|
||||||
var isDownloadingVoiceMessage: Boolean = false,
|
var isDownloadingVoiceMessage: Boolean = false,
|
||||||
|
|
||||||
var resetVoiceMessage: Boolean = false,
|
var resetVoiceMessage: Boolean = false,
|
||||||
@ -141,11 +144,7 @@ data class ChatMessage(
|
|||||||
fun hasFileAttachment(): Boolean {
|
fun hasFileAttachment(): Boolean {
|
||||||
if (messageParameters != null && messageParameters!!.size > 0) {
|
if (messageParameters != null && messageParameters!!.size > 0) {
|
||||||
for ((_, individualHashMap) in messageParameters!!) {
|
for ((_, individualHashMap) in messageParameters!!) {
|
||||||
if (MessageDigest.isEqual(
|
if (isHashMapEntryEqualTo(individualHashMap, "type", "file")) {
|
||||||
individualHashMap["type"]!!.toByteArray(),
|
|
||||||
"file".toByteArray()
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -156,11 +155,7 @@ data class ChatMessage(
|
|||||||
fun hasGeoLocation(): Boolean {
|
fun hasGeoLocation(): Boolean {
|
||||||
if (messageParameters != null && messageParameters!!.size > 0) {
|
if (messageParameters != null && messageParameters!!.size > 0) {
|
||||||
for ((_, individualHashMap) in messageParameters!!) {
|
for ((_, individualHashMap) in messageParameters!!) {
|
||||||
if (MessageDigest.isEqual(
|
if (isHashMapEntryEqualTo(individualHashMap, "type", "geo-location")) {
|
||||||
individualHashMap["type"]!!.toByteArray(),
|
|
||||||
"geo-location".toByteArray()
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -171,11 +166,7 @@ data class ChatMessage(
|
|||||||
fun isPoll(): Boolean {
|
fun isPoll(): Boolean {
|
||||||
if (messageParameters != null && messageParameters!!.size > 0) {
|
if (messageParameters != null && messageParameters!!.size > 0) {
|
||||||
for ((_, individualHashMap) in messageParameters!!) {
|
for ((_, individualHashMap) in messageParameters!!) {
|
||||||
if (MessageDigest.isEqual(
|
if (isHashMapEntryEqualTo(individualHashMap, "type", "talk-poll")) {
|
||||||
individualHashMap["type"]!!.toByteArray(),
|
|
||||||
"talk-poll".toByteArray()
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -183,14 +174,11 @@ data class ChatMessage(
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("Detekt.NestedBlockDepth")
|
||||||
override fun getImageUrl(): String? {
|
override fun getImageUrl(): String? {
|
||||||
if (messageParameters != null && messageParameters!!.size > 0) {
|
if (messageParameters != null && messageParameters!!.size > 0) {
|
||||||
for ((_, individualHashMap) in messageParameters!!) {
|
for ((_, individualHashMap) in messageParameters!!) {
|
||||||
if (MessageDigest.isEqual(
|
if (isHashMapEntryEqualTo(individualHashMap, "type", "file")) {
|
||||||
individualHashMap["type"]!!.toByteArray(),
|
|
||||||
"file".toByteArray()
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
// FIX-ME: this selectedIndividualHashMap stuff needs to be analyzed and most likely be refactored!
|
// FIX-ME: this selectedIndividualHashMap stuff needs to be analyzed and most likely be refactored!
|
||||||
// it just feels wrong to fill this here inside getImageUrl()
|
// it just feels wrong to fill this here inside getImageUrl()
|
||||||
selectedIndividualHashMap = individualHashMap
|
selectedIndividualHashMap = individualHashMap
|
||||||
@ -420,6 +408,10 @@ data class ChatMessage(
|
|||||||
return EnumSystemMessageTypeConverter().convertToString(systemMessageType)
|
return EnumSystemMessageTypeConverter().convertToString(systemMessageType)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun isHashMapEntryEqualTo(map: HashMap<String?, String?>, key: String, searchTerm: String): Boolean {
|
||||||
|
return map != null && MessageDigest.isEqual(map[key]!!.toByteArray(), searchTerm.toByteArray())
|
||||||
|
}
|
||||||
|
|
||||||
val isVoiceMessage: Boolean
|
val isVoiceMessage: Boolean
|
||||||
get() = "voice-message" == messageType
|
get() = "voice-message" == messageType
|
||||||
val isCommandMessage: Boolean
|
val isCommandMessage: Boolean
|
||||||
@ -492,7 +484,9 @@ data class ChatMessage(
|
|||||||
REACTION_DELETED,
|
REACTION_DELETED,
|
||||||
REACTION_REVOKED,
|
REACTION_REVOKED,
|
||||||
POLL_VOTED,
|
POLL_VOTED,
|
||||||
POLL_CLOSED
|
POLL_CLOSED,
|
||||||
|
MESSAGE_EXPIRATION_ENABLED,
|
||||||
|
MESSAGE_EXPIRATION_DISABLED,
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -34,6 +34,7 @@ class ChatUtils {
|
|||||||
return message
|
return message
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("Detekt.ComplexMethod")
|
||||||
private fun parse(
|
private fun parse(
|
||||||
messageParameters: HashMap<String?, HashMap<String?, String?>>,
|
messageParameters: HashMap<String?, HashMap<String?, String?>>,
|
||||||
message: String?
|
message: String?
|
||||||
@ -41,19 +42,22 @@ class ChatUtils {
|
|||||||
var resultMessage = message
|
var resultMessage = message
|
||||||
for (key in messageParameters.keys) {
|
for (key in messageParameters.keys) {
|
||||||
val individualHashMap = messageParameters[key]
|
val individualHashMap = messageParameters[key]
|
||||||
val type = individualHashMap?.get("type")
|
|
||||||
if (type == "user" || type == "guest" || type == "call") {
|
if (individualHashMap != null) {
|
||||||
resultMessage = resultMessage?.replace("{$key}", "@" + individualHashMap["name"])
|
val type = individualHashMap["type"]
|
||||||
} else if (type == "geo-location") {
|
resultMessage = if (type == "user" || type == "guest" || type == "call") {
|
||||||
resultMessage = individualHashMap.get("name")
|
resultMessage?.replace("{$key}", "@" + individualHashMap["name"])
|
||||||
} else if (individualHashMap?.containsKey("link") == true) {
|
} else if (type == "geo-location") {
|
||||||
resultMessage = if (type == "file") {
|
individualHashMap["name"]
|
||||||
resultMessage?.replace("{$key}", individualHashMap["name"].toString())
|
} else if (individualHashMap?.containsKey("link") == true) {
|
||||||
|
if (type == "file") {
|
||||||
|
resultMessage?.replace("{$key}", individualHashMap["name"].toString())
|
||||||
|
} else {
|
||||||
|
individualHashMap["link"].toString()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
individualHashMap["link"].toString()
|
individualHashMap["name"]?.let { resultMessage?.replace("{$key}", it) }
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
resultMessage = individualHashMap?.get("name")?.let { resultMessage?.replace("{$key}", it) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return resultMessage
|
return resultMessage
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
* @author Mario Danic
|
* @author Mario Danic
|
||||||
* @author Tim Krüger
|
* @author Tim Krüger
|
||||||
* @author Andy Scherzinger
|
* @author Andy Scherzinger
|
||||||
|
* @author Marcel Hibbe
|
||||||
|
* Copyright (C) 2022 Marcel Hibbe <dev@mhibbe.de>
|
||||||
* Copyright (C) 2022 Andy Scherzinger <info@andy-scherzinger.de>
|
* Copyright (C) 2022 Andy Scherzinger <info@andy-scherzinger.de>
|
||||||
* Copyright (C) 2021 Tim Krüger <t@timkrueger.me>
|
* Copyright (C) 2021 Tim Krüger <t@timkrueger.me>
|
||||||
* Copyright (C) 2017 Mario Danic (mario@lovelyhq.com)
|
* Copyright (C) 2017 Mario Danic (mario@lovelyhq.com)
|
||||||
@ -122,7 +124,11 @@ data class Conversation(
|
|||||||
var notificationCalls: Int? = null,
|
var notificationCalls: Int? = null,
|
||||||
|
|
||||||
@JsonField(name = ["permissions"])
|
@JsonField(name = ["permissions"])
|
||||||
var permissions: Int = 0
|
var permissions: Int = 0,
|
||||||
|
|
||||||
|
@JsonField(name = ["messageExpiration"])
|
||||||
|
var messageExpiration: Int = 0
|
||||||
|
|
||||||
) : Parcelable {
|
) : Parcelable {
|
||||||
// This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject'
|
// This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject'
|
||||||
constructor() : this(null, null)
|
constructor() : this(null, null)
|
||||||
|
@ -60,6 +60,8 @@ import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.MATTERB
|
|||||||
import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.MATTERBRIDGE_CONFIG_ENABLED
|
import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.MATTERBRIDGE_CONFIG_ENABLED
|
||||||
import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.MATTERBRIDGE_CONFIG_REMOVED
|
import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.MATTERBRIDGE_CONFIG_REMOVED
|
||||||
import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.MESSAGE_DELETED
|
import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.MESSAGE_DELETED
|
||||||
|
import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.MESSAGE_EXPIRATION_DISABLED
|
||||||
|
import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.MESSAGE_EXPIRATION_ENABLED
|
||||||
import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.MODERATOR_DEMOTED
|
import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.MODERATOR_DEMOTED
|
||||||
import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.MODERATOR_PROMOTED
|
import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.MODERATOR_PROMOTED
|
||||||
import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.OBJECT_SHARED
|
import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.OBJECT_SHARED
|
||||||
@ -171,6 +173,8 @@ class EnumSystemMessageTypeConverter : StringBasedTypeConverter<ChatMessage.Syst
|
|||||||
"reaction_revoked" -> REACTION_REVOKED
|
"reaction_revoked" -> REACTION_REVOKED
|
||||||
"poll_voted" -> POLL_VOTED
|
"poll_voted" -> POLL_VOTED
|
||||||
"poll_closed" -> POLL_CLOSED
|
"poll_closed" -> POLL_CLOSED
|
||||||
|
"message_expiration_enabled" -> MESSAGE_EXPIRATION_ENABLED
|
||||||
|
"message_expiration_disabled" -> MESSAGE_EXPIRATION_DISABLED
|
||||||
else -> DUMMY
|
else -> DUMMY
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -226,6 +230,8 @@ class EnumSystemMessageTypeConverter : StringBasedTypeConverter<ChatMessage.Syst
|
|||||||
REACTION_REVOKED -> "reaction_revoked"
|
REACTION_REVOKED -> "reaction_revoked"
|
||||||
POLL_VOTED -> "poll_voted"
|
POLL_VOTED -> "poll_voted"
|
||||||
POLL_CLOSED -> "poll_closed"
|
POLL_CLOSED -> "poll_closed"
|
||||||
|
MESSAGE_EXPIRATION_ENABLED -> "message_expiration_enabled"
|
||||||
|
MESSAGE_EXPIRATION_DISABLED -> "message_expiration_disabled"
|
||||||
else -> ""
|
else -> ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -147,7 +147,7 @@ public class ApiUtils {
|
|||||||
return version;
|
return version;
|
||||||
}
|
}
|
||||||
if (version == APIv1 &&
|
if (version == APIv1 &&
|
||||||
user.hasSpreedFeatureCapability( "mention-flag") &&
|
user.hasSpreedFeatureCapability("mention-flag") &&
|
||||||
!user.hasSpreedFeatureCapability("conversation-v4")) {
|
!user.hasSpreedFeatureCapability("conversation-v4")) {
|
||||||
return version;
|
return version;
|
||||||
}
|
}
|
||||||
@ -479,4 +479,8 @@ public class ApiUtils {
|
|||||||
return baseUrl + ocsApiVersion + spreedApiVersion + "/poll/" + roomToken;
|
return baseUrl + ocsApiVersion + spreedApiVersion + "/poll/" + roomToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getUrlForMessageExpiration(int version, String baseUrl, String token) {
|
||||||
|
return getUrlForRoom(version, baseUrl, token) + "/message-expiration";
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -60,6 +60,7 @@ public class DatabaseStorageModule implements StorageModule {
|
|||||||
@Inject
|
@Inject
|
||||||
NcApi ncApi;
|
NcApi ncApi;
|
||||||
|
|
||||||
|
private int messageExpiration;
|
||||||
private final User conversationUser;
|
private final User conversationUser;
|
||||||
private final String conversationToken;
|
private final String conversationToken;
|
||||||
private final long accountIdentifier;
|
private final long accountIdentifier;
|
||||||
@ -78,7 +79,7 @@ public class DatabaseStorageModule implements StorageModule {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void saveBoolean(String key, boolean value) {
|
public void saveBoolean(String key, boolean value) {
|
||||||
if(key.equals("call_notifications")) {
|
if (key.equals("call_notifications")) {
|
||||||
int apiVersion = ApiUtils.getConversationApiVersion(conversationUser, new int[]{4});
|
int apiVersion = ApiUtils.getConversationApiVersion(conversationUser, new int[]{4});
|
||||||
ncApi.notificationCalls(ApiUtils.getCredentials(conversationUser.getUsername(),
|
ncApi.notificationCalls(ApiUtils.getCredentials(conversationUser.getUsername(),
|
||||||
conversationUser.getToken()),
|
conversationUser.getToken()),
|
||||||
@ -89,27 +90,27 @@ public class DatabaseStorageModule implements StorageModule {
|
|||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe(new Observer<GenericOverall>() {
|
.subscribe(new Observer<GenericOverall>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSubscribe(@NotNull Disposable d) {
|
public void onSubscribe(@NotNull Disposable d) {
|
||||||
// unused atm
|
// unused atm
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onNext(@NotNull GenericOverall genericOverall) {
|
public void onNext(@NotNull GenericOverall genericOverall) {
|
||||||
Log.d(TAG, "Toggled notification calls");
|
Log.d(TAG, "Toggled notification calls");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError(@NotNull Throwable e) {
|
public void onError(@NotNull Throwable e) {
|
||||||
Log.e(TAG, "Error when trying to toggle notification calls", e);
|
Log.e(TAG, "Error when trying to toggle notification calls", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onComplete() {
|
public void onComplete() {
|
||||||
// unused atm
|
// unused atm
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!key.equals("conversation_lobby")) {
|
if (!key.equals("conversation_lobby")) {
|
||||||
@ -124,9 +125,46 @@ public class DatabaseStorageModule implements StorageModule {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void saveString(String key, String value) {
|
public void saveString(String key, String value) {
|
||||||
if (!key.equals("message_notification_level")) {
|
if (key.equals("message_expire_key")) {
|
||||||
arbitraryStorageManager.storeStorageSetting(accountIdentifier, key, value, conversationToken);
|
int apiVersion = ApiUtils.getConversationApiVersion(conversationUser, new int[]{4});
|
||||||
} else {
|
|
||||||
|
String trimmedValue = value.replace("expire_", "");
|
||||||
|
int valueInt = Integer.parseInt(trimmedValue);
|
||||||
|
|
||||||
|
ncApi.setMessageExpiration(
|
||||||
|
ApiUtils.getCredentials(
|
||||||
|
conversationUser.getUsername(),
|
||||||
|
conversationUser.getToken()),
|
||||||
|
ApiUtils.getUrlForMessageExpiration(
|
||||||
|
apiVersion,
|
||||||
|
conversationUser.getBaseUrl(),
|
||||||
|
conversationToken),
|
||||||
|
valueInt)
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(new Observer<GenericOverall>() {
|
||||||
|
@Override
|
||||||
|
public void onSubscribe(@NotNull Disposable d) {
|
||||||
|
// unused atm
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNext(@NotNull GenericOverall genericOverall) {
|
||||||
|
messageExpiration = valueInt;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(@NotNull Throwable e) {
|
||||||
|
Log.e(TAG, "Error when trying to set message expiration", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onComplete() {
|
||||||
|
// unused atm
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
} else if (key.equals("message_notification_level")) {
|
||||||
if (CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "notification-levels")) {
|
if (CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "notification-levels")) {
|
||||||
if (!TextUtils.isEmpty(messageNotificationLevel) && !messageNotificationLevel.equals(value)) {
|
if (!TextUtils.isEmpty(messageNotificationLevel) && !messageNotificationLevel.equals(value)) {
|
||||||
int intValue;
|
int intValue;
|
||||||
@ -144,40 +182,42 @@ public class DatabaseStorageModule implements StorageModule {
|
|||||||
intValue = 0;
|
intValue = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int apiVersion = ApiUtils.getConversationApiVersion(conversationUser, new int[] {ApiUtils.APIv4, 1});
|
int apiVersion = ApiUtils.getConversationApiVersion(conversationUser, new int[]{ApiUtils.APIv4, 1});
|
||||||
|
|
||||||
ncApi.setNotificationLevel(ApiUtils.getCredentials(conversationUser.getUsername(),
|
ncApi.setNotificationLevel(ApiUtils.getCredentials(conversationUser.getUsername(),
|
||||||
conversationUser.getToken()),
|
conversationUser.getToken()),
|
||||||
ApiUtils.getUrlForRoomNotificationLevel(apiVersion,
|
ApiUtils.getUrlForRoomNotificationLevel(apiVersion,
|
||||||
conversationUser.getBaseUrl(),
|
conversationUser.getBaseUrl(),
|
||||||
conversationToken),
|
conversationToken),
|
||||||
intValue)
|
intValue)
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.subscribe(new Observer<GenericOverall>() {
|
.subscribe(new Observer<GenericOverall>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSubscribe(Disposable d) {
|
public void onSubscribe(Disposable d) {
|
||||||
// unused atm
|
// unused atm
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onNext(GenericOverall genericOverall) {
|
public void onNext(GenericOverall genericOverall) {
|
||||||
messageNotificationLevel = value;
|
messageNotificationLevel = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError(Throwable e) {
|
public void onError(Throwable e) {
|
||||||
// unused atm
|
// unused atm
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onComplete() {
|
public void onComplete() {
|
||||||
// unused atm
|
// unused atm
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
messageNotificationLevel = value;
|
messageNotificationLevel = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
arbitraryStorageManager.storeStorageSetting(accountIdentifier, key, value, conversationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,7 +245,22 @@ public class DatabaseStorageModule implements StorageModule {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getString(String key, String defaultVal) {
|
public String getString(String key, String defaultVal) {
|
||||||
if (key.equals("message_notification_level")) {
|
if (key.equals("message_expire_key")) {
|
||||||
|
switch (messageExpiration) {
|
||||||
|
case 2419200:
|
||||||
|
return "expire_2419200";
|
||||||
|
case 604800:
|
||||||
|
return "expire_604800";
|
||||||
|
case 86400:
|
||||||
|
return "expire_86400";
|
||||||
|
case 28800:
|
||||||
|
return "expire_28800";
|
||||||
|
case 3600:
|
||||||
|
return "expire_3600";
|
||||||
|
default:
|
||||||
|
return "expire_0";
|
||||||
|
}
|
||||||
|
} else if (key.equals("message_notification_level")) {
|
||||||
return messageNotificationLevel;
|
return messageNotificationLevel;
|
||||||
} else {
|
} else {
|
||||||
return arbitraryStorageManager
|
return arbitraryStorageManager
|
||||||
@ -243,4 +298,8 @@ public class DatabaseStorageModule implements StorageModule {
|
|||||||
public void onRestoreInstanceState(Bundle savedState) {
|
public void onRestoreInstanceState(Bundle savedState) {
|
||||||
// unused atm
|
// unused atm
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setMessageExpiration(int messageExpiration) {
|
||||||
|
this.messageExpiration = messageExpiration;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
~
|
~
|
||||||
~ @author Mario Danic
|
~ @author Mario Danic
|
||||||
~ @author Andy Scherzinger
|
~ @author Andy Scherzinger
|
||||||
|
~ @author Marcel Hibbe
|
||||||
|
~ Copyright (C) 2022 Marcel Hibbe <dev@mhibbe.de>
|
||||||
~ Copyright (C) 2021 Andy Scherzinger <info@andy-scherzinger.de>
|
~ Copyright (C) 2021 Andy Scherzinger <info@andy-scherzinger.de>
|
||||||
~ Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
|
~ Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
|
||||||
~
|
~
|
||||||
@ -45,9 +47,10 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
<RelativeLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
<com.yarolegovich.mp.MaterialPreferenceCategory
|
<com.yarolegovich.mp.MaterialPreferenceCategory
|
||||||
android:id="@+id/conversation_info_name"
|
android:id="@+id/conversation_info_name"
|
||||||
@ -87,7 +90,6 @@
|
|||||||
android:id="@+id/conversation_description"
|
android:id="@+id/conversation_description"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_below="@id/conversation_info_name"
|
|
||||||
android:animateLayoutChanges="true"
|
android:animateLayoutChanges="true"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
apc:cardBackgroundColor="@color/bg_default"
|
apc:cardBackgroundColor="@color/bg_default"
|
||||||
@ -109,7 +111,6 @@
|
|||||||
android:id="@+id/otherRoomOptions"
|
android:id="@+id/otherRoomOptions"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_below="@id/conversation_description"
|
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
apc:cardBackgroundColor="@color/bg_default"
|
apc:cardBackgroundColor="@color/bg_default"
|
||||||
apc:cardElevation="0dp"
|
apc:cardElevation="0dp"
|
||||||
@ -127,11 +128,87 @@
|
|||||||
|
|
||||||
</com.yarolegovich.mp.MaterialPreferenceCategory>
|
</com.yarolegovich.mp.MaterialPreferenceCategory>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/settings"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/notification_settings_view"
|
||||||
|
layout="@layout/notification_settings_item"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/webinar_info_view"
|
||||||
|
layout="@layout/webinar_info_item"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<com.yarolegovich.mp.MaterialPreferenceCategory
|
||||||
|
android:id="@+id/category_shared_items"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:animateLayoutChanges="true"
|
||||||
|
apc:cardBackgroundColor="@color/bg_default"
|
||||||
|
apc:cardElevation="0dp"
|
||||||
|
apc:mpc_title="@string/nc_shared_items">
|
||||||
|
|
||||||
|
<com.yarolegovich.mp.MaterialStandardPreference
|
||||||
|
android:id="@+id/show_shared_items_action"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
apc:mp_icon="@drawable/ic_folder_multiple_image"
|
||||||
|
apc:mp_icon_tint="@color/grey_600"
|
||||||
|
apc:mp_title="@string/nc_shared_items_description" />
|
||||||
|
|
||||||
|
</com.yarolegovich.mp.MaterialPreferenceCategory>
|
||||||
|
|
||||||
|
<com.yarolegovich.mp.MaterialPreferenceCategory
|
||||||
|
android:id="@+id/category_conversation_settings"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:animateLayoutChanges="true"
|
||||||
|
apc:cardBackgroundColor="@color/bg_default"
|
||||||
|
apc:cardElevation="0dp"
|
||||||
|
apc:mpc_title="@string/nc_conversation_settings">
|
||||||
|
|
||||||
|
<com.yarolegovich.mp.MaterialChoicePreference
|
||||||
|
android:id="@+id/conversation_info_expire_messages"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="gone"
|
||||||
|
apc:mp_entry_descriptions="@array/message_expiring_descriptions"
|
||||||
|
apc:mp_entry_values="@array/message_expiring_values"
|
||||||
|
apc:mp_key="message_expire_key"
|
||||||
|
apc:mp_show_value="onBottom"
|
||||||
|
apc:mp_title="@string/nc_expire_messages">
|
||||||
|
</com.yarolegovich.mp.MaterialChoicePreference>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/conversation_info_expire_messages_explanation"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:layout_marginStart="@dimen/standard_margin"
|
||||||
|
android:layout_marginEnd="@dimen/standard_margin"
|
||||||
|
android:textColor="@color/disabled_text"
|
||||||
|
android:text="@string/nc_expire_messages_explanation">
|
||||||
|
</TextView>
|
||||||
|
|
||||||
|
</com.yarolegovich.mp.MaterialPreferenceCategory>
|
||||||
|
|
||||||
<com.yarolegovich.mp.MaterialPreferenceCategory
|
<com.yarolegovich.mp.MaterialPreferenceCategory
|
||||||
android:id="@+id/participants_list_category"
|
android:id="@+id/participants_list_category"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_below="@+id/category_shared_items"
|
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
apc:cardBackgroundColor="@color/bg_default"
|
apc:cardBackgroundColor="@color/bg_default"
|
||||||
apc:cardElevation="0dp"
|
apc:cardElevation="0dp"
|
||||||
@ -159,7 +236,6 @@
|
|||||||
android:id="@+id/ownOptions"
|
android:id="@+id/ownOptions"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_below="@id/participants_list_category"
|
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
apc:cardBackgroundColor="@color/bg_default"
|
apc:cardBackgroundColor="@color/bg_default"
|
||||||
apc:cardElevation="0dp"
|
apc:cardElevation="0dp"
|
||||||
@ -191,52 +267,6 @@
|
|||||||
|
|
||||||
</com.yarolegovich.mp.MaterialPreferenceCategory>
|
</com.yarolegovich.mp.MaterialPreferenceCategory>
|
||||||
|
|
||||||
<LinearLayout
|
</LinearLayout>
|
||||||
android:id="@+id/settings"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_below="@id/otherRoomOptions"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<include
|
|
||||||
android:id="@+id/notification_settings_view"
|
|
||||||
layout="@layout/notification_settings_item"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:visibility="gone"
|
|
||||||
tools:visibility="gone" />
|
|
||||||
|
|
||||||
<include
|
|
||||||
android:id="@+id/webinar_info_view"
|
|
||||||
layout="@layout/webinar_info_item"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:visibility="gone"
|
|
||||||
tools:visibility="visible" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
|
|
||||||
<com.yarolegovich.mp.MaterialPreferenceCategory
|
|
||||||
android:id="@+id/category_shared_items"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_below="@id/settings"
|
|
||||||
android:animateLayoutChanges="true"
|
|
||||||
apc:cardBackgroundColor="@color/bg_default"
|
|
||||||
apc:cardElevation="0dp"
|
|
||||||
apc:mpc_title="@string/nc_shared_items">
|
|
||||||
|
|
||||||
<com.yarolegovich.mp.MaterialStandardPreference
|
|
||||||
android:id="@+id/show_shared_items_action"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
apc:mp_icon="@drawable/ic_folder_multiple_image"
|
|
||||||
apc:mp_icon_tint="@color/grey_600"
|
|
||||||
apc:mp_title="@string/nc_shared_items_description" />
|
|
||||||
|
|
||||||
</com.yarolegovich.mp.MaterialPreferenceCategory>
|
|
||||||
|
|
||||||
</RelativeLayout>
|
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
~ Nextcloud Talk application
|
~ Nextcloud Talk application
|
||||||
~
|
~
|
||||||
~ @author Mario Danic
|
~ @author Mario Danic
|
||||||
|
~ @author Marcel Hibbe
|
||||||
|
~ Copyright (C) 2022 Marcel Hibbe <dev@mhibbe.de>
|
||||||
~ Copyright (C) 2017 Mario Danic <mario@lovelyhq.com>
|
~ Copyright (C) 2017 Mario Danic <mario@lovelyhq.com>
|
||||||
~
|
~
|
||||||
~ This program is free software: you can redistribute it and/or modify
|
~ This program is free software: you can redistribute it and/or modify
|
||||||
@ -39,6 +41,24 @@
|
|||||||
<item>always</item>
|
<item>always</item>
|
||||||
</array>
|
</array>
|
||||||
|
|
||||||
|
<array name="message_expiring_descriptions">
|
||||||
|
<item>@string/nc_expire_message_off</item>
|
||||||
|
<item>@string/nc_expire_message_four_weeks</item>
|
||||||
|
<item>@string/nc_expire_message_one_week</item>
|
||||||
|
<item>@string/nc_expire_message_one_day</item>
|
||||||
|
<item>@string/nc_expire_message_eight_hours</item>
|
||||||
|
<item>@string/nc_expire_message_one_hour</item>
|
||||||
|
</array>
|
||||||
|
|
||||||
|
<array name="message_expiring_values">
|
||||||
|
<item>expire_0</item>
|
||||||
|
<item>expire_2419200</item>
|
||||||
|
<item>expire_604800</item>
|
||||||
|
<item>expire_86400</item>
|
||||||
|
<item>expire_28800</item>
|
||||||
|
<item>expire_3600</item>
|
||||||
|
</array>
|
||||||
|
|
||||||
<array name="screen_lock_timeout_descriptions">
|
<array name="screen_lock_timeout_descriptions">
|
||||||
<item>@string/nc_screen_lock_timeout_30</item>
|
<item>@string/nc_screen_lock_timeout_30</item>
|
||||||
<item>@string/nc_screen_lock_timeout_60</item>
|
<item>@string/nc_screen_lock_timeout_60</item>
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
~
|
~
|
||||||
~ @author Mario Danic
|
~ @author Mario Danic
|
||||||
~ @author Andy Scherzinger
|
~ @author Andy Scherzinger
|
||||||
|
~ @author Marcel Hibbe
|
||||||
|
~ Copyright (C) 2022 Marcel Hibbe <dev@mhibbe.de>
|
||||||
~ Copyright (C) 2021 Andy Scherzinger <info@andy-scherzinger.de>
|
~ Copyright (C) 2021 Andy Scherzinger <info@andy-scherzinger.de>
|
||||||
~ Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
|
~ Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
|
||||||
~
|
~
|
||||||
@ -564,4 +566,17 @@
|
|||||||
<string name="call_without_notification">Call without notification</string>
|
<string name="call_without_notification">Call without notification</string>
|
||||||
<string name="set_avatar_from_camera">Set avatar from camera</string>
|
<string name="set_avatar_from_camera">Set avatar from camera</string>
|
||||||
|
|
||||||
|
<string name="nc_conversation_settings">Conversation settings</string>
|
||||||
|
|
||||||
|
<!-- Expiring messages -->
|
||||||
|
<string name="nc_expire_messages">Expire chat messages</string>
|
||||||
|
<string name="nc_expire_message_off">Off</string>
|
||||||
|
<string name="nc_expire_message_four_weeks">4 weeks</string>
|
||||||
|
<string name="nc_expire_message_one_week">1 week</string>
|
||||||
|
<string name="nc_expire_message_one_day">1 day</string>
|
||||||
|
<string name="nc_expire_message_eight_hours">8 hours</string>
|
||||||
|
<string name="nc_expire_message_one_hour">1 hour</string>
|
||||||
|
<string name="nc_expire_messages_explanation">Expire chat messages after a certain time. Files shared into the chat will only be unshared from the conversation but are not deleted for the owner.</string>
|
||||||
|
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
Loading…
Reference in New Issue
Block a user