diff --git a/app/build.gradle b/app/build.gradle index 766669860..d03d4d459 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -303,7 +303,7 @@ dependencies { implementation 'androidx.activity:activity-ktx:1.7.2' - implementation 'com.github.nextcloud.android-common:ui:0.10.0' + implementation 'com.github.nextcloud.android-common:ui:0.11.0' implementation 'com.github.nextcloud-deps:android-talk-webrtc:110.5481.0' } 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 abd41f837..acfb77237 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt @@ -67,6 +67,7 @@ import coil.transform.CircleCropTransformation import com.google.android.material.appbar.AppBarLayout import com.google.android.material.button.MaterialButton import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.nextcloud.android.common.ui.theme.utils.ColorRole import com.nextcloud.talk.R import com.nextcloud.talk.activities.BaseActivity import com.nextcloud.talk.activities.CallActivity @@ -97,6 +98,7 @@ import com.nextcloud.talk.settings.SettingsActivity import com.nextcloud.talk.ui.dialog.ChooseAccountDialogFragment import com.nextcloud.talk.ui.dialog.ChooseAccountShareToDialogFragment import com.nextcloud.talk.ui.dialog.ConversationsListBottomDialog +import com.nextcloud.talk.ui.dialog.FilterConversationFragment import com.nextcloud.talk.users.UserManager import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.ClosedInterfaceImpl @@ -169,6 +171,7 @@ class ConversationsListActivity : private var conversationItems: MutableList> = ArrayList() private var conversationItemsWithHeader: MutableList> = ArrayList() private val searchableConversationItems: MutableList> = ArrayList() + private var filterableConversationItems: MutableList> = ArrayList() private var searchItem: MenuItem? = null private var chooseAccountItem: MenuItem? = null private var searchView: SearchView? = null @@ -189,6 +192,11 @@ class ConversationsListActivity : private var conversationsListBottomDialog: ConversationsListBottomDialog? = null private var searchHelper: MessageSearchHelper? = null private var searchViewDisposable: Disposable? = null + private var filterState = + mutableMapOf( + FilterConversationFragment.MENTION to false, + FilterConversationFragment.UNREAD to false + ) private val onBackPressedCallback = object : OnBackPressedCallback(true) { override fun handleOnBackPressed() { @@ -413,7 +421,8 @@ class ConversationsListActivity : searchItem!!.setOnActionExpandListener(object : MenuItem.OnActionExpandListener { override fun onMenuItemActionExpand(item: MenuItem): Boolean { adapter!!.setHeadersShown(true) - adapter!!.updateDataSet(searchableConversationItems, false) + if (!filterState.containsValue(true)) filterableConversationItems = searchableConversationItems + adapter!!.updateDataSet(filterableConversationItems, false) adapter!!.showAllHeaders() binding?.swipeRefreshLayoutView?.isEnabled = false return true @@ -421,7 +430,8 @@ class ConversationsListActivity : override fun onMenuItemActionCollapse(item: MenuItem): Boolean { adapter!!.setHeadersShown(false) - adapter!!.updateDataSet(conversationItems, false) + if (!filterState.containsValue(true)) filterableConversationItems = searchableConversationItems + adapter!!.updateDataSet(filterableConversationItems, false) adapter!!.hideAllHeaders() if (searchHelper != null) { // cancel any pending searches @@ -758,6 +768,18 @@ class ConversationsListActivity : } } + updateFilterConversationButtonColor() + + binding.filterConversationsButton.setOnClickListener { + val newFragment: DialogFragment = FilterConversationFragment.newInstance( + adapter!!, + conversationItems, + filterState, + this + ) + newFragment.show(supportFragmentManager, FilterConversationFragment.TAG) + } + binding?.newMentionPopupBubble?.hide() binding?.newMentionPopupBubble?.setPopupBubbleListener { binding?.recyclerView?.smoothScrollToPosition( @@ -1456,6 +1478,26 @@ class ConversationsListActivity : showErrorDialog() } + fun updateFilterState(mention: Boolean, unread: Boolean) { + filterState[FilterConversationFragment.MENTION] = mention + filterState[FilterConversationFragment.UNREAD] = unread + } + + fun setFilterableItems(items: MutableList>) { filterableConversationItems = items } + + fun updateFilterConversationButtonColor() { + if (filterState.containsValue(true)) { + binding.filterConversationsButton.let { viewThemeUtils.platform.colorImageView(it, ColorRole.PRIMARY) } + } else { + binding.filterConversationsButton.let { + viewThemeUtils.platform.colorImageView( + it, + ColorRole.ON_SURFACE_VARIANT + ) + } + } + } + companion object { const val TAG = "ConvListController" const val UNREAD_BUBBLE_DELAY = 2500 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 new file mode 100644 index 000000000..b1e934288 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/FilterConversationFragment.kt @@ -0,0 +1,169 @@ +/* + * Nextcloud Talk application + * + * @author Julius Linus + * Copyright (C) 2023 Julius Linus + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.nextcloud.talk.ui.dialog + +import android.app.Dialog +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +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.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 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 + + @Inject + lateinit var userManager: UserManager + + @Inject + lateinit var viewThemeUtils: ViewThemeUtils + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + binding = DialogFilterConversationBinding.inflate(LayoutInflater.from(context)) + dialogView = binding.root + + return MaterialAlertDialogBuilder(requireContext()).setView(dialogView).create() + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) + setUpColors() + setUpListeners() + return inflater.inflate(R.layout.dialog_filter_conversation, container, false) + } + + private fun setUpColors() { + viewThemeUtils.material.colorMaterialButtonPrimaryBorderless(binding.buttonClose) + + binding.run { + listOf( + binding.root + ) + }.forEach(viewThemeUtils.platform::colorViewBackground) + + binding.run { + listOf( + unreadFilterChip, + mentionedFilterChip + ) + }.forEach(viewThemeUtils.material::themeChipFilter) + + setUpChips() + } + + private fun setUpListeners() { + binding.unreadFilterChip.setOnCheckedChangeListener { _, isChecked -> + filterState[UNREAD] = isChecked + binding.unreadFilterChip.isChecked = isChecked + processSubmit() + } + + binding.mentionedFilterChip.setOnCheckedChangeListener { _, isChecked -> + filterState[MENTION] = isChecked + binding.mentionedFilterChip.isChecked = isChecked + processSubmit() + } + + binding.buttonClose.setOnClickListener { + dismiss() + } + } + + private fun setUpChips() { + binding.unreadFilterChip.isChecked = filterState[UNREAD]!! + binding.mentionedFilterChip.isChecked = filterState[MENTION]!! + } + + 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]!! + ) + + 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 + UNREAD -> result = result && (conversation.unreadMessages > 0) + } + } + } + + return result + } + + companion object { + @JvmStatic + fun newInstance( + adapter: FlexibleAdapter>, + currentConversations: MutableList>, + savedFilterState: MutableMap, + conversationsListActivity: ConversationsListActivity + ) = FilterConversationFragment(adapter, currentConversations, 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/drawable/ic_baseline_filter_list_24.xml b/app/src/main/res/drawable/ic_baseline_filter_list_24.xml new file mode 100644 index 000000000..ec05deb71 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_filter_list_24.xml @@ -0,0 +1,26 @@ + + + + diff --git a/app/src/main/res/layout/controller_conversations_rv.xml b/app/src/main/res/layout/controller_conversations_rv.xml index c4de69564..8afc03313 100644 --- a/app/src/main/res/layout/controller_conversations_rv.xml +++ b/app/src/main/res/layout/controller_conversations_rv.xml @@ -83,10 +83,22 @@ android:textSize="16sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toEndOf="@id/menu_button" - app:layout_constraintEnd_toStartOf="@id/rightContainer" + app:layout_constraintEnd_toStartOf="@id/filter_conversations_button" app:layout_constraintTop_toTopOf="parent" tools:text="Search in Nextcloud" /> + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 741d89889..ce3dbf019 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -63,7 +63,6 @@ #006400 #E8E8E8 #757575 - #212121 #D5D5D5 #E9FFFFFF #111111 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 46a285c05..5ca7542fc 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -680,5 +680,8 @@ How to translate with transifex: Translation failed Could not detect language Copy translated text + Filter Conversations + Mentioned + Unread diff --git a/scripts/analysis/lint-results.txt b/scripts/analysis/lint-results.txt index 3694e6bcc..76c3174da 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: 108 warnings + Lint Report: 107 warnings