1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.messaging.ui.conversationlist; 17 18 import androidx.collection.ArrayMap; 19 import android.text.TextUtils; 20 import android.view.ActionMode; 21 import android.view.ActionMode.Callback; 22 import android.view.Menu; 23 import android.view.MenuItem; 24 25 import com.android.messaging.R; 26 import com.android.messaging.datamodel.data.ConversationListData; 27 import com.android.messaging.datamodel.data.ConversationListItemData; 28 import com.android.messaging.util.Assert; 29 30 import java.util.Collection; 31 import java.util.HashSet; 32 33 public class MultiSelectActionModeCallback implements Callback { 34 private HashSet<String> mBlockedSet; 35 36 public interface Listener { onActionBarDelete(Collection<SelectedConversation> conversations)37 void onActionBarDelete(Collection<SelectedConversation> conversations); onActionBarArchive(Iterable<SelectedConversation> conversations, boolean isToArchive)38 void onActionBarArchive(Iterable<SelectedConversation> conversations, 39 boolean isToArchive); onActionBarNotification(Iterable<SelectedConversation> conversations, boolean isNotificationOn)40 void onActionBarNotification(Iterable<SelectedConversation> conversations, 41 boolean isNotificationOn); onActionBarAddContact(final SelectedConversation conversation)42 void onActionBarAddContact(final SelectedConversation conversation); onActionBarBlock(final SelectedConversation conversation)43 void onActionBarBlock(final SelectedConversation conversation); onActionBarHome()44 void onActionBarHome(); 45 } 46 47 static class SelectedConversation { 48 public final String conversationId; 49 public final long timestamp; 50 public final String icon; 51 public final String otherParticipantNormalizedDestination; 52 public final CharSequence participantLookupKey; 53 public final boolean isGroup; 54 public final boolean isArchived; 55 public final boolean notificationEnabled; SelectedConversation(ConversationListItemData data)56 public SelectedConversation(ConversationListItemData data) { 57 conversationId = data.getConversationId(); 58 timestamp = data.getTimestamp(); 59 icon = data.getIcon(); 60 otherParticipantNormalizedDestination = data.getOtherParticipantNormalizedDestination(); 61 participantLookupKey = data.getParticipantLookupKey(); 62 isGroup = data.getIsGroup(); 63 isArchived = data.getIsArchived(); 64 notificationEnabled = data.getNotificationEnabled(); 65 } 66 } 67 68 private final ArrayMap<String, SelectedConversation> mSelectedConversations; 69 70 private Listener mListener; 71 private MenuItem mArchiveMenuItem; 72 private MenuItem mUnarchiveMenuItem; 73 private MenuItem mAddContactMenuItem; 74 private MenuItem mBlockMenuItem; 75 private MenuItem mNotificationOnMenuItem; 76 private MenuItem mNotificationOffMenuItem; 77 private boolean mHasInflated; 78 MultiSelectActionModeCallback(final Listener listener)79 public MultiSelectActionModeCallback(final Listener listener) { 80 mListener = listener; 81 mSelectedConversations = new ArrayMap<>(); 82 83 } 84 85 @Override onCreateActionMode(ActionMode actionMode, Menu menu)86 public boolean onCreateActionMode(ActionMode actionMode, Menu menu) { 87 actionMode.getMenuInflater().inflate(R.menu.conversation_list_fragment_select_menu, menu); 88 mArchiveMenuItem = menu.findItem(R.id.action_archive); 89 mUnarchiveMenuItem = menu.findItem(R.id.action_unarchive); 90 mAddContactMenuItem = menu.findItem(R.id.action_add_contact); 91 mBlockMenuItem = menu.findItem(R.id.action_block); 92 mNotificationOffMenuItem = menu.findItem(R.id.action_notification_off); 93 mNotificationOnMenuItem = menu.findItem(R.id.action_notification_on); 94 mHasInflated = true; 95 updateActionIconsVisiblity(); 96 return true; 97 } 98 99 @Override onPrepareActionMode(ActionMode actionMode, Menu menu)100 public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) { 101 return true; 102 } 103 104 @Override onActionItemClicked(ActionMode actionMode, MenuItem menuItem)105 public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) { 106 switch(menuItem.getItemId()) { 107 case R.id.action_delete: 108 mListener.onActionBarDelete(mSelectedConversations.values()); 109 return true; 110 case R.id.action_archive: 111 mListener.onActionBarArchive(mSelectedConversations.values(), true); 112 return true; 113 case R.id.action_unarchive: 114 mListener.onActionBarArchive(mSelectedConversations.values(), false); 115 return true; 116 case R.id.action_notification_off: 117 mListener.onActionBarNotification(mSelectedConversations.values(), false); 118 return true; 119 case R.id.action_notification_on: 120 mListener.onActionBarNotification(mSelectedConversations.values(), true); 121 return true; 122 case R.id.action_add_contact: 123 Assert.isTrue(mSelectedConversations.size() == 1); 124 mListener.onActionBarAddContact(mSelectedConversations.valueAt(0)); 125 return true; 126 case R.id.action_block: 127 Assert.isTrue(mSelectedConversations.size() == 1); 128 mListener.onActionBarBlock(mSelectedConversations.valueAt(0)); 129 return true; 130 case android.R.id.home: 131 mListener.onActionBarHome(); 132 return true; 133 default: 134 return false; 135 } 136 } 137 138 @Override onDestroyActionMode(ActionMode actionMode)139 public void onDestroyActionMode(ActionMode actionMode) { 140 mListener = null; 141 mSelectedConversations.clear(); 142 mHasInflated = false; 143 } 144 toggleSelect(final ConversationListData listData, final ConversationListItemData conversationListItemData)145 public void toggleSelect(final ConversationListData listData, 146 final ConversationListItemData conversationListItemData) { 147 Assert.notNull(conversationListItemData); 148 mBlockedSet = listData.getBlockedParticipants(); 149 final String id = conversationListItemData.getConversationId(); 150 if (mSelectedConversations.containsKey(id)) { 151 mSelectedConversations.remove(id); 152 } else { 153 mSelectedConversations.put(id, new SelectedConversation(conversationListItemData)); 154 } 155 156 if (mSelectedConversations.isEmpty()) { 157 mListener.onActionBarHome(); 158 } else { 159 updateActionIconsVisiblity(); 160 } 161 } 162 isSelected(final String selectedId)163 public boolean isSelected(final String selectedId) { 164 return mSelectedConversations.containsKey(selectedId); 165 } 166 updateActionIconsVisiblity()167 private void updateActionIconsVisiblity() { 168 if (!mHasInflated) { 169 return; 170 } 171 172 if (mSelectedConversations.size() == 1) { 173 final SelectedConversation conversation = mSelectedConversations.valueAt(0); 174 // The look up key is a key given to us by contacts app, so if we have a look up key, 175 // we know that the participant is already in contacts. 176 final boolean isInContacts = !TextUtils.isEmpty(conversation.participantLookupKey); 177 mAddContactMenuItem.setVisible(!conversation.isGroup && !isInContacts); 178 // ParticipantNormalizedDestination is always null for group conversations. 179 final String otherParticipant = conversation.otherParticipantNormalizedDestination; 180 mBlockMenuItem.setVisible(otherParticipant != null 181 && !mBlockedSet.contains(otherParticipant)); 182 } else { 183 mBlockMenuItem.setVisible(false); 184 mAddContactMenuItem.setVisible(false); 185 } 186 187 boolean hasCurrentlyArchived = false; 188 boolean hasCurrentlyUnarchived = false; 189 boolean hasCurrentlyOnNotification = false; 190 boolean hasCurrentlyOffNotification = false; 191 final Iterable<SelectedConversation> conversations = mSelectedConversations.values(); 192 for (final SelectedConversation conversation : conversations) { 193 if (conversation.notificationEnabled) { 194 hasCurrentlyOnNotification = true; 195 } else { 196 hasCurrentlyOffNotification = true; 197 } 198 199 if (conversation.isArchived) { 200 hasCurrentlyArchived = true; 201 } else { 202 hasCurrentlyUnarchived = true; 203 } 204 205 // If we found at least one of each example we don't need to keep looping. 206 if (hasCurrentlyOffNotification && hasCurrentlyOnNotification && 207 hasCurrentlyArchived && hasCurrentlyUnarchived) { 208 break; 209 } 210 } 211 // If we have notification off conversations we show on button, if we have notification on 212 // conversation we show off button. We can show both if we have a mixture. 213 mNotificationOffMenuItem.setVisible(hasCurrentlyOnNotification); 214 mNotificationOnMenuItem.setVisible(hasCurrentlyOffNotification); 215 216 mArchiveMenuItem.setVisible(hasCurrentlyUnarchived); 217 mUnarchiveMenuItem.setVisible(hasCurrentlyArchived); 218 } 219 } 220