Add new bottom actionbar to chat list.

This commit is contained in:
Greyson Parrelli
2021-10-21 10:55:33 -04:00
parent 2167522f7d
commit f533a898f5
23 changed files with 335 additions and 199 deletions

View File

@@ -0,0 +1,13 @@
package org.thoughtcrime.securesms.components.menu
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
/**
* Represents an action to be rendered via [SignalContextMenu] or [SignalBottomActionBar]
*/
data class ActionItem(
@DrawableRes val iconRes: Int,
@StringRes val titleRes: Int,
val action: Runnable
)

View File

@@ -0,0 +1,92 @@
package org.thoughtcrime.securesms.components.menu
import android.content.Context
import android.os.Build
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.util.ViewUtil
/**
* A bar that displays a set of action buttons. Intended as a replacement for ActionModes, this gives you a simple interface to add a bunch of actions, and
* the bar itself will handle putting things in the overflow and whatnot.
*
* Overflow items are rendered in a [SignalContextMenu].
*/
class SignalBottomActionBar(context: Context, attributeSet: AttributeSet) : LinearLayout(context, attributeSet) {
val items: MutableList<ActionItem> = mutableListOf()
init {
orientation = HORIZONTAL
setBackgroundResource(R.drawable.signal_bottom_action_bar_background)
if (Build.VERSION.SDK_INT >= 21) {
elevation = 20f
}
}
fun setItems(items: List<ActionItem>) {
this.items.clear()
this.items.addAll(items)
present(this.items)
}
private fun present(items: List<ActionItem>) {
if (width == 0) {
post { present(items) }
return
}
val widthDp: Float = ViewUtil.pxToDp(width.toFloat())
val minButtonWidthDp = 70
val maxButtons: Int = (widthDp / minButtonWidthDp).toInt()
val usableButtonCount = when {
items.size <= maxButtons -> items.size
else -> maxButtons - 1
}
val renderableItems: List<ActionItem> = items.subList(0, usableButtonCount)
val overflowItems: List<ActionItem> = if (renderableItems.size < items.size) items.subList(usableButtonCount, items.size) else emptyList()
removeAllViews()
renderableItems.forEach { item ->
val view: View = LayoutInflater.from(context).inflate(R.layout.signal_bottom_action_bar_item, this, false)
addView(view)
bindItem(view, item)
}
if (overflowItems.isNotEmpty()) {
val view: View = LayoutInflater.from(context).inflate(R.layout.signal_bottom_action_bar_item, this, false)
addView(view)
bindItem(
view,
ActionItem(
iconRes = R.drawable.ic_more_horiz_24,
titleRes = R.string.SignalBottomActionBar_more,
action = {
SignalContextMenu.Builder(view, parent as ViewGroup)
.preferredHorizontalPosition(SignalContextMenu.HorizontalPosition.END)
.offsetY(ViewUtil.dpToPx(8))
.show(overflowItems)
}
)
)
}
}
private fun bindItem(view: View, item: ActionItem) {
val icon: ImageView = view.findViewById(R.id.signal_bottom_action_bar_item_icon)
val title: TextView = view.findViewById(R.id.signal_bottom_action_bar_item_title)
icon.setImageResource(item.iconRes)
title.setText(item.titleRes)
view.setOnClickListener { item.action.run() }
}
}

View File

