Эх сурвалжийг харах

Merge pull request #2307 from nextcloud/feature/2281/expiringMessages

handle expiring messages
Marcel Hibbe 2 жил өмнө
parent
commit
dc5e7f9f3c

+ 6 - 0
app/src/main/java/com/nextcloud/talk/api/NcApi.java

@@ -550,4 +550,10 @@ public interface NcApi {
     @DELETE
     Observable<PollOverall> closePoll(@Header("Authorization") String authorization,
                                       @Url String url);
+
+    @FormUrlEncoded
+    @POST
+    Observable<GenericOverall> setMessageExpiration(@Header("Authorization") String authorization,
+                                     @Url String url,
+                                     @Field("seconds") Integer seconds);
 }

+ 27 - 0
app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt

@@ -2216,6 +2216,8 @@ class ChatController(args: Bundle) :
                             // since this is called asynchrously and UI might have been destroyed in the meantime
                             Log.i(TAG, "UI destroyed - view binding already gone")
                         }
+
+                        processExpiredMessages()
                     }
 
                     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
                             Log.i(TAG, "UI destroyed - view binding already gone", e)
                         }
+
+                        processExpiredMessages()
                     }
 
                     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) {
         val xChatLastGivenHeader: String? = response.headers().get("X-Chat-Last-Given")
         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 LOOKING_INTO_FUTURE_TIMEOUT = 30
         private const val CHUNK_SIZE: Int = 10
+        private const val ONE_SECOND_IN_MILLIS = 1000
     }
 }

+ 15 - 0
app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt

@@ -204,6 +204,7 @@ class ConversationInfoController(args: Bundle) :
                 participantsListCategory,
                 ownOptions,
                 categorySharedItems,
+                categoryConversationSettings,
                 binding.webinarInfoView.conversationInfoWebinar,
                 binding.notificationSettingsView.notificationSettingsCategory
             ).forEach(viewThemeUtils::colorPreferenceCategory)
@@ -677,6 +678,7 @@ class ConversationInfoController(args: Bundle) :
 
                             loadConversationAvatar()
                             adjustNotificationLevelUI()
