mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-03-01 14:16:49 +00:00
Fix various bugs for chat folders.
This commit is contained in:
committed by
Greyson Parrelli
parent
b519bf6772
commit
dd4fcffec4
@@ -19,7 +19,8 @@ import org.thoughtcrime.securesms.recipients.Recipient
|
||||
@Composable
|
||||
fun AvatarImage(
|
||||
recipient: Recipient,
|
||||
modifier: Modifier = Modifier
|
||||
modifier: Modifier = Modifier,
|
||||
useProfile: Boolean = true
|
||||
) {
|
||||
if (LocalInspectionMode.current) {
|
||||
Spacer(
|
||||
@@ -31,7 +32,11 @@ fun AvatarImage(
|
||||
factory = ::AvatarImageView,
|
||||
modifier = modifier.background(color = Color.Transparent, shape = CircleShape)
|
||||
) {
|
||||
it.setAvatarUsingProfile(recipient)
|
||||
if (useProfile) {
|
||||
it.setAvatarUsingProfile(recipient)
|
||||
} else {
|
||||
it.setAvatar(recipient)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,25 +18,27 @@ object ChatFolderContextMenu {
|
||||
anchorView: View,
|
||||
rootView: ViewGroup = anchorView.rootView as ViewGroup,
|
||||
folderType: ChatFolderRecord.FolderType,
|
||||
unreadCount: Int,
|
||||
isMuted: Boolean,
|
||||
onEdit: () -> Unit = {},
|
||||
onAdd: () -> Unit = {},
|
||||
onMuteAll: () -> Unit = {},
|
||||
onUnmuteAll: () -> Unit = {},
|
||||
onReadAll: () -> Unit = {},
|
||||
onDelete: () -> Unit = {},
|
||||
onReorder: () -> Unit = {}
|
||||
onFolderSettings: () -> Unit = {}
|
||||
) {
|
||||
show(
|
||||
context = context,
|
||||
anchorView = anchorView,
|
||||
rootView = rootView,
|
||||
folderType = folderType,
|
||||
unreadCount = unreadCount,
|
||||
isMuted = isMuted,
|
||||
callbacks = object : Callbacks {
|
||||
override fun onEdit() = onEdit()
|
||||
override fun onAdd() = onAdd()
|
||||
override fun onMuteAll() = onMuteAll()
|
||||
override fun onUnmuteAll() = onUnmuteAll()
|
||||
override fun onReadAll() = onReadAll()
|
||||
override fun onDelete() = onDelete()
|
||||
override fun onReorder() = onReorder()
|
||||
override fun onFolderSettings() = onFolderSettings()
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -45,29 +47,38 @@ object ChatFolderContextMenu {
|
||||
context: Context,
|
||||
anchorView: View,
|
||||
rootView: ViewGroup,
|
||||
unreadCount: Int,
|
||||
isMuted: Boolean,
|
||||
folderType: ChatFolderRecord.FolderType,
|
||||
callbacks: Callbacks
|
||||
) {
|
||||
val actions = mutableListOf<ActionItem>().apply {
|
||||
if (folderType == ChatFolderRecord.FolderType.ALL) {
|
||||
add(
|
||||
ActionItem(R.drawable.symbol_plus_24, context.getString(R.string.ChatFoldersFragment__add_new_folder)) {
|
||||
callbacks.onAdd()
|
||||
}
|
||||
)
|
||||
add(
|
||||
ActionItem(R.drawable.symbol_bell_slash_24, context.getString(R.string.ChatFoldersFragment__mute_all)) {
|
||||
callbacks.onMuteAll()
|
||||
}
|
||||
)
|
||||
if (unreadCount > 0) {
|
||||
add(
|
||||
ActionItem(R.drawable.symbol_chat_check, context.getString(R.string.ChatFoldersFragment__mark_all_read)) {
|
||||
callbacks.onReadAll()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if (isMuted) {
|
||||
add(
|
||||
ActionItem(R.drawable.symbol_exchange_24, context.getString(R.string.ChatFoldersFragment__reorder_folder)) {
|
||||
callbacks.onReorder()
|
||||
ActionItem(R.drawable.symbol_bell_24, context.getString(R.string.ChatFoldersFragment__unmute_all)) {
|
||||
callbacks.onUnmuteAll()
|
||||
}
|
||||
)
|
||||
} else {
|
||||
add(
|
||||
ActionItem(R.drawable.symbol_bell_slash_24, context.getString(R.string.ChatFoldersFragment__mute_all)) {
|
||||
callbacks.onMuteAll()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if (folderType == ChatFolderRecord.FolderType.ALL) {
|
||||
add(
|
||||
ActionItem(R.drawable.symbol_folder_settings, context.getString(R.string.conversation_list_fragment__folder_settings)) {
|
||||
callbacks.onFolderSettings()
|
||||
}
|
||||
)
|
||||
} else {
|
||||
@@ -76,31 +87,6 @@ object ChatFolderContextMenu {
|
||||
callbacks.onEdit()
|
||||
}
|
||||
)
|
||||
add(
|
||||
ActionItem(R.drawable.symbol_plus_24, context.getString(R.string.ChatFoldersFragment__add_new_folder)) {
|
||||
callbacks.onAdd()
|
||||
}
|
||||
)
|
||||
add(
|
||||
ActionItem(R.drawable.symbol_bell_slash_24, context.getString(R.string.ChatFoldersFragment__mute_all)) {
|
||||
callbacks.onMuteAll()
|
||||
}
|
||||
)
|
||||
add(
|
||||
ActionItem(R.drawable.symbol_chat_check, context.getString(R.string.ChatFoldersFragment__mark_all_read)) {
|
||||
callbacks.onReadAll()
|
||||
}
|
||||
)
|
||||
add(
|
||||
ActionItem(R.drawable.symbol_trash_24, context.getString(R.string.ChatFoldersFragment__delete_folder)) {
|
||||
callbacks.onDelete()
|
||||
}
|
||||
)
|
||||
add(
|
||||
ActionItem(R.drawable.symbol_exchange_24, context.getString(R.string.ChatFoldersFragment__reorder_folder)) {
|
||||
callbacks.onReorder()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,10 +99,9 @@ object ChatFolderContextMenu {
|
||||
|
||||
private interface Callbacks {
|
||||
fun onEdit()
|
||||
fun onAdd()
|
||||
fun onMuteAll()
|
||||
fun onUnmuteAll()
|
||||
fun onReadAll()
|
||||
fun onDelete()
|
||||
fun onReorder()
|
||||
fun onFolderSettings()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,24 +104,22 @@ fun FoldersScreen(
|
||||
}
|
||||
|
||||
Column(modifier = modifier.verticalScroll(rememberScrollState())) {
|
||||
Column(modifier = Modifier.padding(start = 24.dp)) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.ChatFoldersFragment__organize_your_chats),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.padding(top = 12.dp, bottom = 12.dp, end = 12.dp)
|
||||
)
|
||||
Text(
|
||||
text = stringResource(id = R.string.ChatFoldersFragment__folders),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
modifier = Modifier.padding(top = 16.dp, bottom = 12.dp)
|
||||
)
|
||||
FolderRow(
|
||||
icon = R.drawable.symbol_plus_compact_16,
|
||||
title = stringResource(R.string.ChatFoldersFragment__create_a_folder),
|
||||
onClick = { onFolderClicked(ChatFolderRecord()) }
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = stringResource(id = R.string.ChatFoldersFragment__organize_your_chats),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.padding(top = 12.dp, bottom = 12.dp, end = 12.dp, start = 24.dp)
|
||||
)
|
||||
Text(
|
||||
text = stringResource(id = R.string.ChatFoldersFragment__folders),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
modifier = Modifier.padding(top = 16.dp, bottom = 12.dp, start = 24.dp)
|
||||
)
|
||||
FolderRow(
|
||||
icon = R.drawable.symbol_plus_compact_16,
|
||||
title = stringResource(R.string.ChatFoldersFragment__create_a_folder),
|
||||
onClick = { onFolderClicked(ChatFolderRecord()) }
|
||||
)
|
||||
|
||||
val columnHeight = dimensionResource(id = R.dimen.chat_folder_row_height).value * state.folders.size
|
||||
LazyColumn(
|
||||
@@ -142,8 +140,7 @@ fun FoldersScreen(
|
||||
{ onFolderClicked(folder) }
|
||||
} else null,
|
||||
elevation = elevation,
|
||||
showDragHandle = true,
|
||||
modifier = Modifier.padding(start = 12.dp)
|
||||
showDragHandle = true
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -167,8 +164,7 @@ fun FoldersScreen(
|
||||
icon = R.drawable.symbol_chat_badge_24,
|
||||
title = title,
|
||||
subtitle = stringResource(R.string.ChatFoldersFragment__unread_messages),
|
||||
onAdd = { onAdd(chatFolder) },
|
||||
modifier = Modifier.padding(start = 12.dp)
|
||||
onAdd = { onAdd(chatFolder) }
|
||||
)
|
||||
}
|
||||
ChatFolderRecord.FolderType.INDIVIDUAL -> {
|
||||
@@ -177,8 +173,7 @@ fun FoldersScreen(
|
||||
icon = R.drawable.symbol_person_light_24,
|
||||
title = title,
|
||||
subtitle = stringResource(R.string.ChatFoldersFragment__only_direct_messages),
|
||||
onAdd = { onAdd(chatFolder) },
|
||||
modifier = Modifier.padding(start = 12.dp)
|
||||
onAdd = { onAdd(chatFolder) }
|
||||
)
|
||||
}
|
||||
ChatFolderRecord.FolderType.GROUP -> {
|
||||
@@ -187,8 +182,7 @@ fun FoldersScreen(
|
||||
icon = R.drawable.symbol_group_light_20,
|
||||
title = title,
|
||||
subtitle = stringResource(R.string.ChatFoldersFragment__only_group_messages),
|
||||
onAdd = { onAdd(chatFolder) },
|
||||
modifier = Modifier.padding(start = 12.dp)
|
||||
onAdd = { onAdd(chatFolder) }
|
||||
)
|
||||
}
|
||||
ChatFolderRecord.FolderType.ALL -> {
|
||||
@@ -239,17 +233,19 @@ fun FolderRow(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = if (onClick != null) {
|
||||
modifier
|
||||
.padding(end = 12.dp)
|
||||
.clickable(onClick = onClick)
|
||||
.fillMaxWidth()
|
||||
.defaultMinSize(minHeight = dimensionResource(id = R.dimen.chat_folder_row_height))
|
||||
.shadow(elevation = elevation)
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
.padding(start = 24.dp, end = 12.dp)
|
||||
} else {
|
||||
modifier
|
||||
.padding(end = 12.dp)
|
||||
.fillMaxWidth()
|
||||
.defaultMinSize(minHeight = dimensionResource(id = R.dimen.chat_folder_row_height))
|
||||
.shadow(elevation = elevation)
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
.padding(start = 24.dp, end = 12.dp)
|
||||
}
|
||||
) {
|
||||
Image(
|
||||
|
||||
@@ -7,8 +7,8 @@ import org.thoughtcrime.securesms.database.SignalDatabase
|
||||
*/
|
||||
object ChatFoldersRepository {
|
||||
|
||||
fun getCurrentFolders(includeUnreadCount: Boolean = false): List<ChatFolderRecord> {
|
||||
return SignalDatabase.chatFolders.getChatFolders(includeUnreadCount)
|
||||
fun getCurrentFolders(includeUnreadAndMutedCounts: Boolean = false): List<ChatFolderRecord> {
|
||||
return SignalDatabase.chatFolders.getChatFolders(includeUnreadAndMutedCounts)
|
||||
}
|
||||
|
||||
fun createFolder(folder: ChatFolderRecord) {
|
||||
|
||||
@@ -24,7 +24,7 @@ class ChatFoldersViewModel : ViewModel() {
|
||||
|
||||
fun loadCurrentFolders(context: Context) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
val folders = ChatFoldersRepository.getCurrentFolders(includeUnreadCount = false)
|
||||
val folders = ChatFoldersRepository.getCurrentFolders(includeUnreadAndMutedCounts = false)
|
||||
val suggestedFolders = getSuggestedFolders(context, folders)
|
||||
|
||||
internalState.update {
|
||||
|
||||
@@ -21,6 +21,7 @@ import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Person
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Switch
|
||||
@@ -229,16 +230,14 @@ fun CreateFolderScreen(
|
||||
FolderRow(
|
||||
icon = R.drawable.symbol_plus_compact_16,
|
||||
title = stringResource(R.string.CreateFoldersFragment__add_chats),
|
||||
onClick = onAddChat,
|
||||
modifier = Modifier.padding(start = 12.dp)
|
||||
onClick = onAddChat
|
||||
)
|
||||
|
||||
if (state.currentFolder.showIndividualChats) {
|
||||
FolderRow(
|
||||
icon = R.drawable.symbol_person_light_24,
|
||||
title = stringResource(R.string.ChatFoldersFragment__one_on_one_chats),
|
||||
onClick = onAddChat,
|
||||
modifier = Modifier.padding(start = 12.dp)
|
||||
onClick = onAddChat
|
||||
)
|
||||
}
|
||||
|
||||
@@ -246,8 +245,7 @@ fun CreateFolderScreen(
|
||||
FolderRow(
|
||||
icon = R.drawable.symbol_group_light_20,
|
||||
title = stringResource(R.string.ChatFoldersFragment__groups),
|
||||
onClick = onAddChat,
|
||||
modifier = Modifier.padding(start = 12.dp)
|
||||
onClick = onAddChat
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -277,8 +275,7 @@ fun CreateFolderScreen(
|
||||
FolderRow(
|
||||
icon = R.drawable.symbol_plus_compact_16,
|
||||
title = stringResource(R.string.CreateFoldersFragment__exclude_chats),
|
||||
onClick = onRemoveChat,
|
||||
modifier = Modifier.padding(start = 12.dp)
|
||||
onClick = onRemoveChat
|
||||
)
|
||||
}
|
||||
|
||||
@@ -334,6 +331,9 @@ fun CreateFolderScreen(
|
||||
}
|
||||
} else if (!isNewFolder) {
|
||||
Buttons.MediumTonal(
|
||||
colors = ButtonDefaults.filledTonalButtonColors(
|
||||
disabledContainerColor = MaterialTheme.colorScheme.surfaceVariant
|
||||
),
|
||||
enabled = hasChanges,
|
||||
onClick = { onCreateConfirmed(true) },
|
||||
modifier = modifier
|
||||
@@ -451,10 +451,11 @@ fun ChatRow(
|
||||
recipient = recipient,
|
||||
modifier = Modifier
|
||||
.padding(start = 24.dp, end = 16.dp)
|
||||
.size(40.dp)
|
||||
.size(40.dp),
|
||||
useProfile = false
|
||||
)
|
||||
}
|
||||
|
||||
Text(text = recipient.getShortDisplayName(LocalContext.current))
|
||||
Text(text = if (recipient.isSelf) stringResource(id = R.string.note_to_self) else recipient.getShortDisplayName(LocalContext.current))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ object SelectedContacts {
|
||||
private val chip: ContactChip = itemView.findViewById(R.id.contact_chip)
|
||||
|
||||
override fun bind(model: RecipientModel) {
|
||||
chip.text = model.recipient.getShortDisplayName(context)
|
||||
chip.text = if (model.recipient.isSelf) context.getString(R.string.note_to_self) else model.recipient.getShortDisplayName(context)
|
||||
chip.setContact(model.selectedContact)
|
||||
chip.isCloseIconVisible = true
|
||||
chip.setOnCloseIconClickListener {
|
||||
|
||||
@@ -42,17 +42,18 @@ class ChatFolderAdapter(val callbacks: Callbacks) : MappingAdapter() {
|
||||
context = itemView.context,
|
||||
anchorView = view,
|
||||
folderType = model.chatFolder.folderType,
|
||||
unreadCount = folder.unreadCount,
|
||||
isMuted = folder.isMuted,
|
||||
onEdit = { callbacks.onEdit(model.chatFolder) },
|
||||
onAdd = { callbacks.onAdd() },
|
||||
onMuteAll = { callbacks.onMuteAll(model.chatFolder) },
|
||||
onUnmuteAll = { callbacks.onUnmuteAll(model.chatFolder) },
|
||||
onReadAll = { callbacks.onReadAll(model.chatFolder) },
|
||||
onDelete = { callbacks.onDelete(model.chatFolder) },
|
||||
onReorder = { callbacks.onReorder() }
|
||||
onFolderSettings = { callbacks.onFolderSettings() }
|
||||
)
|
||||
true
|
||||
}
|
||||
if (model.isSelected) {
|
||||
itemView.backgroundTintList = ColorStateList.valueOf(ContextCompat.getColor(itemView.context, R.color.signal_colorSurfaceVariant))
|
||||
itemView.backgroundTintList = ColorStateList.valueOf(ContextCompat.getColor(itemView.context, R.color.signal_colorSurface2))
|
||||
} else {
|
||||
itemView.backgroundTintList = ColorStateList.valueOf(ContextCompat.getColor(itemView.context, R.color.transparent))
|
||||
}
|
||||
@@ -70,10 +71,9 @@ class ChatFolderAdapter(val callbacks: Callbacks) : MappingAdapter() {
|
||||
interface Callbacks {
|
||||
fun onChatFolderClicked(chatFolder: ChatFolderRecord)
|
||||
fun onEdit(chatFolder: ChatFolderRecord)
|
||||
fun onAdd()
|
||||
fun onMuteAll(chatFolder: ChatFolderRecord)
|
||||
fun onUnmuteAll(chatFolder: ChatFolderRecord)
|
||||
fun onReadAll(chatFolder: ChatFolderRecord)
|
||||
fun onDelete(chatFolder: ChatFolderRecord)
|
||||
fun onReorder()
|
||||
fun onFolderSettings()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
@@ -64,6 +65,7 @@ import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.navigation.fragment.NavHostFragment;
|
||||
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.LinearSmoothScroller;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.airbnb.lottie.SimpleColorFilter;
|
||||
@@ -243,6 +245,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode
|
||||
private SignalContextMenu activeContextMenu;
|
||||
private LifecycleDisposable lifecycleDisposable;
|
||||
private ChatFolderAdapter chatFolderAdapter;
|
||||
private RecyclerView.SmoothScroller smoothScroller;
|
||||
|
||||
protected ConversationListArchiveItemDecoration archiveDecoration;
|
||||
protected ConversationListItemAnimator itemAnimator;
|
||||
@@ -458,7 +461,22 @@ public class ConversationListFragment extends MainFragment implements ActionMode
|
||||
}
|
||||
}));
|
||||
|
||||
requireCallback().bindScrollHelper(list);
|
||||
requireCallback().bindScrollHelper(list, chatFolderList, color -> {
|
||||
for (int i = 0; i < chatFolderList.getChildCount(); i++) {
|
||||
View child = chatFolderList.getChildAt(i);
|
||||
if (child != null && child.isSelected()) {
|
||||
child.setBackgroundTintList(ColorStateList.valueOf(color));
|
||||
}
|
||||
}
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
|
||||
smoothScroller = new LinearSmoothScroller(requireContext()) {
|
||||
@Override
|
||||
protected int calculateTimeForScrolling(int dx) {
|
||||
return 150;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1048,6 +1066,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode
|
||||
}
|
||||
|
||||
private void onChatFoldersChanged(List<ChatFolderMappingModel> folders) {
|
||||
chatFolderList.setVisibility(folders.size() > 1 ? View.VISIBLE : View.GONE);
|
||||
chatFolderAdapter.submitList(new ArrayList<>(folders));
|
||||
}
|
||||
|
||||
@@ -1673,7 +1692,35 @@ public class ConversationListFragment extends MainFragment implements ActionMode
|
||||
|
||||
@Override
|
||||
public void onChatFolderClicked(@NonNull ChatFolderRecord chatFolder) {
|
||||
int oldIndex = -1;
|
||||
int newIndex = -1;
|
||||
|
||||
for (int i = 0; i < viewModel.getFolders().size(); i++) {
|
||||
if (oldIndex != -1 && newIndex != -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
ChatFolderMappingModel folder = viewModel.getFolders().get(i);
|
||||
if (folder.isSelected()) {
|
||||
oldIndex = i;
|
||||
}
|
||||
if (folder.getChatFolder().getId() == chatFolder.getId()) {
|
||||
newIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (oldIndex < newIndex) {
|
||||
smoothScroller.setTargetPosition(Math.min(newIndex + 1, viewModel.getFolders().size()));
|
||||
} else {
|
||||
smoothScroller.setTargetPosition(Math.max(newIndex - 1, 0));
|
||||
}
|
||||
|
||||
if (chatFolderList.getLayoutManager() != null) {
|
||||
chatFolderList.getLayoutManager().startSmoothScroll(smoothScroller);
|
||||
}
|
||||
|
||||
viewModel.select(chatFolder);
|
||||
list.smoothScrollToPosition(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1682,13 +1729,13 @@ public class ConversationListFragment extends MainFragment implements ActionMode
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAdd() {
|
||||
startActivity(AppSettingsActivity.createChatFolder(requireContext(), -1));
|
||||
public void onMuteAll(@NonNull ChatFolderRecord chatFolder) {
|
||||
MuteDialog.show(requireContext(), until -> viewModel.onUpdateMute(chatFolder, until));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMuteAll(@NonNull ChatFolderRecord chatFolder) {
|
||||
MuteDialog.show(requireContext(), until -> viewModel.onMuteChatFolder(chatFolder, until));
|
||||
public void onUnmuteAll(@NonNull ChatFolderRecord chatFolder) {
|
||||
viewModel.onUpdateMute(chatFolder, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1701,16 +1748,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDelete(@NonNull ChatFolderRecord chatFolder) {
|
||||
new MaterialAlertDialogBuilder(requireActivity())
|
||||
.setMessage(getString(R.string.CreateFoldersFragment__delete_this_chat_folder))
|
||||
.setPositiveButton(R.string.delete, (dialog, which) -> viewModel.deleteChatFolder(chatFolder))
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReorder() {
|
||||
public void onFolderSettings() {
|
||||
startActivity(AppSettingsActivity.chatFolders(requireContext()));
|
||||
}
|
||||
|
||||
@@ -1763,6 +1801,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode
|
||||
if (viewHolder.itemView instanceof ConversationListItemAction ||
|
||||
viewHolder instanceof ConversationListAdapter.HeaderViewHolder ||
|
||||
viewHolder instanceof ClearFilterViewHolder ||
|
||||
viewHolder instanceof ConversationListAdapter.EmptyFolderViewHolder ||
|
||||
actionMode != null ||
|
||||
viewHolder.itemView.isSelected() ||
|
||||
activeAdapter == searchAdapter)
|
||||
|
||||
@@ -8,7 +8,6 @@ import io.reactivex.rxjava3.core.BackpressureStrategy
|
||||
import io.reactivex.rxjava3.core.Flowable
|
||||
import io.reactivex.rxjava3.core.Observable
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable
|
||||
import io.reactivex.rxjava3.kotlin.Flowables
|
||||
import io.reactivex.rxjava3.kotlin.addTo
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -88,6 +87,7 @@ class ConversationListViewModel(
|
||||
conversationListDataSource = store
|
||||
.stateFlowable
|
||||
.subscribeOn(Schedulers.io())
|
||||
.filter { it.currentFolder.id != -1L }
|
||||
.map { it.filterRequest to it.currentFolder }
|
||||
.distinctUntilChanged()
|
||||
.map { (filterRequest, folder) ->
|
||||
@@ -115,7 +115,7 @@ class ConversationListViewModel(
|
||||
.subscribe { controller.onDataInvalidated() }
|
||||
.addTo(disposables)
|
||||
|
||||
Flowables.combineLatest(
|
||||
Flowable.merge(
|
||||
RxDatabaseObserver
|
||||
.conversationList
|
||||
.debounce(250, TimeUnit.MILLISECONDS),
|
||||
@@ -219,7 +219,7 @@ class ConversationListViewModel(
|
||||
|
||||
private fun loadCurrentFolders() {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
val folders = ChatFoldersRepository.getCurrentFolders(includeUnreadCount = true)
|
||||
val folders = ChatFoldersRepository.getCurrentFolders(includeUnreadAndMutedCounts = true)
|
||||
|
||||
val selectedFolderId = if (currentFolder.id == -1L) {
|
||||
folders.firstOrNull()?.id
|
||||
@@ -262,7 +262,7 @@ class ConversationListViewModel(
|
||||
}
|
||||
}
|
||||
|
||||
fun onMuteChatFolder(chatFolder: ChatFolderRecord, until: Long) {
|
||||
fun onUpdateMute(chatFolder: ChatFolderRecord, until: Long) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
val ids = SignalDatabase.threads.getRecipientIdsByChatFolder(chatFolder)
|
||||
val recipientIds: List<RecipientId> = ids.filter { id ->
|
||||
@@ -274,20 +274,6 @@ class ConversationListViewModel(
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteChatFolder(chatFolder: ChatFolderRecord) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
SignalDatabase.chatFolders.deleteChatFolder(chatFolder)
|
||||
val updatedFolders = folders.filter { folder -> folder.chatFolder.id != chatFolder.id }
|
||||
|
||||
store.update {
|
||||
it.copy(
|
||||
currentFolder = updatedFolders.first().chatFolder,
|
||||
chatFolders = updatedFolders
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun markChatFolderRead(chatFolder: ChatFolderRecord) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
val ids = SignalDatabase.threads.getThreadIdsByChatFolder(chatFolder)
|
||||
|
||||
@@ -152,7 +152,7 @@ class ChatFolderTables(context: Context?, databaseHelper: SignalDatabase?) : Dat
|
||||
/**
|
||||
* Maps the chat folder ids to its corresponding chat folder
|
||||
*/
|
||||
fun getChatFolders(includeUnreads: Boolean = false): List<ChatFolderRecord> {
|
||||
fun getChatFolders(includeUnreadAndMutedCount: Boolean = false): List<ChatFolderRecord> {
|
||||
val includedChats: Map<Long, List<Long>> = getIncludedChats()
|
||||
val excludedChats: Map<Long, List<Long>> = getExcludedChats()
|
||||
|
||||
@@ -178,10 +178,11 @@ class ChatFolderTables(context: Context?, databaseHelper: SignalDatabase?) : Dat
|
||||
)
|
||||
}
|
||||
|
||||
if (includeUnreads) {
|
||||
if (includeUnreadAndMutedCount) {
|
||||
return folders.map { folder ->
|
||||
folder.copy(
|
||||
unreadCount = SignalDatabase.threads.getUnreadCountByChatFolder(folder)
|
||||
unreadCount = SignalDatabase.threads.getUnreadCountByChatFolder(folder),
|
||||
isMuted = !SignalDatabase.threads.hasUnmutedChatsInFolder(folder)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import org.signal.core.util.exists
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.signal.core.util.or
|
||||
import org.signal.core.util.readToList
|
||||
import org.signal.core.util.readToSingleBoolean
|
||||
import org.signal.core.util.readToSingleInt
|
||||
import org.signal.core.util.readToSingleLong
|
||||
import org.signal.core.util.requireBoolean
|
||||
@@ -631,6 +632,26 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
|
||||
return allCount + forcedUnreadCount
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not there are any unmuted chats in a chat folder
|
||||
*/
|
||||
fun hasUnmutedChatsInFolder(folder: ChatFolderRecord): Boolean {
|
||||
val chatFolderQuery = folder.toQuery()
|
||||
|
||||
val unmutedChats =
|
||||
"""
|
||||
SELECT COUNT(${RecipientTable.MUTE_UNTIL})
|
||||
FROM $TABLE_NAME
|
||||
LEFT OUTER JOIN ${RecipientTable.TABLE_NAME} ON $TABLE_NAME.$RECIPIENT_ID = ${RecipientTable.TABLE_NAME}.${RecipientTable.ID}
|
||||
WHERE
|
||||
$ARCHIVED = 0 AND
|
||||
${RecipientTable.MUTE_UNTIL} = 0
|
||||
$chatFolderQuery
|
||||
"""
|
||||
|
||||
return readableDatabase.rawQuery(unmutedChats, null).readToSingleBoolean()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of unread messages across all threads within a chat folder
|
||||
* Threads that are forced-unread count as 1.
|
||||
|
||||
@@ -386,4 +386,14 @@ class MainActivityListHostFragment : Fragment(R.layout.main_activity_list_host_f
|
||||
viewLifecycleOwner
|
||||
).attach(recyclerView)
|
||||
}
|
||||
|
||||
override fun bindScrollHelper(recyclerView: RecyclerView, chatFolders: RecyclerView, setChatFolder: (Int) -> Unit) {
|
||||
Material3OnScrollHelper(
|
||||
activity = requireActivity(),
|
||||
views = listOf(_toolbarBackground, chatFolders),
|
||||
viewStubs = listOf(_searchToolbar),
|
||||
lifecycleOwner = viewLifecycleOwner,
|
||||
setChatFolderColor = setChatFolder
|
||||
).attach(recyclerView)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,4 +4,5 @@ import androidx.recyclerview.widget.RecyclerView
|
||||
|
||||
interface Material3OnScrollHelperBinder {
|
||||
fun bindScrollHelper(recyclerView: RecyclerView)
|
||||
fun bindScrollHelper(recyclerView: RecyclerView, chatFolders: RecyclerView, setChatFolder: (Int) -> Unit)
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ open class Material3OnScrollHelper(
|
||||
private val context: Context,
|
||||
private val setStatusBarColor: (Int) -> Unit,
|
||||
private val getStatusBarColor: () -> Int,
|
||||
private val setChatFolderColor: (Int) -> Unit = {},
|
||||
private val views: List<View>,
|
||||
private val viewStubs: List<Stub<out View>> = emptyList(),
|
||||
lifecycleOwner: LifecycleOwner
|
||||
@@ -33,16 +34,39 @@ open class Material3OnScrollHelper(
|
||||
constructor(activity: Activity, view: View, lifecycleOwner: LifecycleOwner) : this(activity = activity, views = listOf(view), lifecycleOwner = lifecycleOwner)
|
||||
|
||||
constructor(activity: Activity, views: List<View>, viewStubs: List<Stub<out View>> = emptyList(), lifecycleOwner: LifecycleOwner) : this(
|
||||
activity = activity,
|
||||
views = views,
|
||||
viewStubs = viewStubs,
|
||||
lifecycleOwner = lifecycleOwner,
|
||||
setChatFolderColor = {}
|
||||
)
|
||||
|
||||
constructor(
|
||||
activity: Activity,
|
||||
views: List<View>,
|
||||
viewStubs: List<Stub<out View>> = emptyList(),
|
||||
lifecycleOwner: LifecycleOwner,
|
||||
setChatFolderColor: (Int) -> Unit = {}
|
||||
) : this(
|
||||
context = activity,
|
||||
setStatusBarColor = { WindowUtil.setStatusBarColor(activity.window, it) },
|
||||
getStatusBarColor = { WindowUtil.getStatusBarColor(activity.window) },
|
||||
setChatFolderColor = setChatFolderColor,
|
||||
views = views,
|
||||
viewStubs = viewStubs,
|
||||
lifecycleOwner = lifecycleOwner
|
||||
)
|
||||
|
||||
open val activeColorSet: ColorSet = ColorSet(R.color.signal_colorSurface2)
|
||||
open val inactiveColorSet: ColorSet = ColorSet(R.color.signal_colorBackground)
|
||||
open val activeColorSet: ColorSet = ColorSet(
|
||||
toolbarColorRes = R.color.signal_colorSurface2,
|
||||
statusBarColorRes = R.color.signal_colorSurface2,
|
||||
chatFolderColorRes = R.color.signal_colorBackground
|
||||
)
|
||||
open val inactiveColorSet: ColorSet = ColorSet(
|
||||
toolbarColorRes = R.color.signal_colorBackground,
|
||||
statusBarColorRes = R.color.signal_colorBackground,
|
||||
chatFolderColorRes = R.color.signal_colorSurface2
|
||||
)
|
||||
|
||||
protected var previousStatusBarColor: Int = getStatusBarColor()
|
||||
|
||||
@@ -94,6 +118,7 @@ open class Material3OnScrollHelper(
|
||||
val colorSet = if (active == true) activeColorSet else inactiveColorSet
|
||||
setToolbarColor(ContextCompat.getColor(context, colorSet.toolbarColorRes))
|
||||
setStatusBarColor(ContextCompat.getColor(context, colorSet.statusBarColorRes))
|
||||
setChatFolderColor(ContextCompat.getColor(context, colorSet.chatFolderColorRes))
|
||||
}
|
||||
|
||||
private fun updateActiveState(isActive: Boolean) {
|
||||
@@ -118,12 +143,15 @@ open class Material3OnScrollHelper(
|
||||
val endToolbarColor = ContextCompat.getColor(context, endColorSet.toolbarColorRes)
|
||||
val startStatusBarColor = ContextCompat.getColor(context, startColorSet.statusBarColorRes)
|
||||
val endStatusBarColor = ContextCompat.getColor(context, endColorSet.statusBarColorRes)
|
||||
val startChatFolderColor = ContextCompat.getColor(context, startColorSet.chatFolderColorRes)
|
||||
val endChatFolderColor = ContextCompat.getColor(context, endColorSet.chatFolderColorRes)
|
||||
|
||||
animator = ValueAnimator.ofFloat(0f, 1f).apply {
|
||||
duration = 200
|
||||
addUpdateListener {
|
||||
setToolbarColor(ArgbEvaluatorCompat.getInstance().evaluate(it.animatedFraction, startToolbarColor, endToolbarColor))
|
||||
setStatusBarColor(ArgbEvaluatorCompat.getInstance().evaluate(it.animatedFraction, startStatusBarColor, endStatusBarColor))
|
||||
setChatFolderColor(ArgbEvaluatorCompat.getInstance().evaluate(it.animatedFraction, startChatFolderColor, endChatFolderColor))
|
||||
}
|
||||
start()
|
||||
}
|
||||
@@ -157,8 +185,10 @@ open class Material3OnScrollHelper(
|
||||
*/
|
||||
data class ColorSet(
|
||||
@ColorRes val toolbarColorRes: Int,
|
||||
@ColorRes val statusBarColorRes: Int
|
||||
@ColorRes val statusBarColorRes: Int,
|
||||
@ColorRes val chatFolderColorRes: Int
|
||||
) {
|
||||
constructor(@ColorRes color: Int) : this(color, color)
|
||||
constructor(@ColorRes toolbarColorRes: Int, @ColorRes statusBarColorRes: Int) : this(toolbarColorRes, statusBarColorRes, toolbarColorRes)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user