@@ -1,6 +1,7 @@
package org.thoughtcrime.securesms.components
package org.thoughtcrime.securesms.components.menu
import android.content.Context
import android.graphics.Rect
import android.os.Build
import android.view.LayoutInflater
import android.view.View
@@ -8,8 +9,6 @@ import android.view.ViewGroup
import android.widget.ImageView
import android.widget.PopupWindow
import android.widget.TextView
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
@@ -17,6 +16,7 @@ import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.util.MappingAdapter
import org.thoughtcrime.securesms.util.MappingModel
import org.thoughtcrime.securesms.util.MappingViewHolder
import org.thoughtcrime.securesms.util.ViewUtil
/**
* A custom context menu that will show next to an anchor view and display several options. Basically a PopupMenu with custom UI and positioning rules.
@@ -27,10 +27,11 @@ import org.thoughtcrime.securesms.util.MappingViewHolder
*/
class SignalContextMenu private constructor(
val anchor: View,
val container: View,
val items: List<Item>,
val container: ViewGroup,
val items: List<ActionItem>,
val baseOffsetX: Int = 0,
val baseOffsetY: Int = 0,
val horizontalPosition: HorizontalPosition = HorizontalPosition.START,
val onDismiss: Runnable? = null
) : PopupWindow(
LayoutInflater.from(anchor.context).inflate(R.layout.signal_context_menu, null),
@@ -77,8 +78,14 @@ class SignalContextMenu private constructor(
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
)
val menuBottomBound = anchor.y + anchor.height + contentView.measuredHeight + baseOffsetY
val menuTopBound = anchor.y - contentView.measuredHeight - baseOffsetY
val anchorRect = Rect(anchor.left, anchor.top, anchor.right, anchor.bottom).also {
if (anchor.parent != container) {
container.offsetDescendantRectToMyCoords(anchor, it)
}
}
val menuBottomBound = anchorRect.bottom + contentView.measuredHeight + baseOffsetY
val menuTopBound = anchorRect.top - contentView.measuredHeight - baseOffsetY
val screenBottomBound = container.height
val screenTopBound = container.y
@@ -88,16 +95,33 @@ class SignalContextMenu private constructor(
if (menuBottomBound < screenBottomBound) {
offsetY = baseOffsetY
} else if (menuTopBound > screenTopBound) {
offsetY = -(anchor.height + contentView.measuredHeight + baseOffsetY)
offsetY = -(anchorRect.height() + contentView.measuredHeight + baseOffsetY)
mappingAdapter.submitList(items.reversed().toAdapterItems())
} else {
offsetY = -((anchor.height / 2) + (contentView.measuredHeight / 2) + baseOffsetY)
offsetY = -((anchorRect.height() / 2) + (contentView.measuredHeight / 2) + baseOffsetY)
}
showAsDropDown(anchor, baseOffsetX, offsetY)
val offsetX: Int = when (horizontalPosition) {
HorizontalPosition.START -> {
if (ViewUtil.isLtr(context)) {
baseOffsetX
} else {
-(baseOffsetX + contentView.measuredWidth)
}
}
HorizontalPosition.END -> {
if (ViewUtil.isLtr(context)) {
-(baseOffsetX + contentView.measuredWidth - anchorRect.width())
} else {
baseOffsetX - anchorRect.width()
}
}
}
showAsDropDown(anchor, offsetX, offsetY)
}
private fun List<Item>.toAdapterItems(): List<DisplayItem> {
private fun List<ActionItem>.toAdapterItems(): List<DisplayItem> {
return this.mapIndexed { index, item ->
val displayType: DisplayType = when {
this.size == 1 -> DisplayType.ONLY
@@ -110,14 +134,8 @@ class SignalContextMenu private constructor(
}
}
data class Item(
@DrawableRes val iconRes: Int,
@StringRes val titleRes: Int,
val action: Runnable
)
private data class DisplayItem(
val item: Item,
val item: ActionItem,
val displayType: DisplayType
) : MappingModel<DisplayItem> {
override fun areItemsTheSame(newItem: DisplayItem): Boolean {
@@ -129,7 +147,7 @@ class SignalContextMenu private constructor(
}
}
enum class DisplayType {
private enum class DisplayType {
TOP, BOTTOM, MIDDLE, ONLY
}
@@ -162,18 +180,23 @@ class SignalContextMenu private constructor(
}
}
enum class HorizontalPosition {
START, END
}
/**
* @param anchor The view to put the pop-up on
* @param container A parent of [anchor] that represents the acceptable boundaries of the popup
*/
class Builder(
val anchor: View,
val container: View
val container: ViewGroup
) {
var onDismiss: Runnable? = null
var offsetX: Int = 0
var offsetY: Int = 0
var offsetX = 0
var offsetY = 0
var horizontalPosition = HorizontalPosition.START
fun onDismiss(onDismiss: Runnable): Builder {
this.onDismiss = onDismiss
@@ -190,13 +213,19 @@ class SignalContextMenu private constructor(
return this
}
fun show(items: List<Item>) {
fun preferredHorizontalPosition(horizontalPosition: HorizontalPosition): Builder {
this.horizontalPosition = horizontalPosition
return this
}
fun show(items: List<ActionItem>) {
SignalContextMenu(
anchor = anchor,
container = container,
items = items,
baseOffsetX = offsetX,
baseOffsetY = offsetY,
horizontalPosition = horizontalPosition,
onDismiss = onDismiss
).show()
}

View File

@@ -1855,8 +1855,6 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
private class ActionModeCallback implements ActionMode.Callback {
private int statusBarColor;
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
MenuInflater inflater = mode.getMenuInflater();
@@ -1864,16 +1862,6 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
mode.setTitle(calculateSelectedItemCount());
if (Build.VERSION.SDK_INT >= 21) {
Window window = getActivity().getWindow();
statusBarColor = window.getStatusBarColor();
WindowUtil.setStatusBarColor(window, getResources().getColor(R.color.action_mode_status_bar));
}
if (!ThemeUtil.isDarkTheme(getContext())) {
WindowUtil.setLightStatusBar(getActivity().getWindow());
}
setCorrectActionModeMenuVisibility(menu);
listener.onMessageActionToolbarOpened();
return true;
@@ -1888,12 +1876,6 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
public void onDestroyActionMode(ActionMode mode) {
((ConversationAdapter)list.getAdapter()).clearSelection();
list.invalidateItemDecorations();
if (Build.VERSION.SDK_INT >= 21) {
WindowUtil.setStatusBarColor(requireActivity().getWindow(), statusBarColor);
}
WindowUtil.setLightStatusBarFromTheme(requireActivity());
actionMode = null;
}

View File

@@ -104,11 +104,6 @@ public class ConversationListArchiveFragment extends ConversationListFragment im
return R.plurals.ConversationListFragment_moved_conversations_to_inbox;
}
@Override
protected @MenuRes int getActionModeMenuRes() {
return R.menu.conversation_list_batch_unarchive;
}
@Override
protected @DrawableRes int getArchiveIconRes() {
return R.drawable.ic_unarchive_white_36dp;

View File

@@ -45,12 +45,10 @@ import android.widget.Toast;
import androidx.annotation.DrawableRes;
import androidx.annotation.IdRes;
import androidx.annotation.MenuRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.PluralsRes;
import androidx.annotation.WorkerThread;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.view.ActionMode;
import androidx.appcompat.widget.Toolbar;
@@ -80,7 +78,9 @@ import org.thoughtcrime.securesms.NewConversationActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.RatingManager;
import org.thoughtcrime.securesms.components.SearchToolbar;
import org.thoughtcrime.securesms.components.SignalContextMenu;
import org.thoughtcrime.securesms.components.menu.ActionItem;
import org.thoughtcrime.securesms.components.menu.SignalBottomActionBar;
import org.thoughtcrime.securesms.components.menu.SignalContextMenu;
import org.thoughtcrime.securesms.components.UnreadPaymentsView;
import org.thoughtcrime.securesms.components.recyclerview.DeleteItemAnimator;
import org.thoughtcrime.securesms.components.registration.PulsingFloatingActionButton;
@@ -148,6 +148,7 @@ import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.websocket.WebSocketConnectionState;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
@@ -200,6 +201,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode
private VoiceNoteMediaControllerOwner mediaControllerOwner;
private Stub<FrameLayout> voiceNotePlayerViewStub;
private VoiceNotePlayerView voiceNotePlayerView;
private SignalBottomActionBar bottomActionBar;
private Stopwatch startupStopwatch;
@@ -242,6 +244,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode
toolbarShadow = view.findViewById(R.id.conversation_list_toolbar_shadow);
proxyStatus = view.findViewById(R.id.conversation_list_proxy_status);
unreadPaymentsDot = view.findViewById(R.id.unread_payments_indicator);
bottomActionBar = view.findViewById(R.id.conversation_list_bottom_action_bar);
reminderView = new Stub<>(view.findViewById(R.id.reminder));
emptyState = new Stub<>(view.findViewById(R.id.empty_state));
searchToolbar = new Stub<>(view.findViewById(R.id.search_toolbar));
@@ -777,10 +780,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode
return null;
}, none -> {
if (actionMode != null) {
actionMode.finish();
actionMode = null;
}
endActionModeIfActive();
});
}
@@ -792,10 +792,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode
StorageSyncHelper.scheduleSyncForDataChange();
return null;
}, none -> {
if (actionMode != null) {
actionMode.finish();
actionMode = null;
}
endActionModeIfActive();
});
}
@@ -825,11 +822,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode
@Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
if (actionMode != null) {
actionMode.finish();
actionMode = null;
}
endActionModeIfActive();
}
@Override
@@ -881,10 +874,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode
@Override
protected void onPostExecute(Void result) {
dialog.dismiss();
if (actionMode != null) {
actionMode.finish();
actionMode = null;
}
endActionModeIfActive();
}
}.executeOnExecutor(SignalExecutors.BOUNDED);
}
@@ -906,9 +896,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode
Snackbar.LENGTH_LONG)
.setTextColor(Color.WHITE)
.show();
if (actionMode != null) {
actionMode.finish();
}
endActionModeIfActive();
return;
}
@@ -919,9 +907,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode
return null;
}, unused -> {
if (actionMode != null) {
actionMode.finish();
}
endActionModeIfActive();
});
}
@@ -933,21 +919,40 @@ public class ConversationListFragment extends MainFragment implements ActionMode
return null;
}, unused -> {
if (actionMode != null) {
actionMode.finish();
}
endActionModeIfActive();
});
}
private void handleSelectAllThreads() {
defaultAdapter.selectAllThreads();
actionMode.setTitle(String.valueOf(defaultAdapter.getBatchSelectionIds().size()));
updateMultiSelectState();
}
private void handleCreateConversation(long threadId, Recipient recipient, int distributionType) {
getNavigator().goToConversation(recipient.getId(), threadId, distributionType, -1);
}
private void startActionMode() {
actionMode = ((AppCompatActivity) getActivity()).startSupportActionMode(ConversationListFragment.this);
ViewUtil.fadeIn(bottomActionBar, 250);
ViewUtil.fadeOut(fab, 250);
ViewUtil.fadeOut(cameraFab, 250);
}
private void endActionModeIfActive() {
if (actionMode != null) {
endActionMode();
}
}
private void endActionMode() {
actionMode.finish();
actionMode = null;
ViewUtil.fadeOut(bottomActionBar, 250);
ViewUtil.fadeIn(fab, 250);
ViewUtil.fadeIn(cameraFab, 250);
}
private void onSubmitList(@NonNull List<Conversation> conversationList) {
defaultAdapter.submitList(conversationList);
onPostSubmitList(conversationList.size());
@@ -1016,10 +1021,9 @@ public class ConversationListFragment extends MainFragment implements ActionMode
defaultAdapter.toggleConversationInBatchSet(conversation);
if (defaultAdapter.getBatchSelectionIds().size() == 0) {
actionMode.finish();
endActionModeIfActive();
} else {
actionMode.setTitle(String.valueOf(defaultAdapter.getBatchSelectionIds().size()));
setCorrectMenuVisibility(actionMode.getMenu());
updateMultiSelectState();
}
}
}
@@ -1035,35 +1039,35 @@ public class ConversationListFragment extends MainFragment implements ActionMode
Collection<Long> id = Collections.singleton(conversation.getThreadRecord().getThreadId());
List<SignalContextMenu.Item> items = new ArrayList<>();
List<ActionItem> items = new ArrayList<>();
if (!conversation.getThreadRecord().isArchived()) {
if (conversation.getThreadRecord().isRead()) {
items.add(new SignalContextMenu.Item(R.drawable.ic_unread_24, R.string.ConversationListFragment_unread, () -> handleMarkAsUnread(id)));
items.add(new ActionItem(R.drawable.ic_unread_24, R.string.ConversationListFragment_unread, () -> handleMarkAsUnread(id)));
} else {
items.add(new SignalContextMenu.Item(R.drawable.ic_read_24, R.string.ConversationListFragment_read, () -> handleMarkAsRead(id)));
items.add(new ActionItem(R.drawable.ic_read_24, R.string.ConversationListFragment_read, () -> handleMarkAsRead(id)));
}
if (conversation.getThreadRecord().isPinned()) {
items.add(new SignalContextMenu.Item(R.drawable.ic_unpin_24, R.string.ConversationListFragment_unpin, () -> handleUnpin(id)));
items.add(new ActionItem(R.drawable.ic_unpin_24, R.string.ConversationListFragment_unpin, () -> handleUnpin(id)));
} else {
items.add(new SignalContextMenu.Item(R.drawable.ic_pin_24, R.string.ConversationListFragment_pin, () -> handlePin(Collections.singleton(conversation))));
items.add(new ActionItem(R.drawable.ic_pin_24, R.string.ConversationListFragment_pin, () -> handlePin(Collections.singleton(conversation))));
}
}
items.add(new SignalContextMenu.Item(R.drawable.ic_select_24, R.string.ConversationListFragment_select, () -> {
items.add(new ActionItem(R.drawable.ic_select_24, R.string.ConversationListFragment_select, () -> {
defaultAdapter.initializeBatchMode(true);
defaultAdapter.toggleConversationInBatchSet(conversation);
actionMode = ((AppCompatActivity) getActivity()).startSupportActionMode(ConversationListFragment.this);
startActionMode();
}));
if (conversation.getThreadRecord().isArchived()) {
items.add(new SignalContextMenu.Item(R.drawable.ic_unarchive_24, R.string.ConversationListFragment_unarchive, () -> handleArchive(id, false)));
items.add(new ActionItem(R.drawable.ic_unarchive_24, R.string.ConversationListFragment_unarchive, () -> handleArchive(id, false)));
} else {
items.add(new SignalContextMenu.Item(R.drawable.ic_archive_24, R.string.ConversationListFragment_archive, () -> handleArchive(id, false)));
items.add(new ActionItem(R.drawable.ic_archive_24, R.string.ConversationListFragment_archive, () -> handleArchive(id, false)));
}
items.add(new SignalContextMenu.Item(R.drawable.ic_delete_24, R.string.ConversationListFragment_delete, () -> handleDelete(id)));
items.add(new ActionItem(R.drawable.ic_delete_24, R.string.ConversationListFragment_delete, () -> handleDelete(id)));
new SignalContextMenu.Builder(view, list)
.offsetX(ViewUtil.dpToPx(12))
@@ -1076,45 +1080,26 @@ public class ConversationListFragment extends MainFragment implements ActionMode
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
MenuInflater inflater = getActivity().getMenuInflater();
inflater.inflate(R.menu.conversation_list_batch_pin, menu);
inflater.inflate(getActionModeMenuRes(), menu);
inflater.inflate(R.menu.conversation_list_batch, menu);
mode.setTitle("1");
WindowUtil.setStatusBarColor(requireActivity().getWindow(), getResources().getColor(R.color.action_mode_status_bar));
mode.setTitle(requireContext().getResources().getQuantityString(R.plurals.ConversationListFragment_s_selected, 1, 1));
return true;
}
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
setCorrectMenuVisibility(menu);
updateMultiSelectState();
return false;
}
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_select_all: handleSelectAllThreads(); return true;
case R.id.menu_delete_selected: handleDelete(defaultAdapter.getBatchSelectionIds()); return true;
case R.id.menu_pin_selected: handlePin(defaultAdapter.getBatchSelection()); return true;
case R.id.menu_unpin_selected: handleUnpin(defaultAdapter.getBatchSelectionIds()); return true;
case R.id.menu_archive_selected: handleArchive(defaultAdapter.getBatchSelectionIds(), true); return true;
case R.id.menu_mark_as_read: handleMarkAsRead(defaultAdapter.getBatchSelectionIds()); return true;
case R.id.menu_mark_as_unread: handleMarkAsUnread(defaultAdapter.getBatchSelectionIds()); return true;
}
return false;
return true;
}
@Override
public void onDestroyActionMode(ActionMode mode) {
defaultAdapter.initializeBatchMode(false);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (Build.VERSION.SDK_INT >= 21) {
TypedArray color = getActivity().getTheme().obtainStyledAttributes(new int[] {android.R.attr.statusBarColor});
WindowUtil.setStatusBarColor(getActivity().getWindow(), color.getColor(0, Color.BLACK));
color.recycle();
@@ -1131,7 +1116,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode
lightStatusBarAttr.recycle();
}
actionMode = null;
endActionModeIfActive();
}
@Subscribe(threadMode = ThreadMode.MAIN)
@@ -1145,29 +1130,41 @@ public class ConversationListFragment extends MainFragment implements ActionMode
closeSearchIfOpen();
}
private void setCorrectMenuVisibility(@NonNull Menu menu) {
private void updateMultiSelectState() {
int count = defaultAdapter.getBatchSelectionIds().size();
boolean hasUnread = Stream.of(defaultAdapter.getBatchSelection()).anyMatch(conversation -> !conversation.getThreadRecord().isRead());
boolean hasUnpinned = Stream.of(defaultAdapter.getBatchSelection()).anyMatch(conversation -> !conversation.getThreadRecord().isPinned());
boolean canPin = viewModel.getPinnedCount() < MAXIMUM_PINNED_CONVERSATIONS;
if (actionMode != null) {
actionMode.setTitle(requireContext().getResources().getQuantityString(R.plurals.ConversationListFragment_s_selected, count, count));
}
List<ActionItem> items = new ArrayList<>();
if (hasUnread) {
menu.findItem(R.id.menu_mark_as_unread).setVisible(false);
menu.findItem(R.id.menu_mark_as_read).setVisible(true);
items.add(new ActionItem(R.drawable.ic_read_24, R.string.ConversationListFragment_read, () -> handleMarkAsRead(defaultAdapter.getBatchSelectionIds())));
} else {
menu.findItem(R.id.menu_mark_as_unread).setVisible(true);
menu.findItem(R.id.menu_mark_as_read).setVisible(false);
items.add(new ActionItem(R.drawable.ic_unread_24, R.string.ConversationListFragment_unread, () -> handleMarkAsUnread(defaultAdapter.getBatchSelectionIds())));
}
if (!isArchived() && hasUnpinned && canPin) {
menu.findItem(R.id.menu_pin_selected).setVisible(true);
menu.findItem(R.id.menu_unpin_selected).setVisible(false);
items.add(new ActionItem(R.drawable.ic_pin_24, R.string.ConversationListFragment_pin, () -> handlePin(defaultAdapter.getBatchSelection())));
} else if (!isArchived() && !hasUnpinned) {
menu.findItem(R.id.menu_pin_selected).setVisible(false);
menu.findItem(R.id.menu_unpin_selected).setVisible(true);
} else {
menu.findItem(R.id.menu_pin_selected).setVisible(false);
menu.findItem(R.id.menu_unpin_selected).setVisible(false);
items.add(new ActionItem(R.drawable.ic_unpin_24, R.string.ConversationListFragment_unpin, () -> handleUnpin(defaultAdapter.getBatchSelectionIds())));
}
if (isArchived()) {
items.add(new ActionItem(R.drawable.ic_unarchive_24, R.string.ConversationListFragment_unarchive, () -> handleArchive(defaultAdapter.getBatchSelectionIds(), true)));
} else {
items.add(new ActionItem(R.drawable.ic_archive_24, R.string.ConversationListFragment_archive, () -> handleArchive(defaultAdapter.getBatchSelectionIds(), true)));
}
items.add(new ActionItem(R.drawable.ic_delete_24, R.string.ConversationListFragment_delete, () -> handleDelete(defaultAdapter.getBatchSelectionIds())));
items.add(new ActionItem(R.drawable.ic_select_24, R.string.ConversationListFragment_select_all, this::handleSelectAllThreads));
bottomActionBar.setItems(items);
}
protected Toolbar getToolbar(@NonNull View rootView) {
@@ -1178,10 +1175,6 @@ public class ConversationListFragment extends MainFragment implements ActionMode
return R.plurals.ConversationListFragment_conversations_archived;
}
protected @MenuRes int getActionModeMenuRes() {
return R.menu.conversation_list_batch_archive;
}
protected @DrawableRes int getArchiveIconRes() {
return R.drawable.ic_archive_white_36dp;
}