From d938906d3edb026c0b7eb8da4a9e146dd5d05240 Mon Sep 17 00:00:00 2001 From: Sagar Date: Thu, 6 Feb 2025 22:55:19 +0530 Subject: [PATCH] Support selecting multiple threads to add to chat folder. Resolves #13973 --- .../settings/app/AppSettingsActivity.kt | 6 +- .../app/chats/folders/ChatFoldersFragment.kt | 2 +- .../app/chats/folders/ChatFoldersViewModel.kt | 25 +++--- .../chats/folders/CreateFoldersFragment.kt | 4 +- .../AddToFolderBottomSheet.kt | 86 +++++++++++++------ .../ConversationListFragment.java | 83 ++++++++++++++---- .../ConversationListViewModel.kt | 8 +- .../securesms/database/ChatFolderTables.kt | 18 ++-- .../app_settings_with_change_number.xml | 5 +- app/src/main/res/values/strings.xml | 7 +- 10 files changed, 172 insertions(+), 72 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/AppSettingsActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/AppSettingsActivity.kt index 615d8cdc63..a82ed500da 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/AppSettingsActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/AppSettingsActivity.kt @@ -72,7 +72,7 @@ class AppSettingsActivity : DSLSettingsActivity(), InAppPaymentComponent { StartLocation.CHAT_FOLDERS -> AppSettingsFragmentDirections.actionDirectToChatFoldersFragment() StartLocation.CREATE_CHAT_FOLDER -> AppSettingsFragmentDirections.actionDirectToCreateFoldersFragment( CreateFoldersFragmentArgs.fromBundle(intent.getBundleExtra(START_ARGUMENTS)!!).folderId, - CreateFoldersFragmentArgs.fromBundle(intent.getBundleExtra(START_ARGUMENTS)!!).threadId + CreateFoldersFragmentArgs.fromBundle(intent.getBundleExtra(START_ARGUMENTS)!!).threadIds ) StartLocation.BACKUPS_SETTINGS -> AppSettingsFragmentDirections.actionDirectToBackupsSettingsFragment() } @@ -209,8 +209,8 @@ class AppSettingsActivity : DSLSettingsActivity(), InAppPaymentComponent { fun chatFolders(context: Context): Intent = getIntentForStartLocation(context, StartLocation.CHAT_FOLDERS) @JvmStatic - fun createChatFolder(context: Context, id: Long = -1, threadId: Long?): Intent { - val arguments = CreateFoldersFragmentArgs.Builder(id, threadId ?: -1) + fun createChatFolder(context: Context, id: Long = -1, threadIds: LongArray?): Intent { + val arguments = CreateFoldersFragmentArgs.Builder(id, threadIds ?: longArrayOf()) .build() .toBundle() diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/folders/ChatFoldersFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/folders/ChatFoldersFragment.kt index fc78826d85..c182669c0e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/folders/ChatFoldersFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/folders/ChatFoldersFragment.kt @@ -84,7 +84,7 @@ class ChatFoldersFragment : ComposeFragment() { navController = navController, modifier = Modifier.padding(contentPadding), onFolderClicked = { - navController.safeNavigate(ChatFoldersFragmentDirections.actionChatFoldersFragmentToCreateFoldersFragment(it.id, -1)) + navController.safeNavigate(ChatFoldersFragmentDirections.actionChatFoldersFragmentToCreateFoldersFragment(it.id, null)) }, onAdd = { folder -> Toast.makeText(requireContext(), getString(R.string.ChatFoldersFragment__folder_added, folder.name), Toast.LENGTH_SHORT).show() diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/folders/ChatFoldersViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/folders/ChatFoldersViewModel.kt index 23936d22bf..c074daf8b7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/folders/ChatFoldersViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/folders/ChatFoldersViewModel.kt @@ -225,22 +225,27 @@ class ChatFoldersViewModel : ViewModel() { } } - fun addThreadToIncludedChat(threadId: Long?) { - if (threadId == null || threadId == -1L) { + fun addThreadsToFolder(threadIds: LongArray?) { + if (threadIds == null || threadIds.isEmpty()) { return } viewModelScope.launch { val updatedFolder = internalState.value.currentFolder - val recipient = SignalDatabase.threads.getRecipientForThreadId(threadId) - if (recipient != null) { - internalState.update { - it.copy( - currentFolder = updatedFolder.copy( - includedRecipients = setOf(recipient) - ) - ) + val includedRecipients = mutableSetOf() + threadIds.forEach { threadId -> + val recipient = SignalDatabase.threads.getRecipientForThreadId(threadId) + if (recipient != null) { + includedRecipients.add(recipient) } } + + internalState.update { + it.copy( + currentFolder = updatedFolder.copy( + includedRecipients = includedRecipients + ) + ) + } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/folders/CreateFoldersFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/folders/CreateFoldersFragment.kt index ba229ecc0f..c1f17a962f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/folders/CreateFoldersFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/folders/CreateFoldersFragment.kt @@ -99,7 +99,7 @@ class CreateFoldersFragment : ComposeFragment() { LaunchedEffect(Unit) { if (state.originalFolder == state.currentFolder) { viewModel.setCurrentFolderId(arguments?.getLong(KEY_FOLDER_ID) ?: -1) - viewModel.addThreadToIncludedChat(arguments?.getLong(KEY_THREAD_ID)) + viewModel.addThreadsToFolder(arguments?.getLongArray(KEY_THREAD_IDS)) } } @@ -170,7 +170,7 @@ class CreateFoldersFragment : ComposeFragment() { companion object { private val KEY_FOLDER_ID = "folder_id" - private val KEY_THREAD_ID = "thread_id" + private val KEY_THREAD_IDS = "thread_ids" } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/AddToFolderBottomSheet.kt b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/AddToFolderBottomSheet.kt index 54aa4793ec..82367a581b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/AddToFolderBottomSheet.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/AddToFolderBottomSheet.kt @@ -45,31 +45,41 @@ import org.thoughtcrime.securesms.util.viewModel /** * Bottom sheet shown when choosing to add a chat to a folder */ -class AddToFolderBottomSheet private constructor() : ComposeBottomSheetDialogFragment() { +class AddToFolderBottomSheet private constructor(private val onDismissListener: OnDismissListener) : ComposeBottomSheetDialogFragment() { override val peekHeightPercentage: Float = 1f + interface OnDismissListener { + fun onDismiss() + } + + enum class ThreadType(val value: Int) { + INDIVIDUAL(1), + GROUP(2), + OTHER(3) + } + private val viewModel by viewModel { ConversationListViewModel(isArchived = false) } companion object { private const val ARG_FOLDERS = "argument.folders" - private const val ARG_THREAD_ID = "argument.thread.id" - private const val ARG_IS_INDIVIDUAL_CHAT = "argument.is.individual.chat" + private const val ARG_THREAD_IDS = "argument.thread.ids" + private const val ARG_THREAD_TYPES = "argument.thread.types" /** * Shows a bottom sheet that allows a thread to be added to a folder. * * @param folders list of available folders to add a thread to - * @param threadId the thread that is going to be added - * @param isIndividualChat whether the thread is an individual/1:1 chat as opposed to a group chat + * @param threadIds list of threads that are going to be added + * @param threadTypes list of ThreadType of threads present in threadIds */ @JvmStatic - fun showChatFolderSheet(folders: List, threadId: Long, isIndividualChat: Boolean): ComposeBottomSheetDialogFragment { - return AddToFolderBottomSheet().apply { + fun showChatFolderSheet(folders: List, threadIds: List, threadTypes: List, onDismissListener: OnDismissListener): ComposeBottomSheetDialogFragment { + return AddToFolderBottomSheet(onDismissListener).apply { arguments = bundleOf( ARG_FOLDERS to folders, - ARG_THREAD_ID to threadId, - ARG_IS_INDIVIDUAL_CHAT to isIndividualChat + ARG_THREAD_IDS to threadIds.toLongArray(), + ARG_THREAD_TYPES to threadTypes.toIntArray() ) } } @@ -78,25 +88,31 @@ class AddToFolderBottomSheet private constructor() : ComposeBottomSheetDialogFra @Composable override fun SheetContent() { val folders = requireArguments().getParcelableArrayListCompat(ARG_FOLDERS, ChatFolderRecord::class.java)?.filter { it.folderType != ChatFolderRecord.FolderType.ALL } - val threadId = requireArguments().getLong(ARG_THREAD_ID) - val isIndividualChat = requireArguments().getBoolean(ARG_IS_INDIVIDUAL_CHAT) - + val threadIds = requireArguments().getLongArray(ARG_THREAD_IDS)?.asList() ?: throw IllegalArgumentException("At least one ThreadId is expected!") + val threadTypes = requireArguments().getIntArray(ARG_THREAD_TYPES)?.asList() ?: throw IllegalArgumentException("At least one ThreadId is expected!") AddToChatFolderSheetContent( - threadId = threadId, - isIndividualChat = isIndividualChat, + threadIds = threadIds, + threadTypes = threadTypes, folders = remember { folders ?: emptyList() }, onClick = { folder, isAlreadyAdded -> if (isAlreadyAdded) { - Toast.makeText(context, requireContext().getString(R.string.AddToFolderBottomSheet_this_chat_is_already, folder.name), Toast.LENGTH_SHORT).show() + val message = requireContext().resources.getQuantityString( + R.plurals.AddToFolderBottomSheet_these_chat_are_already_in_s, + threadIds.size, + folder.name + ) + Toast.makeText(context, message, Toast.LENGTH_SHORT).show() } else { - viewModel.addToFolder(folder.id, threadId) + viewModel.addToFolder(folder.id, threadIds) Toast.makeText(context, requireContext().getString(R.string.AddToFolderBottomSheet_added_to_s, folder.name), Toast.LENGTH_SHORT).show() dismissAllowingStateLoss() + onDismissListener.onDismiss() } }, onCreate = { - requireContext().startActivity(AppSettingsActivity.createChatFolder(requireContext(), -1, threadId)) + requireContext().startActivity(AppSettingsActivity.createChatFolder(requireContext(), -1, threadIds.toLongArray())) dismissAllowingStateLoss() + onDismissListener.onDismiss() } ) } @@ -104,8 +120,8 @@ class AddToFolderBottomSheet private constructor() : ComposeBottomSheetDialogFra @Composable private fun AddToChatFolderSheetContent( - threadId: Long, - isIndividualChat: Boolean, + threadIds: List, + threadTypes: List, folders: List, onClick: (ChatFolderRecord, Boolean) -> Unit = { _, _ -> }, onCreate: () -> Unit = {} @@ -130,11 +146,7 @@ private fun AddToChatFolderSheetContent( .background(color = MaterialTheme.colorScheme.surface, shape = RoundedCornerShape(18.dp)) ) { items(folders) { folder -> - val isIncludedViaChatType = (isIndividualChat && folder.showIndividualChats) || (!isIndividualChat && folder.showGroupChats) - val isIncludedExplicitly = folder.includedChats.contains(threadId) - val isExcludedExplicitly = folder.excludedChats.contains(threadId) - - val isAlreadyAdded = (isIncludedExplicitly || isIncludedViaChatType) && !isExcludedExplicitly + val isAlreadyAdded = isThreadListAlreadyAdded(folder, threadIds, threadTypes) Row( verticalAlignment = Alignment.CenterVertically, @@ -210,14 +222,36 @@ private fun AddToChatFolderSheetContent( } } +private fun isThreadListAlreadyAdded(folder: ChatFolderRecord, threadIds: List, threadTypes: List): Boolean { + val isAnyExcluded = threadIds.any { + folder.excludedChats.contains(it) + } + if (isAnyExcluded) { + return false + } + + if (folder.showIndividualChats) { + return threadTypes.indices.all { index -> + threadTypes[index] == AddToFolderBottomSheet.ThreadType.INDIVIDUAL.value || folder.includedChats.contains(threadIds[index]) + } + } + if (folder.showGroupChats) { + return threadTypes.indices.all { index -> + threadTypes[index] == AddToFolderBottomSheet.ThreadType.GROUP.value || folder.includedChats.contains(threadIds[index]) + } + } + + return folder.includedChats.containsAll(threadIds) +} + @SignalPreview @Composable private fun AddToChatFolderSheetContentPreview() { Previews.BottomSheetPreview { AddToChatFolderSheetContent( folders = listOf(ChatFolderRecord(name = "Friends"), ChatFolderRecord(name = "Work")), - threadId = 1, - isIndividualChat = false + threadIds = listOf(1), + threadTypes = listOf(0) ) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java index 71671bb411..a7581d96cc 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java @@ -160,7 +160,6 @@ import org.thoughtcrime.securesms.megaphone.MegaphoneActionController; import org.thoughtcrime.securesms.megaphone.MegaphoneViewBuilder; import org.thoughtcrime.securesms.megaphone.Megaphones; import org.thoughtcrime.securesms.notifications.MarkReadReceiver; -import org.thoughtcrime.securesms.notifications.profiles.NotificationProfile; import org.thoughtcrime.securesms.permissions.Permissions; import org.thoughtcrime.securesms.profiles.manage.UsernameEditFragment; import org.thoughtcrime.securesms.ratelimit.RecaptchaProofBottomSheetFragment; @@ -220,9 +219,9 @@ public class ConversationListFragment extends MainFragment implements ActionMode private static final String TAG = Log.tag(ConversationListFragment.class); - private static final int MAXIMUM_PINNED_CONVERSATIONS = 4; - private static final int MAX_CHATS_ABOVE_FOLD = 7; - private static final int MAX_CONTACTS_ABOVE_FOLD = 5; + private static final int MAXIMUM_PINNED_CONVERSATIONS = 4; + private static final int MAX_CHATS_ABOVE_FOLD = 7; + private static final int MAX_CONTACTS_ABOVE_FOLD = 5; private static final int MAX_GROUP_MEMBERSHIPS_ABOVE_FOLD = 5; private ActionMode actionMode; @@ -396,7 +395,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode archiveDecoration = new ConversationListArchiveItemDecoration(new ColorDrawable(getResources().getColor(R.color.conversation_list_archive_background_end))); itemAnimator = new ConversationListItemAnimator(); - chatFolderAdapter = new ChatFolderAdapter(this); + chatFolderAdapter = new ChatFolderAdapter(this); DefaultItemAnimator chatFolderItemAnimator = getChatFolderItemAnimator(); chatFolderList.setLayoutManager(new LinearLayoutManager(requireActivity(), LinearLayoutManager.HORIZONTAL, false)); @@ -484,7 +483,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode } private @NonNull DefaultItemAnimator getChatFolderItemAnimator() { - int duration = 150; + int duration = 150; DefaultItemAnimator animator = new DefaultItemAnimator(); animator.setAddDuration(duration); animator.setMoveDuration(duration); @@ -669,10 +668,10 @@ public class ConversationListFragment extends MainFragment implements ActionMode builder.addSection(new ContactSearchConfiguration.Section.Chats( unreadOnly, true, - new ContactSearchConfiguration.ExpandConfig( - state.getExpandedSections().contains(ContactSearchConfiguration.SectionKey.CHATS), - (a) -> MAX_CHATS_ABOVE_FOLD - ) + new ContactSearchConfiguration.ExpandConfig( + state.getExpandedSections().contains(ContactSearchConfiguration.SectionKey.CHATS), + (a) -> MAX_CHATS_ABOVE_FOLD + ) )); if (!unreadOnly) { @@ -704,8 +703,8 @@ public class ConversationListFragment extends MainFragment implements ActionMode } else { builder.arbitrary( conversationFilterRequest.getSource() == ConversationFilterSource.DRAG - ? ConversationListSearchAdapter.ChatFilterOptions.WITHOUT_TIP.getCode() - : ConversationListSearchAdapter.ChatFilterOptions.WITH_TIP.getCode() + ? ConversationListSearchAdapter.ChatFilterOptions.WITHOUT_TIP.getCode() + : ConversationListSearchAdapter.ChatFilterOptions.WITH_TIP.getCode() ); } @@ -958,7 +957,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode private void initializeListAdapters() { - defaultAdapter = new ConversationListAdapter(getViewLifecycleOwner(), Glide.with(this), this, this, this); + defaultAdapter = new ConversationListAdapter(getViewLifecycleOwner(), Glide.with(this), this, this, this); setAdapter(defaultAdapter); @@ -1509,10 +1508,10 @@ public class ConversationListFragment extends MainFragment implements ActionMode } else { if (viewModel.getCurrentFolder().getFolderType() == ChatFolderRecord.FolderType.ALL && (conversation.getThreadRecord().getRecipient().isIndividual() || - conversation.getThreadRecord().getRecipient().isPushV2Group())) { - List folders = viewModel.getFolders().stream().map(ChatFolderMappingModel::getChatFolder).collect(Collectors.toList()); + conversation.getThreadRecord().getRecipient().isPushV2Group())) + { items.add(new ActionItem(R.drawable.symbol_folder_add, getString(R.string.ConversationListFragment_add_to_folder), () -> - AddToFolderBottomSheet.showChatFolderSheet(folders, conversation.getThreadRecord().getThreadId(), conversation.getThreadRecord().getRecipient().isIndividual()).show(getParentFragmentManager(), BottomSheetUtil.STANDARD_BOTTOM_SHEET_FRAGMENT_TAG) + showAddToFolderBottomSheet(conversation) )); } else if (viewModel.getCurrentFolder().getFolderType() != ChatFolderRecord.FolderType.ALL) { items.add(new ActionItem(R.drawable.symbol_folder_minus, getString(R.string.ConversationListFragment_remove_from_folder), () -> viewModel.removeChatFromFolder(conversation.getThreadRecord().getThreadId()))); @@ -1582,6 +1581,52 @@ public class ConversationListFragment extends MainFragment implements ActionMode closeSearchIfOpen(); } + private void showAddToFolderBottomSheet(Conversation conversation) { + showAddToFolderBottomSheet( + Collections.singletonList(conversation.getThreadRecord().getThreadId()), + Collections.singletonList(getThreadType(conversation)) + ); + } + + private void showAddToFolderBottomSheet(Set conversations) { + List threadIds = new ArrayList<>(); + List threadTypes = new ArrayList<>(); + + for (Conversation conversation : conversations) { + threadIds.add(conversation.getThreadRecord().getThreadId()); + threadTypes.add(getThreadType(conversation)); + } + + showAddToFolderBottomSheet( + threadIds, + threadTypes + ); + } + + private int getThreadType(Conversation conversation) { + boolean isIndividual = conversation.getThreadRecord().getRecipient().isIndividual(); + boolean isGroup = conversation.getThreadRecord().getRecipient().isPushGroup(); + int type; + if (isIndividual) { + type = AddToFolderBottomSheet.ThreadType.INDIVIDUAL.getValue(); + } else if (isGroup) { + type = AddToFolderBottomSheet.ThreadType.GROUP.getValue(); + } else { + type = AddToFolderBottomSheet.ThreadType.OTHER.getValue(); + } + return type; + } + + private void showAddToFolderBottomSheet(List threadIds, List threadTypes) { + List folders = viewModel.getFolders().stream().map(ChatFolderMappingModel::getChatFolder).collect(Collectors.toList()); + AddToFolderBottomSheet.showChatFolderSheet( + folders, + threadIds, + threadTypes, + this::endActionModeIfActive + ).show(getParentFragmentManager(), BottomSheetUtil.STANDARD_BOTTOM_SHEET_FRAGMENT_TAG); + } + private void updateMultiSelectState() { int count = viewModel.currentSelectedConversations().size(); boolean hasUnread = Stream.of(viewModel.currentSelectedConversations()).anyMatch(conversation -> !conversation.getThreadRecord().isRead()); @@ -1628,6 +1673,12 @@ public class ConversationListFragment extends MainFragment implements ActionMode items.add(new ActionItem(R.drawable.symbol_check_circle_24, getString(R.string.ConversationListFragment_select_all), viewModel::onSelectAllClick)); + if (!isArchived()) { + items.add(new ActionItem(R.drawable.symbol_folder_add, getString(R.string.ConversationListFragment_add_to_folder), () -> { + showAddToFolderBottomSheet(viewModel.currentSelectedConversations()); + })); + } + bottomActionBar.setItems(items); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListViewModel.kt index b45bcef7d2..52a221e9a1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListViewModel.kt @@ -283,9 +283,13 @@ class ConversationListViewModel( } } - fun addToFolder(folderId: Long, threadId: Long) { + fun addToFolder(folderId: Long, threadIds: List) { viewModelScope.launch(Dispatchers.IO) { - SignalDatabase.chatFolders.addToFolder(folderId, threadId) + val includedChats = folders.find { it.chatFolder.id == folderId }?.chatFolder?.includedChats + val threadIdsNotIncluded = threadIds.filterNot { threadId -> + includedChats?.contains(threadId) ?: false + } + SignalDatabase.chatFolders.addToFolder(folderId, threadIdsNotIncluded) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/ChatFolderTables.kt b/app/src/main/java/org/thoughtcrime/securesms/database/ChatFolderTables.kt index 0f09ef7d68..6028387235 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/ChatFolderTables.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/ChatFolderTables.kt @@ -380,15 +380,17 @@ class ChatFolderTables(context: Context?, databaseHelper: SignalDatabase?) : Dat /** * Adds a thread to a chat folder */ - fun addToFolder(folderId: Long, threadId: Long) { + fun addToFolder(folderId: Long, threadIds: List) { writableDatabase.withinTransaction { db -> - db.insertInto(ChatFolderMembershipTable.TABLE_NAME) - .values( - ChatFolderMembershipTable.CHAT_FOLDER_ID to folderId, - ChatFolderMembershipTable.THREAD_ID to threadId, - ChatFolderMembershipTable.MEMBERSHIP_TYPE to MembershipType.INCLUDED.value - ) - .run(SQLiteDatabase.CONFLICT_REPLACE) + threadIds.forEach { threadId -> + db.insertInto(ChatFolderMembershipTable.TABLE_NAME) + .values( + ChatFolderMembershipTable.CHAT_FOLDER_ID to folderId, + ChatFolderMembershipTable.THREAD_ID to threadId, + ChatFolderMembershipTable.MEMBERSHIP_TYPE to MembershipType.INCLUDED.value + ) + .run(SQLiteDatabase.CONFLICT_REPLACE) + } AppDependencies.databaseObserver.notifyChatFolderObservers() } diff --git a/app/src/main/res/navigation/app_settings_with_change_number.xml b/app/src/main/res/navigation/app_settings_with_change_number.xml index b65253f301..dd2f1f8c12 100644 --- a/app/src/main/res/navigation/app_settings_with_change_number.xml +++ b/app/src/main/res/navigation/app_settings_with_change_number.xml @@ -427,8 +427,9 @@ app:argType="long" /> + android:name="thread_ids" + app:argType="long[]" + app:nullable="true" /> Choose a folder Added to \"%1$s\" - - This chat is already in the folder \"%1$s\" + + + This chat is already in \"%1$s\" + These chats are already in \"%1$s\" + Notification profile