diff --git a/app/schemas/com.nextcloud.talk.data.source.local.TalkDatabase/9.json b/app/schemas/com.nextcloud.talk.data.source.local.TalkDatabase/9.json new file mode 100644 index 0000000000..4b966b37e7 --- /dev/null +++ b/app/schemas/com.nextcloud.talk.data.source.local.TalkDatabase/9.json @@ -0,0 +1,139 @@ +{ + "formatVersion": 1, + "database": { + "version": 9, + "identityHash": "666fcc4bbbdf3ff121b8f1ace8fcbcb8", + "entities": [ + { + "tableName": "User", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `userId` TEXT, `username` TEXT, `baseUrl` TEXT, `token` TEXT, `displayName` TEXT, `pushConfigurationState` TEXT, `capabilities` TEXT, `clientCertificate` TEXT, `externalSignalingServer` TEXT, `current` INTEGER NOT NULL, `scheduledForDeletion` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "username", + "columnName": "username", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "baseUrl", + "columnName": "baseUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "token", + "columnName": "token", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "displayName", + "columnName": "displayName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pushConfigurationState", + "columnName": "pushConfigurationState", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "capabilities", + "columnName": "capabilities", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "clientCertificate", + "columnName": "clientCertificate", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "externalSignalingServer", + "columnName": "externalSignalingServer", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "current", + "columnName": "current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "scheduledForDeletion", + "columnName": "scheduledForDeletion", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ArbitraryStorage", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountIdentifier` INTEGER NOT NULL, `key` TEXT NOT NULL, `object` TEXT, `value` TEXT, PRIMARY KEY(`accountIdentifier`, `key`))", + "fields": [ + { + "fieldPath": "accountIdentifier", + "columnName": "accountIdentifier", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "storageObject", + "columnName": "object", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "accountIdentifier", + "key" + ] + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '666fcc4bbbdf3ff121b8f1ace8fcbcb8')" + ] + } +} \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/arbitrarystorage/ArbitraryStorageManager.kt b/app/src/main/java/com/nextcloud/talk/arbitrarystorage/ArbitraryStorageManager.kt index 7ef4c5242b..27c2550e0e 100644 --- a/app/src/main/java/com/nextcloud/talk/arbitrarystorage/ArbitraryStorageManager.kt +++ b/app/src/main/java/com/nextcloud/talk/arbitrarystorage/ArbitraryStorageManager.kt @@ -25,7 +25,7 @@ import com.nextcloud.talk.data.storage.model.ArbitraryStorage import io.reactivex.Maybe class ArbitraryStorageManager(private val arbitraryStoragesRepository: ArbitraryStoragesRepository) { - fun storeStorageSetting(accountIdentifier: Long, key: String?, value: String?, objectString: String?) { + fun storeStorageSetting(accountIdentifier: Long, key: String, value: String?, objectString: String?) { arbitraryStoragesRepository.saveArbitraryStorage(ArbitraryStorage(accountIdentifier, key, objectString, value)) } diff --git a/app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt b/app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt index ce2766d6a9..082af2eaa6 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt @@ -79,6 +79,7 @@ import com.nextcloud.talk.adapters.items.MessageResultItem import com.nextcloud.talk.adapters.items.MessagesTextHeaderItem import com.nextcloud.talk.api.NcApi import com.nextcloud.talk.application.NextcloudTalkApplication +import com.nextcloud.talk.arbitrarystorage.ArbitraryStorageManager import com.nextcloud.talk.chat.ChatActivity import com.nextcloud.talk.contacts.ContactsActivity import com.nextcloud.talk.data.user.model.User @@ -106,6 +107,7 @@ import com.nextcloud.talk.utils.ClosedInterfaceImpl import com.nextcloud.talk.utils.FileUtils import com.nextcloud.talk.utils.Mimetype import com.nextcloud.talk.utils.ParticipantPermissions +import com.nextcloud.talk.utils.UserIdUtils import com.nextcloud.talk.utils.bundle.BundleKeys import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_FORWARD_HIDE_SOURCE_ROOM import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_FORWARD_MSG_FLAG @@ -158,6 +160,9 @@ class ConversationsListActivity : @Inject lateinit var platformPermissionUtil: PlatformPermissionUtil + @Inject + lateinit var arbitraryStorageManager: ArbitraryStorageManager + override val appBarLayoutType: AppBarLayoutType get() = AppBarLayoutType.SEARCH_BAR @@ -272,6 +277,65 @@ class ConversationsListActivity : showSearchOrToolbar() } + fun filterConversation() { + val accountId = UserIdUtils.getIdForUser(userManager.currentUser.blockingGet()) + filterState[FilterConversationFragment.UNREAD] = ( + arbitraryStorageManager.getStorageSetting( + accountId, + FilterConversationFragment.UNREAD, + "" + ).blockingGet()?.value ?: "" + ) == "true" + + filterState[FilterConversationFragment.MENTION] = ( + arbitraryStorageManager.getStorageSetting( + accountId, + FilterConversationFragment.MENTION, + "" + ).blockingGet()?.value ?: "" + ) == "true" + + val newItems: MutableList> = ArrayList() + if (filterState[FilterConversationFragment.UNREAD] == false && + filterState[FilterConversationFragment.MENTION] == false + ) { + adapter!!.updateDataSet(conversationItems, true) + } else { + val items = conversationItems + for (i in items) { + val conversation = (i as ConversationItem).model + if (filter(conversation)) { + newItems.add(i) + } + } + adapter!!.updateDataSet(newItems, true) + setFilterableItems(newItems) + } + + updateFilterConversationButtonColor() + } + + private fun filter(conversation: Conversation): Boolean { + var result = true + for ((k, v) in filterState) { + if (v) { + when (k) { + FilterConversationFragment.MENTION -> result = (result && conversation.unreadMention) || + ( + result && + ( + conversation.type == Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL || + conversation.type == Conversation.ConversationType.FORMER_ONE_TO_ONE + ) && + (conversation.unreadMessages > 0) + ) + FilterConversationFragment.UNREAD -> result = result && (conversation.unreadMessages > 0) + } + } + } + + return result + } private fun setupActionBar() { setSupportActionBar(binding.conversationListToolbar) @@ -578,6 +642,7 @@ class ConversationsListActivity : sortConversations(conversationItems) sortConversations(conversationItemsWithHeader) if (!filterState.containsValue(true)) filterableConversationItems = conversationItems + filterConversation() adapter!!.updateDataSet(filterableConversationItems, false) Handler().postDelayed({ checkToShowUnreadBubble() }, UNREAD_BUBBLE_DELAY.toLong()) fetchOpenConversations(apiVersion) @@ -788,8 +853,6 @@ class ConversationsListActivity : binding.filterConversationsButton.setOnClickListener { val newFragment: DialogFragment = FilterConversationFragment.newInstance( - adapter!!, - conversationItems, filterState, this ) diff --git a/app/src/main/java/com/nextcloud/talk/data/source/local/Migrations.kt b/app/src/main/java/com/nextcloud/talk/data/source/local/Migrations.kt index 4d675f0f54..11ce9552f9 100644 --- a/app/src/main/java/com/nextcloud/talk/data/source/local/Migrations.kt +++ b/app/src/main/java/com/nextcloud/talk/data/source/local/Migrations.kt @@ -40,6 +40,13 @@ object Migrations { } } + val MIGRATION_8_9 = object : Migration(8, 9) { + override fun migrate(database: SupportSQLiteDatabase) { + Log.i("Migrations", "Migrating 8 to 9") + migrateToDualPrimaryKeyArbitraryStorage(database) + } + } + fun migrateToRoom(database: SupportSQLiteDatabase) { database.execSQL( "CREATE TABLE User_new (" + @@ -92,4 +99,29 @@ object Migrations { database.execSQL("ALTER TABLE User_new RENAME TO User") database.execSQL("ALTER TABLE ArbitraryStorage_new RENAME TO ArbitraryStorage") } + + fun migrateToDualPrimaryKeyArbitraryStorage(database: SupportSQLiteDatabase) { + database.execSQL( + "CREATE TABLE ArbitraryStorage_dualPK (" + + "accountIdentifier INTEGER NOT NULL, " + + "\"key\" TEXT NOT NULL, " + + "object TEXT, " + + "value TEXT, " + + "PRIMARY KEY(accountIdentifier, \"key\")" + + ")" + ) + // Copy the data + database.execSQL( + "INSERT INTO ArbitraryStorage_dualPK (" + + "accountIdentifier, \"key\", object, value) " + + "SELECT " + + "accountIdentifier, \"key\", object, value " + + "FROM ArbitraryStorage" + ) + // Remove the old table + database.execSQL("DROP TABLE ArbitraryStorage") + + // Change the table name to the correct one + database.execSQL("ALTER TABLE ArbitraryStorage_dualPK RENAME TO ArbitraryStorage") + } } diff --git a/app/src/main/java/com/nextcloud/talk/data/source/local/TalkDatabase.kt b/app/src/main/java/com/nextcloud/talk/data/source/local/TalkDatabase.kt index de64a74d32..4db51a3e73 100644 --- a/app/src/main/java/com/nextcloud/talk/data/source/local/TalkDatabase.kt +++ b/app/src/main/java/com/nextcloud/talk/data/source/local/TalkDatabase.kt @@ -45,7 +45,7 @@ import java.util.Locale @Database( entities = [UserEntity::class, ArbitraryStorageEntity::class], - version = 8, + version = 9, exportSchema = true ) @TypeConverters( @@ -96,7 +96,7 @@ abstract class TalkDatabase : RoomDatabase() { .databaseBuilder(context.applicationContext, TalkDatabase::class.java, dbName) // comment out openHelperFactory to view the database entries in Android Studio for debugging .openHelperFactory(factory) - .addMigrations(Migrations.MIGRATION_6_8, Migrations.MIGRATION_7_8) + .addMigrations(Migrations.MIGRATION_6_8, Migrations.MIGRATION_7_8, Migrations.MIGRATION_8_9) .allowMainThreadQueries() .addCallback( object : RoomDatabase.Callback() { diff --git a/app/src/main/java/com/nextcloud/talk/data/storage/ArbitraryStoragesDao.kt b/app/src/main/java/com/nextcloud/talk/data/storage/ArbitraryStoragesDao.kt index a5d0d08b9f..0941b6f9af 100644 --- a/app/src/main/java/com/nextcloud/talk/data/storage/ArbitraryStoragesDao.kt +++ b/app/src/main/java/com/nextcloud/talk/data/storage/ArbitraryStoragesDao.kt @@ -41,6 +41,11 @@ abstract class ArbitraryStoragesDao { objectString: String ): Maybe + @Query( + "SELECT * FROM ArbitraryStorage" + ) + abstract fun getAll(): Maybe> + @Query("DELETE FROM ArbitraryStorage WHERE accountIdentifier = :accountIdentifier") abstract fun deleteArbitraryStorage(accountIdentifier: Long): Int diff --git a/app/src/main/java/com/nextcloud/talk/data/storage/ArbitraryStoragesRepository.kt b/app/src/main/java/com/nextcloud/talk/data/storage/ArbitraryStoragesRepository.kt index 8086c4d2ae..ea5c3c9f8e 100644 --- a/app/src/main/java/com/nextcloud/talk/data/storage/ArbitraryStoragesRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/data/storage/ArbitraryStoragesRepository.kt @@ -21,10 +21,12 @@ package com.nextcloud.talk.data.storage import com.nextcloud.talk.data.storage.model.ArbitraryStorage +import com.nextcloud.talk.data.storage.model.ArbitraryStorageEntity import io.reactivex.Maybe interface ArbitraryStoragesRepository { fun getStorageSetting(accountIdentifier: Long, key: String, objectString: String): Maybe fun deleteArbitraryStorage(accountIdentifier: Long): Int fun saveArbitraryStorage(arbitraryStorage: ArbitraryStorage): Long + fun getAll(): Maybe> } diff --git a/app/src/main/java/com/nextcloud/talk/data/storage/ArbitraryStoragesRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/data/storage/ArbitraryStoragesRepositoryImpl.kt index 9f04bfbd4e..6eb07cf761 100644 --- a/app/src/main/java/com/nextcloud/talk/data/storage/ArbitraryStoragesRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/data/storage/ArbitraryStoragesRepositoryImpl.kt @@ -21,6 +21,7 @@ package com.nextcloud.talk.data.storage import com.nextcloud.talk.data.storage.model.ArbitraryStorage +import com.nextcloud.talk.data.storage.model.ArbitraryStorageEntity import io.reactivex.Maybe class ArbitraryStoragesRepositoryImpl(private val arbitraryStoragesDao: ArbitraryStoragesDao) : @@ -35,6 +36,10 @@ class ArbitraryStoragesRepositoryImpl(private val arbitraryStoragesDao: Arbitrar .map { ArbitraryStorageMapper.toModel(it) } } + override fun getAll(): Maybe> { + return arbitraryStoragesDao.getAll() + } + override fun deleteArbitraryStorage(accountIdentifier: Long): Int { return arbitraryStoragesDao.deleteArbitraryStorage(accountIdentifier) } diff --git a/app/src/main/java/com/nextcloud/talk/data/storage/model/ArbitraryStorage.kt b/app/src/main/java/com/nextcloud/talk/data/storage/model/ArbitraryStorage.kt index baf5e96781..56b93b38b4 100644 --- a/app/src/main/java/com/nextcloud/talk/data/storage/model/ArbitraryStorage.kt +++ b/app/src/main/java/com/nextcloud/talk/data/storage/model/ArbitraryStorage.kt @@ -25,8 +25,8 @@ import kotlinx.parcelize.Parcelize @Parcelize data class ArbitraryStorage( - var accountIdentifier: Long = 0, - var key: String? = null, + var accountIdentifier: Long, + var key: String, var storageObject: String? = null, var value: String? = null ) : Parcelable diff --git a/app/src/main/java/com/nextcloud/talk/data/storage/model/ArbitraryStorageEntity.kt b/app/src/main/java/com/nextcloud/talk/data/storage/model/ArbitraryStorageEntity.kt index 45e66e1f46..ab5f7dc626 100644 --- a/app/src/main/java/com/nextcloud/talk/data/storage/model/ArbitraryStorageEntity.kt +++ b/app/src/main/java/com/nextcloud/talk/data/storage/model/ArbitraryStorageEntity.kt @@ -23,18 +23,16 @@ package com.nextcloud.talk.data.storage.model import android.os.Parcelable import androidx.room.ColumnInfo import androidx.room.Entity -import androidx.room.PrimaryKey import kotlinx.parcelize.Parcelize @Parcelize -@Entity(tableName = "ArbitraryStorage") +@Entity(tableName = "ArbitraryStorage", primaryKeys = ["accountIdentifier", "key"]) data class ArbitraryStorageEntity( - @PrimaryKey @ColumnInfo(name = "accountIdentifier") var accountIdentifier: Long = 0, @ColumnInfo(name = "key") - var key: String? = null, + var key: String = "", @ColumnInfo(name = "object") var storageObject: String? = null, diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/FilterConversationFragment.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/FilterConversationFragment.kt index 5cabc25e2a..aa8061e42c 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/FilterConversationFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/FilterConversationFragment.kt @@ -28,28 +28,22 @@ import androidx.fragment.app.DialogFragment import autodagger.AutoInjector import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.nextcloud.talk.R -import com.nextcloud.talk.adapters.items.ConversationItem import com.nextcloud.talk.application.NextcloudTalkApplication +import com.nextcloud.talk.arbitrarystorage.ArbitraryStorageManager import com.nextcloud.talk.conversationlist.ConversationsListActivity import com.nextcloud.talk.databinding.DialogFilterConversationBinding -import com.nextcloud.talk.models.json.conversations.Conversation import com.nextcloud.talk.ui.theme.ViewThemeUtils import com.nextcloud.talk.users.UserManager -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import com.nextcloud.talk.utils.UserIdUtils import javax.inject.Inject @AutoInjector(NextcloudTalkApplication::class) class FilterConversationFragment( - adapter: FlexibleAdapter>, - currentConversations: MutableList>, savedFilterState: MutableMap, conversationsListActivity: ConversationsListActivity ) : DialogFragment() { lateinit var binding: DialogFilterConversationBinding private var dialogView: View? = null - private var currentAdapter: FlexibleAdapter> = adapter - private var currentItems = currentConversations private var filterState = savedFilterState private var conversationsList = conversationsListActivity @@ -58,6 +52,9 @@ class FilterConversationFragment( @Inject lateinit var viewThemeUtils: ViewThemeUtils + + @Inject + lateinit var arbitraryStorageManager: ArbitraryStorageManager override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { binding = DialogFilterConversationBinding.inflate(LayoutInflater.from(context)) dialogView = binding.root @@ -119,57 +116,23 @@ class FilterConversationFragment( } private fun processSubmit() { - val newItems: MutableList> = ArrayList() - if (!filterState.containsValue(true)) { - currentAdapter.updateDataSet(currentItems, true) - } else { - val items = currentItems - for (i in items) { - val conversation = (i as ConversationItem).model - if (filter(conversation)) { - newItems.add(i) - } - } - currentAdapter.updateDataSet(newItems, true) - conversationsList.setFilterableItems(newItems) - } - conversationsList.updateFilterState( - filterState[MENTION]!!, - filterState[UNREAD]!! - ) + // store + val accountId = UserIdUtils.getIdForUser(userManager.currentUser.blockingGet()) + val mentionValue = filterState[MENTION] == true + val unreadValue = filterState[UNREAD] == true - conversationsList.updateFilterConversationButtonColor() - } - private fun filter(conversation: Conversation): Boolean { - var result = true - for ((k, v) in filterState) { - if (v) { - when (k) { - MENTION -> result = (result && conversation.unreadMention) || - ( - result && - ( - conversation.type == Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL || - conversation.type == Conversation.ConversationType.FORMER_ONE_TO_ONE - ) && - (conversation.unreadMessages > 0) - ) - UNREAD -> result = result && (conversation.unreadMessages > 0) - } - } - } + arbitraryStorageManager.storeStorageSetting(accountId, MENTION, mentionValue.toString(), "") + arbitraryStorageManager.storeStorageSetting(accountId, UNREAD, unreadValue.toString(), "") - return result + conversationsList.filterConversation() } companion object { @JvmStatic fun newInstance( - adapter: FlexibleAdapter>, - currentConversations: MutableList>, savedFilterState: MutableMap, conversationsListActivity: ConversationsListActivity - ) = FilterConversationFragment(adapter, currentConversations, savedFilterState, conversationsListActivity) + ) = FilterConversationFragment(savedFilterState, conversationsListActivity) val TAG: String = FilterConversationFragment::class.java.simpleName const val MENTION: String = "mention" const val UNREAD: String = "unread" diff --git a/app/src/main/res/layout/item_guest_access_settings.xml b/app/src/main/res/layout/item_guest_access_settings.xml index 348d9bac24..ee60abfe04 100644 --- a/app/src/main/res/layout/item_guest_access_settings.xml +++ b/app/src/main/res/layout/item_guest_access_settings.xml @@ -1,7 +1,6 @@ diff --git a/scripts/analysis/lint-results.txt b/scripts/analysis/lint-results.txt index 7a5728da39..2d857a44dc 100644 --- a/scripts/analysis/lint-results.txt +++ b/scripts/analysis/lint-results.txt @@ -1,2 +1,2 @@ DO NOT TOUCH; GENERATED BY DRONE - Lint Report: 93 warnings + Lint Report: 92 warnings