mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-27 12:05:22 +00:00
Support for multi-select in the conversation list.
// FREEBIE Closes #1601 Closes #2214 Fixes #2188 Fixes #786
This commit is contained in:
parent
ebf6a2d833
commit
ed556fbd3a
@ -118,7 +118,7 @@
|
|||||||
<string name="ConversationFragment_transport_s_sent_received_s">Transport: %1$s\nSent/Received: %2$s</string>
|
<string name="ConversationFragment_transport_s_sent_received_s">Transport: %1$s\nSent/Received: %2$s</string>
|
||||||
<string name="ConversationFragment_sender_s_transport_s_sent_s_received_s">Sender: %1$s\nTransport: %2$s\nSent: %3$s\nReceived: %4$s</string>
|
<string name="ConversationFragment_sender_s_transport_s_sent_s_received_s">Sender: %1$s\nTransport: %2$s\nSent: %3$s\nReceived: %4$s</string>
|
||||||
<string name="ConversationFragment_confirm_message_delete">Confirm message delete</string>
|
<string name="ConversationFragment_confirm_message_delete">Confirm message delete</string>
|
||||||
<string name="ConversationFragment_are_you_sure_you_want_to_permanently_delete_this_message">Are you sure that you want to permanently delete this message?</string>
|
<string name="ConversationFragment_are_you_sure_you_want_to_permanently_delete_all_selected_messages">Are you sure that you want to permanently delete all selected messages?</string>
|
||||||
<string name="ConversationFragment_save_to_sd_card">Save to storage?</string>
|
<string name="ConversationFragment_save_to_sd_card">Save to storage?</string>
|
||||||
<string name="ConversationFragment_this_media_has_been_stored_in_an_encrypted_database_warning">Saving this media to storage will allow any other apps on your phone to access it.\n\nContinue?</string>
|
<string name="ConversationFragment_this_media_has_been_stored_in_an_encrypted_database_warning">Saving this media to storage will allow any other apps on your phone to access it.\n\nContinue?</string>
|
||||||
<string name="ConversationFragment_error_while_saving_attachment_to_sd_card">Error while saving attachment to storage!</string>
|
<string name="ConversationFragment_error_while_saving_attachment_to_sd_card">Error while saving attachment to storage!</string>
|
||||||
@ -130,6 +130,8 @@
|
|||||||
<string name="ConversationFragment_push">PUSH</string>
|
<string name="ConversationFragment_push">PUSH</string>
|
||||||
<string name="ConversationFragment_mms">MMS</string>
|
<string name="ConversationFragment_mms">MMS</string>
|
||||||
<string name="ConversationFragment_sms">SMS</string>
|
<string name="ConversationFragment_sms">SMS</string>
|
||||||
|
<string name="ConversationFragment_deleting">Deleting...</string>
|
||||||
|
<string name="ConversationFragment_deleting_messages">Deleting messages...</string>
|
||||||
|
|
||||||
<!-- ConversationListFragment -->
|
<!-- ConversationListFragment -->
|
||||||
<string name="ConversationListFragment_delete_threads_question">Delete threads?</string>
|
<string name="ConversationListFragment_delete_threads_question">Delete threads?</string>
|
||||||
|
@ -35,7 +35,11 @@ import org.thoughtcrime.securesms.util.LRUCache;
|
|||||||
|
|
||||||
import java.lang.ref.SoftReference;
|
import java.lang.ref.SoftReference;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.ConversationFragment.SelectionClickListener;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A cursor adapter for a conversation thread. Ultimately
|
* A cursor adapter for a conversation thread. Ultimately
|
||||||
@ -55,19 +59,23 @@ public class ConversationAdapter extends CursorAdapter implements AbsListView.Re
|
|||||||
public static final int MESSAGE_TYPE_INCOMING = 1;
|
public static final int MESSAGE_TYPE_INCOMING = 1;
|
||||||
public static final int MESSAGE_TYPE_GROUP_ACTION = 2;
|
public static final int MESSAGE_TYPE_GROUP_ACTION = 2;
|
||||||
|
|
||||||
private final Handler failedIconClickHandler;
|
private final Set<MessageRecord> batchSelected = Collections.synchronizedSet(new HashSet<MessageRecord>());
|
||||||
private final Context context;
|
|
||||||
private final MasterSecret masterSecret;
|
|
||||||
private final boolean groupThread;
|
|
||||||
private final boolean pushDestination;
|
|
||||||
private final LayoutInflater inflater;
|
|
||||||
|
|
||||||
public ConversationAdapter(Context context, MasterSecret masterSecret,
|
private final SelectionClickListener selectionClickListener;
|
||||||
|
private final Handler failedIconClickHandler;
|
||||||
|
private final Context context;
|
||||||
|
private final MasterSecret masterSecret;
|
||||||
|
private final boolean groupThread;
|
||||||
|
private final boolean pushDestination;
|
||||||
|
private final LayoutInflater inflater;
|
||||||
|
|
||||||
|
public ConversationAdapter(Context context, MasterSecret masterSecret, SelectionClickListener selectionClickListener,
|
||||||
Handler failedIconClickHandler, boolean groupThread, boolean pushDestination)
|
Handler failedIconClickHandler, boolean groupThread, boolean pushDestination)
|
||||||
{
|
{
|
||||||
super(context, null, 0);
|
super(context, null, 0);
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.masterSecret = masterSecret;
|
this.masterSecret = masterSecret;
|
||||||
|
this.selectionClickListener = selectionClickListener;
|
||||||
this.failedIconClickHandler = failedIconClickHandler;
|
this.failedIconClickHandler = failedIconClickHandler;
|
||||||
this.groupThread = groupThread;
|
this.groupThread = groupThread;
|
||||||
this.pushDestination = pushDestination;
|
this.pushDestination = pushDestination;
|
||||||
@ -81,7 +89,8 @@ public class ConversationAdapter extends CursorAdapter implements AbsListView.Re
|
|||||||
String type = cursor.getString(cursor.getColumnIndexOrThrow(MmsSmsDatabase.TRANSPORT));
|
String type = cursor.getString(cursor.getColumnIndexOrThrow(MmsSmsDatabase.TRANSPORT));
|
||||||
MessageRecord messageRecord = getMessageRecord(id, cursor, type);
|
MessageRecord messageRecord = getMessageRecord(id, cursor, type);
|
||||||
|
|
||||||
item.set(masterSecret, messageRecord, failedIconClickHandler, groupThread, pushDestination);
|
item.set(masterSecret, messageRecord, batchSelected, selectionClickListener,
|
||||||
|
failedIconClickHandler, groupThread, pushDestination);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -158,6 +167,18 @@ public class ConversationAdapter extends CursorAdapter implements AbsListView.Re
|
|||||||
this.getCursor().close();
|
this.getCursor().close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void toggleBatchSelected(MessageRecord messageRecord) {
|
||||||
|
if (batchSelected.contains(messageRecord)) {
|
||||||
|
batchSelected.remove(messageRecord);
|
||||||
|
} else {
|
||||||
|
batchSelected.add(messageRecord);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<MessageRecord> getBatchSelected() {
|
||||||
|
return batchSelected;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onMovedToScrapHeap(View view) {
|
public void onMovedToScrapHeap(View view) {
|
||||||
((ConversationItem)view).unbind();
|
((ConversationItem)view).unbind();
|
||||||
|
@ -40,17 +40,23 @@ import org.thoughtcrime.securesms.sms.MessageSender;
|
|||||||
import org.thoughtcrime.securesms.util.Dialogs;
|
import org.thoughtcrime.securesms.util.Dialogs;
|
||||||
import org.thoughtcrime.securesms.util.DirectoryHelper;
|
import org.thoughtcrime.securesms.util.DirectoryHelper;
|
||||||
import org.thoughtcrime.securesms.util.FutureTaskListener;
|
import org.thoughtcrime.securesms.util.FutureTaskListener;
|
||||||
|
import org.thoughtcrime.securesms.util.ProgressDialogAsyncTask;
|
||||||
import org.thoughtcrime.securesms.util.SaveAttachmentTask;
|
import org.thoughtcrime.securesms.util.SaveAttachmentTask;
|
||||||
import org.thoughtcrime.securesms.util.SaveAttachmentTask.Attachment;
|
import org.thoughtcrime.securesms.util.SaveAttachmentTask.Attachment;
|
||||||
|
|
||||||
import java.sql.Date;
|
import java.sql.Date;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class ConversationFragment extends ListFragment
|
public class ConversationFragment extends ListFragment
|
||||||
implements LoaderManager.LoaderCallbacks<Cursor>
|
implements LoaderManager.LoaderCallbacks<Cursor>
|
||||||
{
|
{
|
||||||
private static final String TAG = ConversationFragment.class.getSimpleName();
|
private static final String TAG = ConversationFragment.class.getSimpleName();
|
||||||
|
|
||||||
|
private final ActionModeCallback actionModeCallback = new ActionModeCallback();
|
||||||
|
private final SelectionClickListener selectionClickListener = new SelectionClickListener();
|
||||||
|
|
||||||
private ConversationFragmentListener listener;
|
private ConversationFragmentListener listener;
|
||||||
|
|
||||||
private MasterSecret masterSecret;
|
private MasterSecret masterSecret;
|
||||||
@ -96,7 +102,7 @@ public class ConversationFragment extends ListFragment
|
|||||||
|
|
||||||
private void initializeListAdapter() {
|
private void initializeListAdapter() {
|
||||||
if (this.recipients != null && this.threadId != -1) {
|
if (this.recipients != null && this.threadId != -1) {
|
||||||
this.setListAdapter(new ConversationAdapter(getActivity(), masterSecret,
|
this.setListAdapter(new ConversationAdapter(getActivity(), masterSecret, selectionClickListener,
|
||||||
new FailedIconClickHandler(),
|
new FailedIconClickHandler(),
|
||||||
(!this.recipients.isSingleRecipient()) || this.recipients.isGroupRecipient(),
|
(!this.recipients.isSingleRecipient()) || this.recipients.isGroupRecipient(),
|
||||||
DirectoryHelper.isPushDestination(getActivity(), this.recipients)));
|
DirectoryHelper.isPushDestination(getActivity(), this.recipients)));
|
||||||
@ -106,49 +112,50 @@ public class ConversationFragment extends ListFragment
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initializeContextualActionBar() {
|
private void initializeContextualActionBar() {
|
||||||
getListView().setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
|
getListView().setOnItemClickListener(selectionClickListener);
|
||||||
@Override
|
getListView().setOnItemLongClickListener(selectionClickListener);
|
||||||
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
|
|
||||||
if (actionMode != null) {
|
|
||||||
view.setSelected(true);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
actionMode = ((ActionBarActivity)getActivity()).startSupportActionMode(actionModeCallback);
|
|
||||||
view.setSelected(true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
getListView().setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
|
||||||
if (actionMode != null) {
|
|
||||||
view.setSelected(true);
|
|
||||||
setCorrectMenuVisibility(getMessageRecord(), actionMode.getMenu());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setCorrectMenuVisibility(MessageRecord messageRecord, Menu menu) {
|
private void setCorrectMenuVisibility(Menu menu) {
|
||||||
MenuItem resend = menu.findItem(R.id.menu_context_resend);
|
ConversationAdapter adapter = (ConversationAdapter) getListAdapter();
|
||||||
MenuItem saveAttachment = menu.findItem(R.id.menu_context_save_attachment);
|
List<MessageRecord> messageRecords = getSelectedMessageRecords();
|
||||||
|
|
||||||
if (messageRecord.isFailed()) resend.setVisible(true);
|
if (actionMode != null && messageRecords.size() == 0) {
|
||||||
else resend.setVisible(false);
|
adapter.getBatchSelected().clear();
|
||||||
|
adapter.notifyDataSetChanged();
|
||||||
|
actionMode.finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (messageRecord.isMms() && !messageRecord.isMmsNotification()) {
|
if (messageRecords.size() > 1) {
|
||||||
saveAttachment.setVisible(((MediaMmsMessageRecord)messageRecord).containsMediaSlide());
|
menu.findItem(R.id.menu_context_forward).setVisible(false);
|
||||||
|
menu.findItem(R.id.menu_context_copy).setVisible(false);
|
||||||
|
menu.findItem(R.id.menu_context_details).setVisible(false);
|
||||||
|
menu.findItem(R.id.menu_context_save_attachment).setVisible(false);
|
||||||
|
menu.findItem(R.id.menu_context_resend).setVisible(false);
|
||||||
} else {
|
} else {
|
||||||
saveAttachment.setVisible(false);
|
MessageRecord messageRecord = messageRecords.get(0);
|
||||||
|
|
||||||
|
menu.findItem(R.id.menu_context_resend).setVisible(messageRecord.isFailed());
|
||||||
|
menu.findItem(R.id.menu_context_save_attachment).setVisible(messageRecord.isMms() &&
|
||||||
|
!messageRecord.isMmsNotification() &&
|
||||||
|
((MediaMmsMessageRecord)messageRecord).containsMediaSlide());
|
||||||
|
|
||||||
|
menu.findItem(R.id.menu_context_forward).setVisible(true);
|
||||||
|
menu.findItem(R.id.menu_context_details).setVisible(true);
|
||||||
|
menu.findItem(R.id.menu_context_copy).setVisible(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private MessageRecord getMessageRecord() {
|
private MessageRecord getSelectedMessageRecord() {
|
||||||
Cursor cursor = ((CursorAdapter)getListAdapter()).getCursor();
|
List<MessageRecord> messageRecords = getSelectedMessageRecords();
|
||||||
ConversationItem conversationItem = (ConversationItem)(((ConversationAdapter)getListAdapter()).newView(getActivity(), cursor, null));
|
|
||||||
return conversationItem.getMessageRecord();
|
if (messageRecords.size() == 1) return messageRecords.get(0);
|
||||||
|
else throw new AssertionError();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<MessageRecord> getSelectedMessageRecords() {
|
||||||
|
return new LinkedList<>(((ConversationAdapter)getListAdapter()).getBatchSelected());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reload(Recipients recipients, long threadId) {
|
public void reload(Recipients recipients, long threadId) {
|
||||||
@ -177,23 +184,32 @@ public class ConversationFragment extends ListFragment
|
|||||||
clipboard.setText(body);
|
clipboard.setText(body);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleDeleteMessage(final MessageRecord message) {
|
private void handleDeleteMessages(final List<MessageRecord> messageRecords) {
|
||||||
final long messageId = message.getId();
|
|
||||||
|
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||||
builder.setTitle(R.string.ConversationFragment_confirm_message_delete);
|
builder.setTitle(R.string.ConversationFragment_confirm_message_delete);
|
||||||
builder.setIcon(Dialogs.resolveIcon(getActivity(), R.attr.dialog_alert_icon));
|
builder.setIcon(Dialogs.resolveIcon(getActivity(), R.attr.dialog_alert_icon));
|
||||||
builder.setCancelable(true);
|
builder.setCancelable(true);
|
||||||
builder.setMessage(R.string.ConversationFragment_are_you_sure_you_want_to_permanently_delete_this_message);
|
builder.setMessage(R.string.ConversationFragment_are_you_sure_you_want_to_permanently_delete_all_selected_messages);
|
||||||
|
|
||||||
builder.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
|
builder.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
if (message.isMms()) {
|
new ProgressDialogAsyncTask<MessageRecord, Void, Void>(getActivity(),
|
||||||
DatabaseFactory.getMmsDatabase(getActivity()).delete(messageId);
|
R.string.ConversationFragment_deleting,
|
||||||
} else {
|
R.string.ConversationFragment_deleting_messages)
|
||||||
DatabaseFactory.getSmsDatabase(getActivity()).deleteMessage(messageId);
|
{
|
||||||
}
|
@Override
|
||||||
|
protected Void doInBackground(MessageRecord... messageRecords) {
|
||||||
|
for (MessageRecord messageRecord : messageRecords) {
|
||||||
|
if (messageRecord.isMms()) {
|
||||||
|
DatabaseFactory.getMmsDatabase(getActivity()).delete(messageRecord.getId());
|
||||||
|
} else {
|
||||||
|
DatabaseFactory.getSmsDatabase(getActivity()).deleteMessage(messageRecord.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}.execute(messageRecords.toArray(new MessageRecord[messageRecords.size()]));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -312,16 +328,43 @@ public class ConversationFragment extends ListFragment
|
|||||||
public void setComposeText(String text);
|
public void setComposeText(String text);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ActionMode.Callback actionModeCallback = new ActionMode.Callback() {
|
public class SelectionClickListener
|
||||||
|
implements AdapterView.OnItemLongClickListener, AdapterView.OnItemClickListener
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||||
|
if (actionMode != null && view instanceof ConversationItem) {
|
||||||
|
MessageRecord messageRecord = ((ConversationItem)view).getMessageRecord();
|
||||||
|
((ConversationAdapter) getListAdapter()).toggleBatchSelected(messageRecord);
|
||||||
|
((ConversationAdapter) getListAdapter()).notifyDataSetChanged();
|
||||||
|
|
||||||
|
setCorrectMenuVisibility(actionMode.getMenu());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
|
||||||
|
if (actionMode == null && view instanceof ConversationItem) {
|
||||||
|
MessageRecord messageRecord = ((ConversationItem)view).getMessageRecord();
|
||||||
|
((ConversationAdapter) getListAdapter()).toggleBatchSelected(messageRecord);
|
||||||
|
((ConversationAdapter) getListAdapter()).notifyDataSetChanged();
|
||||||
|
|
||||||
|
actionMode = ((ActionBarActivity)getActivity()).startSupportActionMode(actionModeCallback);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ActionModeCallback implements ActionMode.Callback {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
|
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
|
||||||
MenuInflater inflater = mode.getMenuInflater();
|
MenuInflater inflater = mode.getMenuInflater();
|
||||||
inflater.inflate(R.menu.conversation_context, menu);
|
inflater.inflate(R.menu.conversation_context, menu);
|
||||||
|
|
||||||
MessageRecord messageRecord = getMessageRecord();
|
setCorrectMenuVisibility(menu);
|
||||||
setCorrectMenuVisibility(messageRecord, menu);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -332,41 +375,37 @@ public class ConversationFragment extends ListFragment
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDestroyActionMode(ActionMode mode) {
|
public void onDestroyActionMode(ActionMode mode) {
|
||||||
if (getListView() != null && getListView().getChildCount() > 0) {
|
((ConversationAdapter)getListAdapter()).getBatchSelected().clear();
|
||||||
for (int i = 0; i < getListView().getChildCount(); i++){
|
((ConversationAdapter)getListAdapter()).notifyDataSetChanged();
|
||||||
getListView().getChildAt(i).setSelected(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
actionMode = null;
|
actionMode = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
|
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
|
||||||
MessageRecord messageRecord = getMessageRecord();
|
|
||||||
|
|
||||||
switch(item.getItemId()) {
|
switch(item.getItemId()) {
|
||||||
case R.id.menu_context_copy:
|
case R.id.menu_context_copy:
|
||||||
handleCopyMessage(messageRecord);
|
handleCopyMessage(getSelectedMessageRecord());
|
||||||
actionMode.finish();
|
actionMode.finish();
|
||||||
return true;
|
return true;
|
||||||
case R.id.menu_context_delete_message:
|
case R.id.menu_context_delete_message:
|
||||||
handleDeleteMessage(messageRecord);
|
handleDeleteMessages(getSelectedMessageRecords());
|
||||||
actionMode.finish();
|
actionMode.finish();
|
||||||
return true;
|
return true;
|
||||||
case R.id.menu_context_details:
|
case R.id.menu_context_details:
|
||||||
handleDisplayDetails(messageRecord);
|
handleDisplayDetails(getSelectedMessageRecord());
|
||||||
actionMode.finish();
|
actionMode.finish();
|
||||||
return true;
|
return true;
|
||||||
case R.id.menu_context_forward:
|
case R.id.menu_context_forward:
|
||||||
handleForwardMessage(messageRecord);
|
handleForwardMessage(getSelectedMessageRecord());
|
||||||
actionMode.finish();
|
actionMode.finish();
|
||||||
return true;
|
return true;
|
||||||
case R.id.menu_context_resend:
|
case R.id.menu_context_resend:
|
||||||
handleResendMessage(messageRecord);
|
handleResendMessage(getSelectedMessageRecord());
|
||||||
actionMode.finish();
|
actionMode.finish();
|
||||||
return true;
|
return true;
|
||||||
case R.id.menu_context_save_attachment:
|
case R.id.menu_context_save_attachment:
|
||||||
handleSaveAttachment((MediaMmsMessageRecord)messageRecord);
|
handleSaveAttachment((MediaMmsMessageRecord)getSelectedMessageRecord());
|
||||||
actionMode.finish();
|
actionMode.finish();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -25,10 +25,8 @@ import android.content.res.TypedArray;
|
|||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.drawable.ColorDrawable;
|
import android.graphics.drawable.ColorDrawable;
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
import android.provider.Contacts.Intents;
|
|
||||||
import android.provider.ContactsContract;
|
import android.provider.ContactsContract;
|
||||||
import android.provider.ContactsContract.QuickContact;
|
import android.provider.ContactsContract.QuickContact;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
@ -40,9 +38,10 @@ import android.widget.LinearLayout;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.ConversationFragment.SelectionClickListener;
|
||||||
|
import org.thoughtcrime.securesms.contacts.ContactPhotoFactory;
|
||||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.contacts.ContactPhotoFactory;
|
|
||||||
import org.thoughtcrime.securesms.database.MmsDatabase;
|
import org.thoughtcrime.securesms.database.MmsDatabase;
|
||||||
import org.thoughtcrime.securesms.database.SmsDatabase;
|
import org.thoughtcrime.securesms.database.SmsDatabase;
|
||||||
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
|
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
|
||||||
@ -61,6 +60,8 @@ import org.thoughtcrime.securesms.util.Emoji;
|
|||||||
import org.thoughtcrime.securesms.util.FutureTaskListener;
|
import org.thoughtcrime.securesms.util.FutureTaskListener;
|
||||||
import org.thoughtcrime.securesms.util.ListenableFutureTask;
|
import org.thoughtcrime.securesms.util.ListenableFutureTask;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A view that displays an individual conversation item within a conversation
|
* A view that displays an individual conversation item within a conversation
|
||||||
* thread. Used by ComposeMessageActivity's ListActivity via a ConversationAdapter.
|
* thread. Used by ComposeMessageActivity's ListActivity via a ConversationAdapter.
|
||||||
@ -81,13 +82,13 @@ public class ConversationItem extends LinearLayout {
|
|||||||
R.attr.conversation_item_sent_push_pending_background,
|
R.attr.conversation_item_sent_push_pending_background,
|
||||||
R.attr.conversation_item_sent_push_pending_triangle_background};
|
R.attr.conversation_item_sent_push_pending_triangle_background};
|
||||||
|
|
||||||
private final static int SENT_PUSH = 0;
|
private final static int SENT_PUSH = 0;
|
||||||
private final static int SENT_PUSH_TRIANGLE = 1;
|
private final static int SENT_PUSH_TRIANGLE = 1;
|
||||||
private final static int SENT_SMS = 2;
|
private final static int SENT_SMS = 2;
|
||||||
private final static int SENT_SMS_TRIANGLE = 3;
|
private final static int SENT_SMS_TRIANGLE = 3;
|
||||||
private final static int SENT_SMS_PENDING = 4;
|
private final static int SENT_SMS_PENDING = 4;
|
||||||
private final static int SENT_SMS_PENDING_TRIANGLE = 5;
|
private final static int SENT_SMS_PENDING_TRIANGLE = 5;
|
||||||
private final static int SENT_PUSH_PENDING = 6;
|
private final static int SENT_PUSH_PENDING = 6;
|
||||||
private final static int SENT_PUSH_PENDING_TRIANGLE = 7;
|
private final static int SENT_PUSH_PENDING_TRIANGLE = 7;
|
||||||
|
|
||||||
private Handler failedIconHandler;
|
private Handler failedIconHandler;
|
||||||
@ -108,19 +109,21 @@ public class ConversationItem extends LinearLayout {
|
|||||||
private View triangleTick;
|
private View triangleTick;
|
||||||
private ImageView pendingIndicator;
|
private ImageView pendingIndicator;
|
||||||
|
|
||||||
private View mmsContainer;
|
private Set<MessageRecord> batchSelected;
|
||||||
private ImageView mmsThumbnail;
|
private SelectionClickListener selectionClickListener;
|
||||||
private Button mmsDownloadButton;
|
private View mmsContainer;
|
||||||
private TextView mmsDownloadingLabel;
|
private ImageView mmsThumbnail;
|
||||||
private ListenableFutureTask<SlideDeck> slideDeck;
|
private Button mmsDownloadButton;
|
||||||
private FutureTaskListener<SlideDeck> slideDeckListener;
|
private TextView mmsDownloadingLabel;
|
||||||
private TypedArray backgroundDrawables;
|
private ListenableFutureTask<SlideDeck> slideDeck;
|
||||||
|
private FutureTaskListener<SlideDeck> slideDeckListener;
|
||||||
|
private TypedArray backgroundDrawables;
|
||||||
|
|
||||||
private final FailedIconClickListener failedIconClickListener = new FailedIconClickListener();
|
private final FailedIconClickListener failedIconClickListener = new FailedIconClickListener();
|
||||||
private final MmsDownloadClickListener mmsDownloadClickListener = new MmsDownloadClickListener();
|
private final MmsDownloadClickListener mmsDownloadClickListener = new MmsDownloadClickListener();
|
||||||
private final MmsPreferencesClickListener mmsPreferencesClickListener = new MmsPreferencesClickListener();
|
private final MmsPreferencesClickListener mmsPreferencesClickListener = new MmsPreferencesClickListener();
|
||||||
private final ClickListener clickListener = new ClickListener();
|
private final ClickListener clickListener = new ClickListener();
|
||||||
private final Handler handler = new Handler();
|
private final Handler handler = new Handler();
|
||||||
private final Context context;
|
private final Context context;
|
||||||
|
|
||||||
public ConversationItem(Context context) {
|
public ConversationItem(Context context) {
|
||||||
@ -157,19 +160,23 @@ public class ConversationItem extends LinearLayout {
|
|||||||
setOnClickListener(clickListener);
|
setOnClickListener(clickListener);
|
||||||
if (failedImage != null) failedImage.setOnClickListener(failedIconClickListener);
|
if (failedImage != null) failedImage.setOnClickListener(failedIconClickListener);
|
||||||
if (mmsDownloadButton != null) mmsDownloadButton.setOnClickListener(mmsDownloadClickListener);
|
if (mmsDownloadButton != null) mmsDownloadButton.setOnClickListener(mmsDownloadClickListener);
|
||||||
|
if (mmsThumbnail != null) mmsThumbnail.setOnLongClickListener(new MultiSelectLongClickListener());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void set(MasterSecret masterSecret, MessageRecord messageRecord,
|
public void set(MasterSecret masterSecret, MessageRecord messageRecord,
|
||||||
|
Set<MessageRecord> batchSelected, SelectionClickListener selectionClickListener,
|
||||||
Handler failedIconHandler, boolean groupThread, boolean pushDestination)
|
Handler failedIconHandler, boolean groupThread, boolean pushDestination)
|
||||||
{
|
{
|
||||||
|
this.masterSecret = masterSecret;
|
||||||
|
this.messageRecord = messageRecord;
|
||||||
|
this.batchSelected = batchSelected;
|
||||||
|
this.selectionClickListener = selectionClickListener;
|
||||||
|
this.failedIconHandler = failedIconHandler;
|
||||||
|
this.groupThread = groupThread;
|
||||||
|
this.pushDestination = pushDestination;
|
||||||
|
|
||||||
this.messageRecord = messageRecord;
|
setConversationBackgroundDrawables(messageRecord);
|
||||||
this.masterSecret = masterSecret;
|
setSelectionBackgroundDrawables(messageRecord);
|
||||||
this.failedIconHandler = failedIconHandler;
|
|
||||||
this.groupThread = groupThread;
|
|
||||||
this.pushDestination = pushDestination;
|
|
||||||
|
|
||||||
setBackgroundDrawables(messageRecord);
|
|
||||||
setBodyText(messageRecord);
|
setBodyText(messageRecord);
|
||||||
|
|
||||||
if (!messageRecord.isGroupAction()) {
|
if (!messageRecord.isGroupAction()) {
|
||||||
@ -211,33 +218,57 @@ public class ConversationItem extends LinearLayout {
|
|||||||
|
|
||||||
/// MessageRecord Attribute Parsers
|
/// MessageRecord Attribute Parsers
|
||||||
|
|
||||||
private void setBackgroundDrawables(MessageRecord messageRecord) {
|
private void setConversationBackgroundDrawables(MessageRecord messageRecord) {
|
||||||
if (conversationParent != null && backgroundDrawables != null) {
|
if (conversationParent != null && backgroundDrawables != null) {
|
||||||
if (messageRecord.isOutgoing()) {
|
if (messageRecord.isOutgoing()) {
|
||||||
final int background;
|
final int background;
|
||||||
final int triangleBackground;
|
final int triangleBackground;
|
||||||
if (messageRecord.isPending() && pushDestination && !messageRecord.isForcedSms()) {
|
if (messageRecord.isPending() && pushDestination && !messageRecord.isForcedSms()) {
|
||||||
background = SENT_PUSH_PENDING;
|
background = SENT_PUSH_PENDING;
|
||||||
triangleBackground = SENT_PUSH_PENDING_TRIANGLE;
|
triangleBackground = SENT_PUSH_PENDING_TRIANGLE;
|
||||||
} else if (messageRecord.isPending() || messageRecord.isPendingSmsFallback()) {
|
} else if (messageRecord.isPending() || messageRecord.isPendingSmsFallback()) {
|
||||||
background = SENT_SMS_PENDING;
|
background = SENT_SMS_PENDING;
|
||||||
triangleBackground = SENT_SMS_PENDING_TRIANGLE;
|
triangleBackground = SENT_SMS_PENDING_TRIANGLE;
|
||||||
} else if (messageRecord.isPush()) {
|
} else if (messageRecord.isPush()) {
|
||||||
background = SENT_PUSH;
|
background = SENT_PUSH;
|
||||||
triangleBackground = SENT_PUSH_TRIANGLE;
|
triangleBackground = SENT_PUSH_TRIANGLE;
|
||||||
} else {
|
} else {
|
||||||
background = SENT_SMS;
|
background = SENT_SMS;
|
||||||
triangleBackground = SENT_SMS_TRIANGLE;
|
triangleBackground = SENT_SMS_TRIANGLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
setViewBackgroundWithoutResettingPadding(conversationParent, backgroundDrawables.getResourceId(background, -1));
|
setViewBackgroundWithoutResettingPadding(conversationParent, backgroundDrawables.getResourceId(background, -1));
|
||||||
setViewBackgroundWithoutResettingPadding(triangleTick, backgroundDrawables.getResourceId(triangleBackground, -1));
|
setViewBackgroundWithoutResettingPadding(triangleTick, backgroundDrawables.getResourceId(triangleBackground, -1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setSelectionBackgroundDrawables(MessageRecord messageRecord) {
|
||||||
|
int[] attributes = new int[]{R.attr.conversation_list_item_background_selected,
|
||||||
|
R.attr.conversation_item_background};
|
||||||
|
|
||||||
|
TypedArray drawables = context.obtainStyledAttributes(attributes);
|
||||||
|
|
||||||
|
if (batchSelected.contains(messageRecord)) {
|
||||||
|
setBackgroundDrawable(drawables.getDrawable(0));
|
||||||
|
} else {
|
||||||
|
setBackgroundDrawable(drawables.getDrawable(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
drawables.recycle();
|
||||||
|
}
|
||||||
|
|
||||||
private void setBodyText(MessageRecord messageRecord) {
|
private void setBodyText(MessageRecord messageRecord) {
|
||||||
bodyText.setText(Emoji.getInstance(context).emojify(messageRecord.getDisplayBody(), new Emoji.InvalidatingPageLoadedListener(bodyText)),
|
bodyText.setClickable(false);
|
||||||
TextView.BufferType.SPANNABLE);
|
bodyText.setFocusable(false);
|
||||||
|
bodyText.setText(Emoji.getInstance(context).emojify(messageRecord.getDisplayBody(),
|
||||||
|
new Emoji.InvalidatingPageLoadedListener(bodyText)),
|
||||||
|
TextView.BufferType.SPANNABLE);
|
||||||
|
|
||||||
|
if (bodyText.isClickable() && bodyText.isFocusable()) {
|
||||||
|
bodyText.setOnLongClickListener(new MultiSelectLongClickListener());
|
||||||
|
bodyText.setOnClickListener(new MultiSelectLongClickListener());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setContactPhoto(MessageRecord messageRecord) {
|
private void setContactPhoto(MessageRecord messageRecord) {
|
||||||
@ -365,12 +396,6 @@ public class ConversationItem extends LinearLayout {
|
|||||||
if (slide.hasImage()) {
|
if (slide.hasImage()) {
|
||||||
slide.setThumbnailOn(mmsThumbnail);
|
slide.setThumbnailOn(mmsThumbnail);
|
||||||
mmsThumbnail.setOnClickListener(new ThumbnailClickListener(slide));
|
mmsThumbnail.setOnClickListener(new ThumbnailClickListener(slide));
|
||||||
mmsThumbnail.setOnLongClickListener(new OnLongClickListener() {
|
|
||||||
@Override
|
|
||||||
public boolean onLongClick(View v) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
mmsThumbnail.setVisibility(View.VISIBLE);
|
mmsThumbnail.setVisibility(View.VISIBLE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -474,7 +499,9 @@ public class ConversationItem extends LinearLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
if (MediaPreviewActivity.isContentTypeSupported(slide.getContentType())) {
|
if (!batchSelected.isEmpty()) {
|
||||||
|
selectionClickListener.onItemClick(null, ConversationItem.this, -1, -1);
|
||||||
|
} else if (MediaPreviewActivity.isContentTypeSupported(slide.getContentType())) {
|
||||||
Intent intent = new Intent(context, MediaPreviewActivity.class);
|
Intent intent = new Intent(context, MediaPreviewActivity.class);
|
||||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||||
intent.setDataAndType(slide.getUri(), slide.getContentType());
|
intent.setDataAndType(slide.getUri(), slide.getContentType());
|
||||||
@ -545,6 +572,19 @@ public class ConversationItem extends LinearLayout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class MultiSelectLongClickListener implements OnLongClickListener, OnClickListener {
|
||||||
|
@Override
|
||||||
|
public boolean onLongClick(View view) {
|
||||||
|
selectionClickListener.onItemLongClick(null, ConversationItem.this, -1, -1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
selectionClickListener.onItemClick(null, ConversationItem.this, -1, -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void handleMessageApproval() {
|
private void handleMessageApproval() {
|
||||||
final int title;
|
final int title;
|
||||||
final int message;
|
final int message;
|
||||||
|
@ -182,4 +182,15 @@ public abstract class MessageRecord extends DisplayRecord {
|
|||||||
|
|
||||||
return spannable;
|
return spannable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
return other != null &&
|
||||||
|
other instanceof MessageRecord &&
|
||||||
|
((MessageRecord) other).getId() == getId() &&
|
||||||
|
((MessageRecord) other).isMms() == isMms();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int hashCode() {
|
||||||
|
return (int)getId();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user