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 android.app.Activity; 19 import android.app.AlertDialog; 20 import android.app.Fragment; 21 import android.content.Context; 22 import android.content.DialogInterface; 23 import android.content.Intent; 24 import android.content.res.Resources; 25 import android.net.Uri; 26 import android.os.Bundle; 27 import androidx.annotation.NonNull; 28 import android.view.View; 29 30 import com.android.messaging.R; 31 import com.android.messaging.datamodel.action.DeleteConversationAction; 32 import com.android.messaging.datamodel.action.UpdateConversationArchiveStatusAction; 33 import com.android.messaging.datamodel.action.UpdateConversationOptionsAction; 34 import com.android.messaging.datamodel.action.UpdateDestinationBlockedAction; 35 import com.android.messaging.datamodel.data.ConversationListData; 36 import com.android.messaging.datamodel.data.ConversationListItemData; 37 import com.android.messaging.ui.BugleActionBarActivity; 38 import com.android.messaging.ui.SnackBar; 39 import com.android.messaging.ui.SnackBarInteraction; 40 import com.android.messaging.ui.UIIntents; 41 import com.android.messaging.ui.contact.AddContactsConfirmationDialog; 42 import com.android.messaging.ui.conversationlist.ConversationListFragment.ConversationListFragmentHost; 43 import com.android.messaging.ui.conversationlist.MultiSelectActionModeCallback.SelectedConversation; 44 import com.android.messaging.util.BugleGservices; 45 import com.android.messaging.util.BugleGservicesKeys; 46 import com.android.messaging.util.DebugUtils; 47 import com.android.messaging.util.PhoneUtils; 48 import com.android.messaging.util.Trace; 49 import com.android.messaging.util.UiUtils; 50 51 import java.util.ArrayList; 52 import java.util.Collection; 53 import java.util.List; 54 55 import javax.annotation.Nullable; 56 57 /** 58 * Base class for many Conversation List activities. This will handle the common actions of multi 59 * select and common launching of intents. 60 */ 61 public abstract class AbstractConversationListActivity extends BugleActionBarActivity 62 implements ConversationListFragmentHost, MultiSelectActionModeCallback.Listener { 63 64 private static final int REQUEST_SET_DEFAULT_SMS_APP = 1; 65 66 protected ConversationListFragment mConversationListFragment; 67 68 @Override onAttachFragment(final Fragment fragment)69 public void onAttachFragment(final Fragment fragment) { 70 Trace.beginSection("AbstractConversationListActivity.onAttachFragment"); 71 // Fragment could be debug dialog 72 if (fragment instanceof ConversationListFragment) { 73 mConversationListFragment = (ConversationListFragment) fragment; 74 mConversationListFragment.setHost(this); 75 } 76 Trace.endSection(); 77 } 78 79 @Override onBackPressed()80 public void onBackPressed() { 81 // If action mode is active dismiss it 82 if (getActionMode() != null) { 83 dismissActionMode(); 84 return; 85 } 86 super.onBackPressed(); 87 } 88 startMultiSelectActionMode()89 protected void startMultiSelectActionMode() { 90 startActionMode(new MultiSelectActionModeCallback(this)); 91 } 92 exitMultiSelectState()93 protected void exitMultiSelectState() { 94 mConversationListFragment.showFab(); 95 dismissActionMode(); 96 mConversationListFragment.updateUi(); 97 } 98 isInConversationListSelectMode()99 protected boolean isInConversationListSelectMode() { 100 return getActionModeCallback() instanceof MultiSelectActionModeCallback; 101 } 102 103 @Override isSelectionMode()104 public boolean isSelectionMode() { 105 return isInConversationListSelectMode(); 106 } 107 108 @SuppressWarnings("MissingSuperCall") // TODO: fix me 109 @Override onActivityResult(final int requestCode, final int resultCode, final Intent data)110 public void onActivityResult(final int requestCode, final int resultCode, final Intent data) { 111 } 112 113 @Override onActionBarDelete(final Collection<SelectedConversation> conversations)114 public void onActionBarDelete(final Collection<SelectedConversation> conversations) { 115 if (!PhoneUtils.getDefault().isDefaultSmsApp()) { 116 // TODO: figure out a good way to combine this with the implementation in 117 // ConversationFragment doing similar things 118 final Activity activity = this; 119 UiUtils.showSnackBarWithCustomAction(this, 120 getWindow().getDecorView().getRootView(), 121 getString(R.string.requires_default_sms_app), 122 SnackBar.Action.createCustomAction(new Runnable() { 123 @Override 124 public void run() { 125 final Intent intent = 126 UIIntents.get().getChangeDefaultSmsAppIntent(activity); 127 startActivityForResult(intent, REQUEST_SET_DEFAULT_SMS_APP); 128 } 129 }, 130 getString(R.string.requires_default_sms_change_button)), 131 null /* interactions */, 132 null /* placement */); 133 return; 134 } 135 136 new AlertDialog.Builder(this) 137 .setTitle(getResources().getQuantityString( 138 R.plurals.delete_conversations_confirmation_dialog_title, 139 conversations.size())) 140 .setPositiveButton(R.string.delete_conversation_confirmation_button, 141 new DialogInterface.OnClickListener() { 142 @Override 143 public void onClick(final DialogInterface dialog, 144 final int button) { 145 for (final SelectedConversation conversation : conversations) { 146 DeleteConversationAction.deleteConversation( 147 conversation.conversationId, 148 conversation.timestamp); 149 } 150 exitMultiSelectState(); 151 } 152 }) 153 .setNegativeButton(R.string.delete_conversation_decline_button, null) 154 .show(); 155 } 156 157 @Override onActionBarArchive(final Iterable<SelectedConversation> conversations, final boolean isToArchive)158 public void onActionBarArchive(final Iterable<SelectedConversation> conversations, 159 final boolean isToArchive) { 160 final ArrayList<String> conversationIds = new ArrayList<String>(); 161 for (final SelectedConversation conversation : conversations) { 162 final String conversationId = conversation.conversationId; 163 conversationIds.add(conversationId); 164 if (isToArchive) { 165 UpdateConversationArchiveStatusAction.archiveConversation(conversationId); 166 } else { 167 UpdateConversationArchiveStatusAction.unarchiveConversation(conversationId); 168 } 169 } 170 171 final Runnable undoRunnable = new Runnable() { 172 @Override 173 public void run() { 174 for (final String conversationId : conversationIds) { 175 if (isToArchive) { 176 UpdateConversationArchiveStatusAction.unarchiveConversation(conversationId); 177 } else { 178 UpdateConversationArchiveStatusAction.archiveConversation(conversationId); 179 } 180 } 181 } 182 }; 183 184 final int textId = 185 isToArchive ? R.string.archived_toast_message : R.string.unarchived_toast_message; 186 final String message = getResources().getString(textId, conversationIds.size()); 187 UiUtils.showSnackBar(this, findViewById(android.R.id.list), message, undoRunnable, 188 SnackBar.Action.SNACK_BAR_UNDO, 189 mConversationListFragment.getSnackBarInteractions()); 190 exitMultiSelectState(); 191 } 192 193 @Override onActionBarNotification(final Iterable<SelectedConversation> conversations, final boolean isNotificationOn)194 public void onActionBarNotification(final Iterable<SelectedConversation> conversations, 195 final boolean isNotificationOn) { 196 for (final SelectedConversation conversation : conversations) { 197 UpdateConversationOptionsAction.enableConversationNotifications( 198 conversation.conversationId, isNotificationOn); 199 } 200 201 final int textId = isNotificationOn ? 202 R.string.notification_on_toast_message : R.string.notification_off_toast_message; 203 final String message = getResources().getString(textId, 1); 204 UiUtils.showSnackBar(this, findViewById(android.R.id.list), message, 205 null /* undoRunnable */, 206 SnackBar.Action.SNACK_BAR_UNDO, mConversationListFragment.getSnackBarInteractions()); 207 exitMultiSelectState(); 208 } 209 210 @Override onActionBarAddContact(final SelectedConversation conversation)211 public void onActionBarAddContact(final SelectedConversation conversation) { 212 final Uri avatarUri; 213 if (conversation.icon != null) { 214 avatarUri = Uri.parse(conversation.icon); 215 } else { 216 avatarUri = null; 217 } 218 final AddContactsConfirmationDialog dialog = new AddContactsConfirmationDialog( 219 this, avatarUri, conversation.otherParticipantNormalizedDestination); 220 dialog.show(); 221 exitMultiSelectState(); 222 } 223 224 @Override onActionBarBlock(final SelectedConversation conversation)225 public void onActionBarBlock(final SelectedConversation conversation) { 226 final Resources res = getResources(); 227 new AlertDialog.Builder(this) 228 .setTitle(res.getString(R.string.block_confirmation_title, 229 conversation.otherParticipantNormalizedDestination)) 230 .setMessage(res.getString(R.string.block_confirmation_message)) 231 .setNegativeButton(android.R.string.cancel, null) 232 .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { 233 @Override 234 public void onClick(final DialogInterface arg0, final int arg1) { 235 final Context context = AbstractConversationListActivity.this; 236 final View listView = findViewById(android.R.id.list); 237 final List<SnackBarInteraction> interactions = 238 mConversationListFragment.getSnackBarInteractions(); 239 final UpdateDestinationBlockedAction.UpdateDestinationBlockedActionListener 240 undoListener = 241 new UpdateDestinationBlockedActionSnackBar( 242 context, listView, null /* undoRunnable */, 243 interactions); 244 final Runnable undoRunnable = new Runnable() { 245 @Override 246 public void run() { 247 UpdateDestinationBlockedAction.updateDestinationBlocked( 248 conversation.otherParticipantNormalizedDestination, false, 249 conversation.conversationId, 250 undoListener); 251 } 252 }; 253 final UpdateDestinationBlockedAction.UpdateDestinationBlockedActionListener 254 listener = new UpdateDestinationBlockedActionSnackBar( 255 context, listView, undoRunnable, interactions); 256 UpdateDestinationBlockedAction.updateDestinationBlocked( 257 conversation.otherParticipantNormalizedDestination, true, 258 conversation.conversationId, 259 listener); 260 exitMultiSelectState(); 261 } 262 }) 263 .create() 264 .show(); 265 } 266 267 @Override onConversationClick(final ConversationListData listData, final ConversationListItemData conversationListItemData, final boolean isLongClick, final ConversationListItemView conversationView)268 public void onConversationClick(final ConversationListData listData, 269 final ConversationListItemData conversationListItemData, 270 final boolean isLongClick, 271 final ConversationListItemView conversationView) { 272 if (isLongClick && !isInConversationListSelectMode()) { 273 startMultiSelectActionMode(); 274 } 275 276 if (isInConversationListSelectMode()) { 277 final MultiSelectActionModeCallback multiSelectActionMode = 278 (MultiSelectActionModeCallback) getActionModeCallback(); 279 multiSelectActionMode.toggleSelect(listData, conversationListItemData); 280 mConversationListFragment.updateUi(); 281 } else { 282 final String conversationId = conversationListItemData.getConversationId(); 283 Bundle sceneTransitionAnimationOptions = null; 284 boolean hasCustomTransitions = false; 285 286 UIIntents.get().launchConversationActivity( 287 this, conversationId, null, 288 sceneTransitionAnimationOptions, 289 hasCustomTransitions); 290 } 291 } 292 293 @Override onCreateConversationClick()294 public void onCreateConversationClick() { 295 UIIntents.get().launchCreateNewConversationActivity(this, null); 296 } 297 298 299 @Override isConversationSelected(final String conversationId)300 public boolean isConversationSelected(final String conversationId) { 301 return isInConversationListSelectMode() && 302 ((MultiSelectActionModeCallback) getActionModeCallback()).isSelected( 303 conversationId); 304 } 305 onActionBarDebug()306 public void onActionBarDebug() { 307 DebugUtils.showDebugOptions(this); 308 } 309 310 private static class UpdateDestinationBlockedActionSnackBar 311 implements UpdateDestinationBlockedAction.UpdateDestinationBlockedActionListener { 312 private final Context mContext; 313 private final View mParentView; 314 private final Runnable mUndoRunnable; 315 private final List<SnackBarInteraction> mInteractions; 316 UpdateDestinationBlockedActionSnackBar(final Context context, @NonNull final View parentView, @Nullable final Runnable undoRunnable, @Nullable List<SnackBarInteraction> interactions)317 UpdateDestinationBlockedActionSnackBar(final Context context, 318 @NonNull final View parentView, @Nullable final Runnable undoRunnable, 319 @Nullable List<SnackBarInteraction> interactions) { 320 mContext = context; 321 mParentView = parentView; 322 mUndoRunnable = undoRunnable; 323 mInteractions = interactions; 324 } 325 326 @Override onUpdateDestinationBlockedAction( final UpdateDestinationBlockedAction action, final boolean success, final boolean block, final String destination)327 public void onUpdateDestinationBlockedAction( 328 final UpdateDestinationBlockedAction action, 329 final boolean success, final boolean block, 330 final String destination) { 331 if (success) { 332 final int messageId = block ? R.string.blocked_toast_message 333 : R.string.unblocked_toast_message; 334 final String message = mContext.getResources().getString(messageId, 1); 335 UiUtils.showSnackBar(mContext, mParentView, message, mUndoRunnable, 336 SnackBar.Action.SNACK_BAR_UNDO, mInteractions); 337 } 338 } 339 } 340 } 341