+                            initExpiringMessageOption()
 
                             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() {
         if (conversation != null) {
             if (

+ 15 - 21
app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.kt

@@ -112,6 +112,9 @@ data class ChatMessage(
     @JsonField(name = ["reactionsSelf"])
     var reactionsSelf: ArrayList<String>? = null,
 
+    @JsonField(name = ["expirationTimestamp"])
+    var expirationTimestamp: Int = 0,
+
     var isDownloadingVoiceMessage: Boolean = false,
 
     var resetVoiceMessage: Boolean = false,
@@ -141,11 +144,7 @@ data class ChatMessage(
     fun hasFileAttachment(): Boolean {
         if (messageParameters != null && messageParameters!!.size > 0) {
             for ((_, individualHashMap) in messageParameters!!) {
-                if (MessageDigest.isEqual(
-                        individualHashMap["type"]!!.toByteArray(),
-                        "file".toByteArray()
-                    )
-                ) {
+                if (isHashMapEntryEqualTo(individualHashMap, "type", "file")) {
                     return true
                 }
             }
@@ -156,11 +155,7 @@ data class ChatMessage(
     fun hasGeoLocation(): Boolean {
         if (messageParameters != null && messageParameters!!.size > 0) {
             for ((_, individualHashMap) in messageParameters!!) {
-                if (MessageDigest.isEqual(
-                        individualHashMap["type"]!!.toByteArray(),
-                        "geo-location".toByteArray()
-                    )
-                ) {
+                if (isHashMapEntryEqualTo(individualHashMap, "type", "geo-location")) {
                     return true
                 }
             }
@@ -171,11 +166,7 @@ data class ChatMessage(
     fun isPoll(): Boolean {
         if (messageParameters != null && messageParameters!!.size > 0) {
             for ((_, individualHashMap) in messageParameters!!) {
-                if (MessageDigest.isEqual(
-                        individualHashMap["type"]!!.toByteArray(),
-                        "talk-poll".toByteArray()
-                    )
-                ) {
+                if (isHashMapEntryEqualTo(individualHashMap, "type", "talk-poll")) {
                     return true
                 }
             }
@@ -183,14 +174,11 @@ data class ChatMessage(
         return false
     }
 
+    @Suppress("Detekt.NestedBlockDepth")
     override fun getImageUrl(): String? {
         if (messageParameters != null && messageParameters!!.size > 0) {
             for ((_, individualHashMap) in messageParameters!!) {
-                if (MessageDigest.isEqual(
-                        individualHashMap["type"]!!.toByteArray(),
-                        "file".toByteArray()
-                    )
-                ) {
+                if (isHashMapEntryEqualTo(individualHashMap, "type", "file")) {
                     // FIX-ME: this selectedIndividualHashMap stuff needs to be analyzed and most likely be refactored!
                     //  it just feels wrong to fill this here inside getImageUrl()
                     selectedIndividualHashMap = individualHashMap
@@ -420,6 +408,10 @@ data class ChatMessage(
         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
         get() = "voice-message" == messageType
     val isCommandMessage: Boolean
@@ -492,7 +484,9 @@ data class ChatMessage(
         REACTION_DELETED,
         REACTION_REVOKED,
         POLL_VOTED,
-        POLL_CLOSED
+        POLL_CLOSED,
+        MESSAGE_EXPIRATION_ENABLED,
+        MESSAGE_EXPIRATION_DISABLED,
     }
 
     companion object {

+ 15 - 11
app/src/main/java/com/nextcloud/talk/models/json/chat/ChatUtils.kt

@@ -34,6 +34,7 @@ class ChatUtils {
             return message
         }
 
+        @Suppress("Detekt.ComplexMethod")
         private fun parse(
             messageParameters: HashMap<String?, HashMap<String?, String?>>,
             message: String?
@@ -41,19 +42,22 @@ class ChatUtils {
             var resultMessage = message
             for (key in messageParameters.keys) {
                 val individualHashMap = messageParameters[key]
-                val type = individualHashMap?.get("type")
-                if (type == "user" || type == "guest" || type == "call") {
-                    resultMessage = resultMessage?.replace("{$key}", "@" + individualHashMap["name"])
-                } else if (type == "geo-location") {
-                    resultMessage = individualHashMap.get("name")
-                } else if (individualHashMap?.containsKey("link") == true) {
-                    resultMessage = if (type == "file") {
-                        resultMessage?.replace("{$key}", individualHashMap["name"].toString())
+
+                if (individualHashMap != null) {
+                    val type = individualHashMap["type"]
+                    resultMessage = if (type == "user" || type == "guest" || type == "call") {
+                        resultMessage?.replace("{$key}", "@" + individualHashMap["name"])
+                    } else if (type == "geo-location") {
+                        individualHashMap["name"]
+                    } else if (individualHashMap?.containsKey("link") == true) {
+                        if (type == "file") {
+                            resultMessage?.replace("{$key}", individualHashMap["name"].toString())
+                        } else {
+                            individualHashMap["link"].toString()
+                        }
                     } else {
-                        individualHashMap["link"].toString()
+                        individualHashMap["name"]?.let { resultMessage?.replace("{$key}", it) }
                     }
-                } else {
-                    resultMessage = individualHashMap?.get("name")?.let { resultMessage?.replace("{$key}", it) }
                 }
             }
             return resultMessage

+ 7 - 1
app/src/main/java/com/nextcloud/talk/models/json/conversations/Conversation.kt

@@ -4,6 +4,8 @@
  * @author Mario Danic
  * @author Tim Krüger
  * @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) 2021 Tim Krüger <t@timkrueger.me>
  * Copyright (C) 2017 Mario Danic (mario@lovelyhq.com)
@@ -122,7 +124,11 @@ data class Conversation(
     var notificationCalls: Int? = null,
 
     @JsonField(name = ["permissions"])
-    var permissions: Int = 0
+    var permissions: Int = 0,
+
+    @JsonField(name = ["messageExpiration"])
+    var messageExpiration: Int = 0
+
 ) : Parcelable {
     // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject'
     constructor() : this(null, null)

+ 6 - 0
app/src/main/java/com/nextcloud/talk/models/json/converters/EnumSystemMessageTypeConverter.kt

@@ -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_REMOVED
 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_PROMOTED
 import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.OBJECT_SHARED
@@ -171,6 +173,8 @@ class EnumSystemMessageTypeConverter : StringBasedTypeConverter<ChatMessage.Syst
             "reaction_revoked" -> REACTION_REVOKED
             "poll_voted" -> POLL_VOTED
             "poll_closed" -> POLL_CLOSED
+            "message_expiration_enabled" -> MESSAGE_EXPIRATION_ENABLED
+            "message_expiration_disabled" -> MESSAGE_EXPIRATION_DISABLED
             else -> DUMMY
         }
     }
@@ -226,6 +230,8 @@ class EnumSystemMessageTypeConverter : StringBasedTypeConverter<ChatMessage.Syst
             REACTION_REVOKED -> "reaction_revoked"
             POLL_VOTED -> "poll_voted"
             POLL_CLOSED -> "poll_closed"
+            MESSAGE_EXPIRATION_ENABLED -> "message_expiration_enabled"
+            MESSAGE_EXPIRATION_DISABLED -> "message_expiration_disabled"
             else -> ""
         }
     }

+ 5 - 1
app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java

@@ -147,7 +147,7 @@ public class ApiUtils {
                     return version;
                 }
                 if (version == APIv1 &&
-                    user.hasSpreedFeatureCapability( "mention-flag") &&
+                    user.hasSpreedFeatureCapability("mention-flag") &&
                     !user.hasSpreedFeatureCapability("conversation-v4")) {
                     return version;
                 }
@@ -479,4 +479,8 @@ public class ApiUtils {
         return baseUrl + ocsApiVersion + spreedApiVersion + "/poll/" + roomToken;
     }
 
+    public static String getUrlForMessageExpiration(int version, String baseUrl, String token) {
+        return getUrlForRoom(version, baseUrl, token) + "/message-expiration";
+    }
+
 }

+ 108 - 49
app/src/main/java/com/nextcloud/talk/utils/preferences/preferencestorage/DatabaseStorageModule.java

@@ -60,6 +60,7 @@ public class DatabaseStorageModule implements StorageModule {
     @Inject
     NcApi ncApi;
 
+    private int messageExpiration;
     private final User conversationUser;
     private final String conversationToken;
     private final long accountIdentifier;
@@ -78,7 +79,7 @@ public class DatabaseStorageModule implements StorageModule {
 
     @Override
     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});
             ncApi.notificationCalls(ApiUtils.getCredentials(conversationUser.getUsername(),
                                                             conversationUser.getToken()),
@@ -88,6 +89,59 @@ public class DatabaseStorageModule implements StorageModule {
                                     value ? 1 : 0)
                 .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) {
+                                   Log.d(TAG, "Toggled notification calls");
+                               }
+
+                               @Override
+                               public void onError(@NotNull Throwable e) {
+                                   Log.e(TAG, "Error when trying to toggle notification calls", e);
+                               }
+
+                               @Override
+                               public void onComplete() {
+                                   // unused atm
+                               }
+                           }
+                          );
+        }
+
+        if (!key.equals("conversation_lobby")) {
+            arbitraryStorageManager.storeStorageSetting(accountIdentifier,
+                                                        key,
+                                                        Boolean.toString(value),
+                                                        conversationToken);
+        } else {
+            lobbyValue = value;
+        }
+    }
+
+    @Override
+    public void saveString(String key, String value) {
+        if (key.equals("message_expire_key")) {
+            int apiVersion = ApiUtils.getConversationApiVersion(conversationUser, new int[]{4});
+
+            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) {
@@ -96,37 +150,21 @@ public class DatabaseStorageModule implements StorageModule {
 
                     @Override
                     public void onNext(@NotNull GenericOverall genericOverall) {
-                        Log.d(TAG, "Toggled notification calls");
+                        messageExpiration = valueInt;
                     }
 
                     @Override
                     public void onError(@NotNull Throwable e) {
-                        Log.e(TAG, "Error when trying to toggle notification calls", e);
+                        Log.e(TAG, "Error when trying to set message expiration", e);
                     }
 
                     @Override
                     public void onComplete() {
                         // unused atm
                     }
-                }
-            );
-        }
-
-        if (!key.equals("conversation_lobby")) {
-            arbitraryStorageManager.storeStorageSetting(accountIdentifier,
-                                                        key,
-                                                        Boolean.toString(value),
-                                                        conversationToken);
-        } else {
-            lobbyValue = value;
-        }
-    }
+                });
 
-    @Override
-    public void saveString(String key, String value) {
-        if (!key.equals("message_notification_level")) {
-            arbitraryStorageManager.storeStorageSetting(accountIdentifier, key, value, conversationToken);
-        } else {
+        } else if (key.equals("message_notification_level")) {
             if (CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "notification-levels")) {
                 if (!TextUtils.isEmpty(messageNotificationLevel) && !messageNotificationLevel.equals(value)) {
                     int intValue;
@@ -144,40 +182,42 @@ public class DatabaseStorageModule implements StorageModule {
                             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(),
                                                                        conversationUser.getToken()),
-                            ApiUtils.getUrlForRoomNotificationLevel(apiVersion,
-                                                                    conversationUser.getBaseUrl(),
-                                                                    conversationToken),
-                            intValue)
-                            .subscribeOn(Schedulers.io())
-                            .subscribe(new Observer<GenericOverall>() {
-                                @Override
-                                public void onSubscribe(Disposable d) {
-                                    // unused atm
-                                }
-
-                                @Override
-                                public void onNext(GenericOverall genericOverall) {
-                                    messageNotificationLevel = value;
-                                }
-
-                                @Override
-                                public void onError(Throwable e) {
-                                    // unused atm
-                                }
-
-                                @Override
-                                public void onComplete() {
-                                    // unused atm
-                                }
-                            });
+                                               ApiUtils.getUrlForRoomNotificationLevel(apiVersion,
+                                                                                       conversationUser.getBaseUrl(),
+                                                                                       conversationToken),
+                                               intValue)
+                        .subscribeOn(Schedulers.io())
+                        .subscribe(new Observer<GenericOverall>() {
+                            @Override
+                            public void onSubscribe(Disposable d) {
+                                // unused atm
+                            }
+
+                            @Override
+                            public void onNext(GenericOverall genericOverall) {
+                                messageNotificationLevel = value;
+                            }
+
+                            @Override
+                            public void onError(Throwable e) {
+                                // unused atm
+                            }
+
+                            @Override
+                            public void onComplete() {
+                                // unused atm
+                            }
+                        });
                 } else {
                     messageNotificationLevel = value;
                 }
             }
+        } else {
+            arbitraryStorageManager.storeStorageSetting(accountIdentifier, key, value, conversationToken);
         }
     }
 
@@ -205,7 +245,22 @@ public class DatabaseStorageModule implements StorageModule {
 
     @Override
     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;
         } else {
             return arbitraryStorageManager
@@ -243,4 +298,8 @@ public class DatabaseStorageModule implements StorageModule {
     public void onRestoreInstanceState(Bundle savedState) {
         // unused atm
     }
+
+    public void setMessageExpiration(int messageExpiration) {
+        this.messageExpiration = messageExpiration;
+    }
 }

+ 83 - 53
app/src/main/res/layout/controller_conversation_info.xml

@@ -3,6 +3,8 @@
   ~
   ~ @author Mario Danic
   ~ @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) 2017-2018 Mario Danic <mario@lovelyhq.com>
   ~
@@ -45,9 +47,10 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content">
 
-        <RelativeLayout
+        <LinearLayout
             android:layout_width="match_parent"
-            android:layout_height="wrap_content">
+            android:layout_height="wrap_content"
+            android:orientation="vertical">
 
             <com.yarolegovich.mp.MaterialPreferenceCategory
                 android:id="@+id/conversation_info_name"
@@ -87,7 +90,6 @@
                 android:id="@+id/conversation_description"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:layout_below="@id/conversation_info_name"
                 android:animateLayoutChanges="true"
                 android:visibility="gone"
                 apc:cardBackgroundColor="@color/bg_default"
@@ -109,7 +111,6 @@
                 android:id="@+id/otherRoomOptions"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:layout_below="@id/conversation_description"
                 android:visibility="gone"
                 apc:cardBackgroundColor="@color/bg_default"
                 apc:cardElevation="0dp"
@@ -127,11 +128,87 @@
 
             </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
                 android:id="@+id/participants_list_category"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:layout_below="@+id/category_shared_items"
                 android:visibility="gone"
                 apc:cardBackgroundColor="@color/bg_default"
                 apc:cardElevation="0dp"
@@ -159,7 +236,6 @@
                 android:id="@+id/ownOptions"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:layout_below="@id/participants_list_category"
                 android:visibility="gone"
                 apc:cardBackgroundColor="@color/bg_default"
                 apc:cardElevation="0dp"
@@ -191,52 +267,6 @@
 
             </com.yarolegovich.mp.MaterialPreferenceCategory>
 
-            <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>
+        </LinearLayout>
     </ScrollView>
 </RelativeLayout>

+ 20 - 0
app/src/main/res/values/arrays.xml

@@ -3,6 +3,8 @@
   ~ Nextcloud Talk application
   ~
   ~ @author Mario Danic
+  ~ @author Marcel Hibbe
+  ~ Copyright (C) 2022 Marcel Hibbe <dev@mhibbe.de>
   ~ Copyright (C) 2017 Mario Danic <mario@lovelyhq.com>
   ~
   ~ This program is free software: you can redistribute it and/or modify
@@ -39,6 +41,24 @@
         <item>always</item>
     </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">
         <item>@string/nc_screen_lock_timeout_30</item>
         <item>@string/nc_screen_lock_timeout_60</item>

+ 15 - 0
app/src/main/res/values/strings.xml

@@ -3,6 +3,8 @@
   ~
   ~ @author Mario Danic
   ~ @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) 2017-2018 Mario Danic <mario@lovelyhq.com>
   ~
@@ -564,4 +566,17 @@
     <string name="call_without_notification">Call without notification</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>