mirror of
https://github.com/oxen-io/session-android.git
synced 2025-10-26 10:42:20 +00:00
@@ -0,0 +1,15 @@
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.database.model.ThreadRecord;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
public interface BindableConversationListItem extends Unbindable {
|
||||
|
||||
public void bind(@NonNull MasterSecret masterSecret, @NonNull ThreadRecord thread,
|
||||
@NonNull Locale locale, @NonNull Set<Long> selectedThreads, boolean batchMode);
|
||||
}
|
||||
@@ -392,7 +392,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
switch (item.getItemId()) {
|
||||
case R.id.menu_call_secure:
|
||||
case R.id.menu_call_insecure: handleDial(getRecipients().getPrimaryRecipient()); return true;
|
||||
case R.id.menu_delete_thread: handleDeleteThread(); return true;
|
||||
case R.id.menu_add_attachment: handleAddAttachment(); return true;
|
||||
case R.id.menu_view_media: handleViewMedia(); return true;
|
||||
case R.id.menu_add_to_contacts: handleAddToContacts(); return true;
|
||||
@@ -650,28 +649,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
new GroupMembersDialog(this, getRecipients()).display();
|
||||
}
|
||||
|
||||
private void handleDeleteThread() {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle(R.string.ConversationActivity_delete_thread_question);
|
||||
builder.setIconAttribute(R.attr.dialog_alert_icon);
|
||||
builder.setCancelable(true);
|
||||
builder.setMessage(R.string.ConversationActivity_this_will_permanently_delete_all_messages_in_this_conversation);
|
||||
builder.setPositiveButton(R.string.delete, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
if (threadId > 0) {
|
||||
DatabaseFactory.getThreadDatabase(ConversationActivity.this).deleteConversation(threadId);
|
||||
}
|
||||
composeText.getText().clear();
|
||||
threadId = -1;
|
||||
finish();
|
||||
}
|
||||
});
|
||||
|
||||
builder.setNegativeButton(android.R.string.cancel, null);
|
||||
builder.show();
|
||||
}
|
||||
|
||||
private void handleAddToContacts() {
|
||||
final Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
|
||||
intent.putExtra(ContactsContract.Intents.Insert.PHONE, recipients.getPrimaryRecipient().getNumber());
|
||||
@@ -1089,9 +1066,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
draftDatabase.insertDrafts(new MasterCipher(thisMasterSecret), threadId, drafts);
|
||||
threadDatabase.updateSnippet(threadId, drafts.getSnippet(ConversationActivity.this),
|
||||
drafts.getUriSnippet(ConversationActivity.this),
|
||||
System.currentTimeMillis(), Types.BASE_DRAFT_TYPE);
|
||||
System.currentTimeMillis(), Types.BASE_DRAFT_TYPE, true);
|
||||
} else if (threadId > 0) {
|
||||
threadDatabase.update(threadId);
|
||||
threadDatabase.update(threadId, false);
|
||||
}
|
||||
|
||||
return threadId;
|
||||
|
||||
@@ -57,7 +57,7 @@ import org.thoughtcrime.securesms.mms.Slide;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientFactory;
|
||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||
import org.thoughtcrime.securesms.sms.MessageSender;
|
||||
import org.thoughtcrime.securesms.util.ProgressDialogAsyncTask;
|
||||
import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask;
|
||||
import org.thoughtcrime.securesms.util.SaveAttachmentTask;
|
||||
import org.thoughtcrime.securesms.util.SaveAttachmentTask.Attachment;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
|
||||
@@ -161,15 +161,6 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
|
||||
|
||||
@Override
|
||||
public void onCreateConversation(long threadId, Recipients recipients, int distributionType) {
|
||||
createConversation(threadId, recipients, distributionType);
|
||||
}
|
||||
|
||||
private void createGroup() {
|
||||
Intent intent = new Intent(this, GroupCreateActivity.class);
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
private void createConversation(long threadId, Recipients recipients, int distributionType) {
|
||||
Intent intent = new Intent(this, ConversationActivity.class);
|
||||
intent.putExtra(ConversationActivity.RECIPIENTS_EXTRA, recipients.getIds());
|
||||
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId);
|
||||
@@ -179,6 +170,17 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
|
||||
overridePendingTransition(R.anim.slide_from_right, R.anim.fade_scale_out);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSwitchToArchive() {
|
||||
Intent intent = new Intent(this, ConversationListArchiveActivity.class);
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
private void createGroup() {
|
||||
Intent intent = new Intent(this, GroupCreateActivity.class);
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
private void handleDisplaySettings() {
|
||||
Intent preferencesIntent = new Intent(this, ApplicationPreferencesActivity.class);
|
||||
startActivity(preferencesIntent);
|
||||
|
||||
@@ -49,6 +49,9 @@ import java.util.Set;
|
||||
*/
|
||||
public class ConversationListAdapter extends CursorRecyclerViewAdapter<ConversationListAdapter.ViewHolder> {
|
||||
|
||||
private static final int MESSAGE_TYPE_SWITCH_ARCHIVE = 1;
|
||||
private static final int MESSAGE_TYPE_THREAD = 2;
|
||||
|
||||
private final ThreadDatabase threadDatabase;
|
||||
private final MasterSecret masterSecret;
|
||||
private final MasterCipher masterCipher;
|
||||
@@ -61,37 +64,25 @@ public class ConversationListAdapter extends CursorRecyclerViewAdapter<Conversat
|
||||
private boolean batchMode = false;
|
||||
|
||||
protected static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
public ViewHolder(final @NonNull ConversationListItem itemView,
|
||||
final @Nullable ItemClickListener clickListener)
|
||||
public <V extends View & BindableConversationListItem> ViewHolder(final @NonNull V itemView)
|
||||
{
|
||||
super(itemView);
|
||||
itemView.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (clickListener != null) clickListener.onItemClick(itemView);
|
||||
}
|
||||
});
|
||||
itemView.setOnLongClickListener(new OnLongClickListener() {
|
||||
@Override
|
||||
public boolean onLongClick(View view) {
|
||||
if (clickListener != null) clickListener.onItemLongClick(itemView);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public ConversationListItem getItem() {
|
||||
return (ConversationListItem)itemView;
|
||||
public BindableConversationListItem getItem() {
|
||||
return (BindableConversationListItem)itemView;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(@NonNull Cursor cursor) {
|
||||
ThreadRecord record = getThreadRecord(cursor);
|
||||
StringBuilder builder = new StringBuilder(""+record.getThreadId());
|
||||
ThreadRecord record = getThreadRecord(cursor);
|
||||
StringBuilder builder = new StringBuilder("" + record.getThreadId());
|
||||
|
||||
for (long recipientId : record.getRecipients().getIds()) {
|
||||
builder.append("::").append(recipientId);
|
||||
}
|
||||
|
||||
return Conversions.byteArrayToLong(digest.digest(builder.toString().getBytes()));
|
||||
}
|
||||
|
||||
@@ -116,10 +107,51 @@ public class ConversationListAdapter extends CursorRecyclerViewAdapter<Conversat
|
||||
}
|
||||
}
|
||||
|
||||
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, boolean archived) {
|
||||
ConversationListItem listItem = (ConversationListItem)viewHolder.itemView;
|
||||
|
||||
if (!archived) {
|
||||
DatabaseFactory.getThreadDatabase(getContext()).archiveConversation(listItem.getThreadId());
|
||||
} else {
|
||||
DatabaseFactory.getThreadDatabase(getContext()).unarchiveConversation(listItem.getThreadId());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViewHolder onCreateItemViewHolder(ViewGroup parent, int viewType) {
|
||||
return new ViewHolder((ConversationListItem)inflater.inflate(R.layout.conversation_list_item_view,
|
||||
parent, false), clickListener);
|
||||
if (viewType == MESSAGE_TYPE_SWITCH_ARCHIVE) {
|
||||
ConversationListItemAction action = (ConversationListItemAction)inflater.inflate(R.layout.conversation_list_item_action,
|
||||
parent, false);
|
||||
|
||||
action.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (clickListener != null) clickListener.onSwitchToArchive();
|
||||
}
|
||||
});
|
||||
|
||||
return new ViewHolder(action);
|
||||
} else {
|
||||
final ConversationListItem item = (ConversationListItem)inflater.inflate(R.layout.conversation_list_item_view,
|
||||
parent, false);
|
||||
|
||||
item.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (clickListener != null) clickListener.onItemClick(item);
|
||||
}
|
||||
});
|
||||
|
||||
item.setOnLongClickListener(new OnLongClickListener() {
|
||||
@Override
|
||||
public boolean onLongClick(View view) {
|
||||
if (clickListener != null) clickListener.onItemLongClick(item);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
return new ViewHolder(item);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -129,7 +161,18 @@ public class ConversationListAdapter extends CursorRecyclerViewAdapter<Conversat
|
||||
|
||||
@Override
|
||||
public void onBindItemViewHolder(ViewHolder viewHolder, @NonNull Cursor cursor) {
|
||||
viewHolder.getItem().set(masterSecret, getThreadRecord(cursor), locale, batchSet, batchMode);
|
||||
viewHolder.getItem().bind(masterSecret, getThreadRecord(cursor), locale, batchSet, batchMode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(@NonNull Cursor cursor) {
|
||||
ThreadRecord threadRecord = getThreadRecord(cursor);
|
||||
|
||||
if (threadRecord.getDistributionType() == ThreadDatabase.DistributionTypes.ARCHIVE) {
|
||||
return MESSAGE_TYPE_SWITCH_ARCHIVE;
|
||||
} else {
|
||||
return MESSAGE_TYPE_THREAD;
|
||||
}
|
||||
}
|
||||
|
||||
private ThreadRecord getThreadRecord(@NonNull Cursor cursor) {
|
||||
@@ -168,5 +211,6 @@ public class ConversationListAdapter extends CursorRecyclerViewAdapter<Conversat
|
||||
public interface ItemClickListener {
|
||||
void onItemClick(ConversationListItem item);
|
||||
void onItemLongClick(ConversationListItem item);
|
||||
void onSwitchToArchive();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||
import org.thoughtcrime.securesms.util.DynamicLanguage;
|
||||
import org.thoughtcrime.securesms.util.DynamicTheme;
|
||||
|
||||
public class ConversationListArchiveActivity extends PassphraseRequiredActionBarActivity
|
||||
implements ConversationListFragment.ConversationSelectedListener
|
||||
{
|
||||
|
||||
private final DynamicTheme dynamicTheme = new DynamicTheme();
|
||||
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
|
||||
|
||||
@Override
|
||||
protected void onPreCreate() {
|
||||
dynamicTheme.onCreate(this);
|
||||
dynamicLanguage.onCreate(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle icicle, @NonNull MasterSecret masterSecret) {
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putBoolean(ConversationListFragment.ARCHIVE, true);
|
||||
|
||||
initFragment(android.R.id.content, new ConversationListFragment(),
|
||||
masterSecret, dynamicLanguage.getCurrentLocale(), bundle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
dynamicTheme.onResume(this);
|
||||
dynamicLanguage.onResume(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
super.onOptionsItemSelected(item);
|
||||
|
||||
switch (item.getItemId()) {
|
||||
case R.id.home: super.onBackPressed(); return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateConversation(long threadId, Recipients recipients, int distributionType) {
|
||||
Intent intent = new Intent(this, ConversationActivity.class);
|
||||
intent.putExtra(ConversationActivity.RECIPIENTS_EXTRA, recipients.getIds());
|
||||
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId);
|
||||
intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, distributionType);
|
||||
|
||||
startActivity(intent);
|
||||
overridePendingTransition(R.anim.slide_from_right, R.anim.fade_scale_out);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSwitchToArchive() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (C) 2011 Whisper Systems
|
||||
* Copyright (C) 2015 Open Whisper Systems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -20,21 +20,31 @@ import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.design.widget.FloatingActionButton;
|
||||
import android.support.design.widget.Snackbar;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.LoaderManager;
|
||||
import android.support.v4.content.Loader;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.view.ActionMode;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.support.v7.widget.helper.ItemTouchHelper;
|
||||
import android.text.TextUtils;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
@@ -43,7 +53,6 @@ import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.melnykov.fab.FloatingActionButton;
|
||||
|
||||
import org.thoughtcrime.securesms.ConversationListAdapter.ItemClickListener;
|
||||
import org.thoughtcrime.securesms.components.reminder.DefaultSmsReminder;
|
||||
@@ -60,8 +69,11 @@ import org.thoughtcrime.securesms.database.loaders.ConversationListLoader;
|
||||
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
import org.thoughtcrime.securesms.util.task.SnackbarAsyncTask;
|
||||
import org.whispersystems.libaxolotl.util.guava.Optional;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -69,6 +81,9 @@ import java.util.Set;
|
||||
public class ConversationListFragment extends Fragment
|
||||
implements LoaderManager.LoaderCallbacks<Cursor>, ActionMode.Callback, ItemClickListener
|
||||
{
|
||||
|
||||
public static final String ARCHIVE = "archive";
|
||||
|
||||
private MasterSecret masterSecret;
|
||||
private ActionMode actionMode;
|
||||
private RecyclerView list;
|
||||
@@ -76,27 +91,39 @@ public class ConversationListFragment extends Fragment
|
||||
private FloatingActionButton fab;
|
||||
private Locale locale;
|
||||
private String queryFilter = "";
|
||||
private boolean archive;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle icicle) {
|
||||
super.onCreate(icicle);
|
||||
masterSecret = getArguments().getParcelable("master_secret");
|
||||
locale = (Locale) getArguments().getSerializable(PassphraseRequiredActionBarActivity.LOCALE_EXTRA);
|
||||
archive = getArguments().getBoolean(ARCHIVE, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
|
||||
final View view = inflater.inflate(R.layout.conversation_list_fragment, container, false);
|
||||
reminderView = (ReminderView) view.findViewById(R.id.reminder);
|
||||
list = (RecyclerView) view.findViewById(R.id.list);
|
||||
fab = (FloatingActionButton) view.findViewById(R.id.fab);
|
||||
|
||||
reminderView = ViewUtil.findById(view, R.id.reminder);
|
||||
list = ViewUtil.findById(view, R.id.list);
|
||||
fab = ViewUtil.findById(view, R.id.fab);
|
||||
|
||||
if (archive) fab.setVisibility(View.GONE);
|
||||
else fab.setVisibility(View.VISIBLE);
|
||||
|
||||
reminderView.setOnDismissListener(new OnDismissListener() {
|
||||
@Override public void onDismiss() {
|
||||
@Override
|
||||
public void onDismiss() {
|
||||
updateReminders();
|
||||
}
|
||||
});
|
||||
|
||||
list.setHasFixedSize(true);
|
||||
list.setLayoutManager(new LinearLayoutManager(getActivity()));
|
||||
|
||||
new ItemTouchHelper(new ArchiveListenerCallback()).attachToRecyclerView(list);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@@ -170,6 +197,49 @@ public class ConversationListFragment extends Fragment
|
||||
getLoaderManager().restartLoader(0, null, this);
|
||||
}
|
||||
|
||||
private void handleArchiveAllSelected() {
|
||||
final Set<Long> selectedConversations = new HashSet<>(getListAdapter().getBatchSelections());
|
||||
final boolean archive = this.archive;
|
||||
|
||||
String snackBarTitle;
|
||||
|
||||
if (archive) snackBarTitle = getString(R.string.ConversationListFragment_moved_conversations_to_inbox);
|
||||
else snackBarTitle = getString(R.string.ConversationListFragment_archived_conversations);
|
||||
|
||||
new SnackbarAsyncTask<Void>(getView(), snackBarTitle,
|
||||
getString(R.string.ConversationListFragment_undo),
|
||||
getResources().getColor(R.color.amber_500),
|
||||
Snackbar.LENGTH_LONG, true)
|
||||
{
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void result) {
|
||||
super.onPostExecute(result);
|
||||
|
||||
if (actionMode != null) {
|
||||
actionMode.finish();
|
||||
actionMode = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void executeAction(@Nullable Void parameter) {
|
||||
for (long threadId : selectedConversations) {
|
||||
if (!archive) DatabaseFactory.getThreadDatabase(getActivity()).archiveConversation(threadId);
|
||||
else DatabaseFactory.getThreadDatabase(getActivity()).unarchiveConversation(threadId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void reverseAction(@Nullable Void parameter) {
|
||||
for (long threadId : selectedConversations) {
|
||||
if (!archive) DatabaseFactory.getThreadDatabase(getActivity()).unarchiveConversation(threadId);
|
||||
else DatabaseFactory.getThreadDatabase(getActivity()).archiveConversation(threadId);
|
||||
}
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
private void handleDeleteAllSelected() {
|
||||
int conversationsCount = getListAdapter().getBatchSelections().size();
|
||||
AlertDialog.Builder alert = new AlertDialog.Builder(getActivity());
|
||||
@@ -225,7 +295,7 @@ public class ConversationListFragment extends Fragment
|
||||
private void handleSelectAllThreads() {
|
||||
getListAdapter().selectAllThreads();
|
||||
actionMode.setSubtitle(getString(R.string.conversation_fragment_cab__batch_selection_amount,
|
||||
getListAdapter().getBatchSelections().size()));
|
||||
getListAdapter().getBatchSelections().size()));
|
||||
}
|
||||
|
||||
private void handleCreateConversation(long threadId, Recipients recipients, int distributionType) {
|
||||
@@ -234,7 +304,7 @@ public class ConversationListFragment extends Fragment
|
||||
|
||||
@Override
|
||||
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
|
||||
return new ConversationListLoader(getActivity(), queryFilter);
|
||||
return new ConversationListLoader(getActivity(), queryFilter, archive);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -276,21 +346,30 @@ public class ConversationListFragment extends Fragment
|
||||
getListAdapter().notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSwitchToArchive() {
|
||||
((ConversationSelectedListener)getActivity()).onSwitchToArchive();
|
||||
}
|
||||
|
||||
public interface ConversationSelectedListener {
|
||||
void onCreateConversation(long threadId, Recipients recipients, int distributionType);
|
||||
void onSwitchToArchive();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
|
||||
MenuInflater inflater = getActivity().getMenuInflater();
|
||||
|
||||
if (archive) inflater.inflate(R.menu.conversation_list_batch_unarchive, menu);
|
||||
else inflater.inflate(R.menu.conversation_list_batch_archive, menu);
|
||||
|
||||
inflater.inflate(R.menu.conversation_list_batch, menu);
|
||||
|
||||
mode.setTitle(R.string.conversation_fragment_cab__batch_selection_mode);
|
||||
mode.setSubtitle(getString(R.string.conversation_fragment_cab__batch_selection_amount, 1));
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
getActivity().getWindow()
|
||||
.setStatusBarColor(getResources().getColor(R.color.action_mode_status_bar));
|
||||
getActivity().getWindow().setStatusBarColor(getResources().getColor(R.color.action_mode_status_bar));
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -304,8 +383,9 @@ public class ConversationListFragment extends Fragment
|
||||
@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: handleDeleteAllSelected(); return true;
|
||||
case R.id.menu_select_all: handleSelectAllThreads(); return true;
|
||||
case R.id.menu_delete_selected: handleDeleteAllSelected(); return true;
|
||||
case R.id.menu_archive_selected: handleArchiveAllSelected(); return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -316,8 +396,7 @@ public class ConversationListFragment extends Fragment
|
||||
getListAdapter().initializeBatchMode(false);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
TypedArray color = getActivity().getTheme()
|
||||
.obtainStyledAttributes(new int[] { android.R.attr.statusBarColor });
|
||||
TypedArray color = getActivity().getTheme().obtainStyledAttributes(new int[] {android.R.attr.statusBarColor});
|
||||
getActivity().getWindow().setStatusBarColor(color.getColor(0, Color.BLACK));
|
||||
color.recycle();
|
||||
}
|
||||
@@ -325,6 +404,114 @@ public class ConversationListFragment extends Fragment
|
||||
actionMode = null;
|
||||
}
|
||||
|
||||
private class ArchiveListenerCallback extends ItemTouchHelper.SimpleCallback {
|
||||
|
||||
public ArchiveListenerCallback() {
|
||||
super(0, ItemTouchHelper.RIGHT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMove(RecyclerView recyclerView,
|
||||
RecyclerView.ViewHolder viewHolder,
|
||||
RecyclerView.ViewHolder target)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSwipeDirs(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
|
||||
if (viewHolder.itemView instanceof ConversationListItemAction) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (actionMode != null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return super.getSwipeDirs(recyclerView, viewHolder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
|
||||
final long threadId = ((ConversationListItem)viewHolder.itemView).getThreadId();
|
||||
|
||||
if (archive) {
|
||||
new SnackbarAsyncTask<Long>(getView(),
|
||||
getString(R.string.ConversationListFragment_moved_conversation_to_inbox),
|
||||
getString(R.string.ConversationListFragment_undo),
|
||||
getResources().getColor(R.color.amber_500),
|
||||
Snackbar.LENGTH_SHORT, false)
|
||||
{
|
||||
@Override
|
||||
protected void executeAction(@Nullable Long parameter) {
|
||||
DatabaseFactory.getThreadDatabase(getActivity()).unarchiveConversation(threadId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void reverseAction(@Nullable Long parameter) {
|
||||
DatabaseFactory.getThreadDatabase(getActivity()).archiveConversation(threadId);
|
||||
}
|
||||
}.execute(threadId);
|
||||
} else {
|
||||
new SnackbarAsyncTask<Long>(getView(),
|
||||
getString(R.string.ConversationListFragment_archived_conversation),
|
||||
getString(R.string.ConversationListFragment_undo),
|
||||
getResources().getColor(R.color.amber_500),
|
||||
Snackbar.LENGTH_SHORT, false)
|
||||
{
|
||||
@Override
|
||||
protected void executeAction(@Nullable Long parameter) {
|
||||
DatabaseFactory.getThreadDatabase(getActivity()).archiveConversation(threadId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void reverseAction(@Nullable Long parameter) {
|
||||
DatabaseFactory.getThreadDatabase(getActivity()).unarchiveConversation(threadId);
|
||||
}
|
||||
}.execute(threadId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChildDraw(Canvas c, RecyclerView recyclerView,
|
||||
RecyclerView.ViewHolder viewHolder,
|
||||
float dX, float dY, int actionState,
|
||||
boolean isCurrentlyActive)
|
||||
{
|
||||
|
||||
if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
|
||||
View itemView = viewHolder.itemView;
|
||||
Paint p = new Paint();
|
||||
|
||||
if (dX > 0) {
|
||||
Bitmap icon;
|
||||
|
||||
if (archive) icon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_unarchive_white_36dp);
|
||||
else icon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_archive_white_36dp);
|
||||
|
||||
p.setColor(getResources().getColor(R.color.green_500));
|
||||
|
||||
c.drawRect((float) itemView.getLeft(), (float) itemView.getTop(), dX,
|
||||
(float) itemView.getBottom(), p);
|
||||
|
||||
c.drawBitmap(icon,
|
||||
(float) itemView.getLeft() + getResources().getDimension(R.dimen.conversation_list_fragment_archive_padding),
|
||||
(float) itemView.getTop() + ((float) itemView.getBottom() - (float) itemView.getTop() - icon.getHeight())/2,
|
||||
p);
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 11) {
|
||||
float alpha = 1.0f - Math.abs(dX) / (float) viewHolder.itemView.getWidth();
|
||||
viewHolder.itemView.setAlpha(alpha);
|
||||
viewHolder.itemView.setTranslationX(dX);
|
||||
}
|
||||
|
||||
} else {
|
||||
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ import org.thoughtcrime.securesms.database.model.ThreadRecord;
|
||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||
import org.thoughtcrime.securesms.util.DateUtils;
|
||||
import org.thoughtcrime.securesms.util.ResUtil;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
@@ -53,7 +54,8 @@ import static org.thoughtcrime.securesms.util.SpanUtil.color;
|
||||
*/
|
||||
|
||||
public class ConversationListItem extends RelativeLayout
|
||||
implements Recipients.RecipientsModifiedListener, Unbindable
|
||||
implements Recipients.RecipientsModifiedListener,
|
||||
BindableConversationListItem, Unbindable
|
||||
{
|
||||
private final static String TAG = ConversationListItem.class.getSimpleName();
|
||||
|
||||
@@ -66,6 +68,7 @@ public class ConversationListItem extends RelativeLayout
|
||||
private TextView subjectView;
|
||||
private FromTextView fromView;
|
||||
private TextView dateView;
|
||||
private TextView archivedView;
|
||||
private boolean read;
|
||||
private AvatarImageView contactPhotoImage;
|
||||
private ThumbnailView thumbnailView;
|
||||
@@ -94,11 +97,12 @@ public class ConversationListItem extends RelativeLayout
|
||||
this.dateView = (TextView) findViewById(R.id.date);
|
||||
this.contactPhotoImage = (AvatarImageView) findViewById(R.id.contact_photo_image);
|
||||
this.thumbnailView = (ThumbnailView) findViewById(R.id.thumbnail);
|
||||
this.archivedView = ViewUtil.findById(this, R.id.archived);
|
||||
thumbnailView.setClickable(false);
|
||||
}
|
||||
|
||||
public void set(@NonNull MasterSecret masterSecret, @NonNull ThreadRecord thread,
|
||||
@NonNull Locale locale, @NonNull Set<Long> selectedThreads, boolean batchMode)
|
||||
public void bind(@NonNull MasterSecret masterSecret, @NonNull ThreadRecord thread,
|
||||
@NonNull Locale locale, @NonNull Set<Long> selectedThreads, boolean batchMode)
|
||||
{
|
||||
this.selectedThreads = selectedThreads;
|
||||
this.recipients = thread.getRecipients();
|
||||
@@ -118,6 +122,12 @@ public class ConversationListItem extends RelativeLayout
|
||||
dateView.setTypeface(read ? LIGHT_TYPEFACE : BOLD_TYPEFACE);
|
||||
}
|
||||
|
||||
if (thread.isArchived()) {
|
||||
this.archivedView.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
this.archivedView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
setThumbnailSnippet(masterSecret, thread);
|
||||
setBatchState(batchMode);
|
||||
setBackground(thread);
|
||||
@@ -158,7 +168,7 @@ public class ConversationListItem extends RelativeLayout
|
||||
this.thumbnailView.setVisibility(View.GONE);
|
||||
|
||||
LayoutParams subjectParams = (RelativeLayout.LayoutParams)this.subjectView.getLayoutParams();
|
||||
subjectParams.addRule(RelativeLayout.LEFT_OF, 0);
|
||||
subjectParams.addRule(RelativeLayout.LEFT_OF, R.id.archived);
|
||||
this.subjectView.setLayoutParams(subjectParams);
|
||||
}
|
||||
}
|
||||
@@ -187,4 +197,5 @@ public class ConversationListItem extends RelativeLayout
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.database.model.ThreadRecord;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
public class ConversationListItemAction extends LinearLayout implements BindableConversationListItem {
|
||||
|
||||
private TextView description;
|
||||
|
||||
public ConversationListItemAction(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public ConversationListItemAction(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
public ConversationListItemAction(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
this.description = ViewUtil.findById(this, R.id.description);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(@NonNull MasterSecret masterSecret, @NonNull ThreadRecord thread, @NonNull Locale locale, @NonNull Set<Long> selectedThreads, boolean batchMode) {
|
||||
this.description.setText(getContext().getString(R.string.ConversationListItemAction_archived_conversations_d, thread.getCount()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unbind() {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ import org.thoughtcrime.securesms.push.TextSecureCommunicationFactory;
|
||||
import org.thoughtcrime.securesms.util.Base64;
|
||||
import org.thoughtcrime.securesms.util.DynamicLanguage;
|
||||
import org.thoughtcrime.securesms.util.DynamicTheme;
|
||||
import org.thoughtcrime.securesms.util.ProgressDialogAsyncTask;
|
||||
import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.whispersystems.libaxolotl.IdentityKeyPair;
|
||||
|
||||
@@ -22,7 +22,7 @@ import com.melnykov.fab.FloatingActionButton;
|
||||
|
||||
import org.thoughtcrime.securesms.database.loaders.DeviceListLoader;
|
||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
||||
import org.thoughtcrime.securesms.util.ProgressDialogAsyncTask;
|
||||
import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
import org.whispersystems.textsecure.api.TextSecureAccountManager;
|
||||
|
||||
@@ -16,7 +16,7 @@ import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.push.TextSecureCommunicationFactory;
|
||||
import org.thoughtcrime.securesms.util.Base64;
|
||||
import org.thoughtcrime.securesms.util.ProgressDialogAsyncTask;
|
||||
import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.whispersystems.libaxolotl.IdentityKeyPair;
|
||||
import org.whispersystems.libaxolotl.InvalidKeyException;
|
||||
|
||||
@@ -65,7 +65,7 @@ import org.thoughtcrime.securesms.util.BitmapUtil;
|
||||
import org.thoughtcrime.securesms.util.DynamicLanguage;
|
||||
import org.thoughtcrime.securesms.util.DynamicTheme;
|
||||
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||
import org.thoughtcrime.securesms.util.ProgressDialogAsyncTask;
|
||||
import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask;
|
||||
import org.thoughtcrime.securesms.util.SelectedRecipientsAdapter;
|
||||
import org.thoughtcrime.securesms.util.SelectedRecipientsAdapter.OnRecipientDeletedListener;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
|
||||
@@ -32,7 +32,7 @@ import org.thoughtcrime.securesms.recipients.RecipientFactory;
|
||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||
import org.thoughtcrime.securesms.sms.MessageSender;
|
||||
import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
|
||||
import org.thoughtcrime.securesms.util.ProgressDialogAsyncTask;
|
||||
import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
import org.thoughtcrime.securesms.util.concurrent.ListenableFuture.Listener;
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ public class ShareFragment extends ListFragment implements LoaderManager.LoaderC
|
||||
|
||||
@Override
|
||||
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
|
||||
return new ConversationListLoader(getActivity(), null);
|
||||
return new ConversationListLoader(getActivity(), null, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -68,7 +68,8 @@ public class DatabaseFactory {
|
||||
private static final int INTRODUCED_DB_OPTIMIZATIONS_VERSION = 21;
|
||||
private static final int INTRODUCED_INVITE_REMINDERS_VERSION = 22;
|
||||
private static final int INTRODUCED_CONVERSATION_LIST_THUMBNAILS_VERSION = 23;
|
||||
private static final int DATABASE_VERSION = 23;
|
||||
private static final int INTRODUCED_ARCHIVE_VERSION = 24;
|
||||
private static final int DATABASE_VERSION = 24;
|
||||
|
||||
private static final String DATABASE_NAME = "messages.db";
|
||||
private static final Object lock = new Object();
|
||||
@@ -778,6 +779,11 @@ public class DatabaseFactory {
|
||||
db.execSQL("ALTER TABLE thread ADD COLUMN snippet_uri TEXT DEFAULT NULL");
|
||||
}
|
||||
|
||||
if (oldVersion < INTRODUCED_ARCHIVE_VERSION) {
|
||||
db.execSQL("ALTER TABLE thread ADD COLUMN archived INTEGER DEFAULT 0");
|
||||
db.execSQL("CREATE INDEX IF NOT EXISTS archived_index ON thread (archived)");
|
||||
}
|
||||
|
||||
db.setTransactionSuccessful();
|
||||
db.endTransaction();
|
||||
}
|
||||
|
||||
@@ -449,7 +449,7 @@ public class MmsDatabase extends MessagingDatabase {
|
||||
|
||||
long threadId = getThreadIdForMessage(messageId);
|
||||
|
||||
DatabaseFactory.getThreadDatabase(context).update(threadId);
|
||||
DatabaseFactory.getThreadDatabase(context).update(threadId, true);
|
||||
notifyConversationListeners(threadId);
|
||||
notifyConversationListListeners();
|
||||
|
||||
@@ -604,7 +604,7 @@ public class MmsDatabase extends MessagingDatabase {
|
||||
contentValues);
|
||||
|
||||
DatabaseFactory.getThreadDatabase(context).setUnread(threadId);
|
||||
DatabaseFactory.getThreadDatabase(context).update(threadId);
|
||||
DatabaseFactory.getThreadDatabase(context).update(threadId, true);
|
||||
notifyConversationListeners(threadId);
|
||||
jobManager.add(new TrimThreadJob(context, threadId));
|
||||
|
||||
@@ -692,7 +692,7 @@ public class MmsDatabase extends MessagingDatabase {
|
||||
|
||||
public void markIncomingNotificationReceived(long threadId) {
|
||||
notifyConversationListeners(threadId);
|
||||
DatabaseFactory.getThreadDatabase(context).update(threadId);
|
||||
DatabaseFactory.getThreadDatabase(context).update(threadId, true);
|
||||
|
||||
if (org.thoughtcrime.securesms.util.Util.isDefaultSmsProvider(context)) {
|
||||
DatabaseFactory.getThreadDatabase(context).setUnread(threadId);
|
||||
@@ -808,7 +808,7 @@ public class MmsDatabase extends MessagingDatabase {
|
||||
db.endTransaction();
|
||||
|
||||
notifyConversationListeners(contentValues.getAsLong(THREAD_ID));
|
||||
DatabaseFactory.getThreadDatabase(context).update(contentValues.getAsLong(THREAD_ID));
|
||||
DatabaseFactory.getThreadDatabase(context).update(contentValues.getAsLong(THREAD_ID), true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -821,7 +821,7 @@ public class MmsDatabase extends MessagingDatabase {
|
||||
|
||||
SQLiteDatabase database = databaseHelper.getWritableDatabase();
|
||||
database.delete(TABLE_NAME, ID_WHERE, new String[] {messageId+""});
|
||||
boolean threadDeleted = DatabaseFactory.getThreadDatabase(context).update(threadId);
|
||||
boolean threadDeleted = DatabaseFactory.getThreadDatabase(context).update(threadId, false);
|
||||
notifyConversationListeners(threadId);
|
||||
return threadDeleted;
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ public class PlaintextBackupImporter {
|
||||
}
|
||||
|
||||
for (long threadId : modifiedThreads) {
|
||||
threads.update(threadId);
|
||||
threads.update(threadId, true);
|
||||
}
|
||||
|
||||
Log.w("PlaintextBackupImporter", "Exited loop");
|
||||
|
||||
@@ -118,7 +118,7 @@ public class SmsDatabase extends MessagingDatabase {
|
||||
|
||||
long threadId = getThreadIdForMessage(id);
|
||||
|
||||
DatabaseFactory.getThreadDatabase(context).update(threadId);
|
||||
DatabaseFactory.getThreadDatabase(context).update(threadId, false);
|
||||
notifyConversationListeners(threadId);
|
||||
notifyConversationListListeners();
|
||||
}
|
||||
@@ -310,7 +310,7 @@ public class SmsDatabase extends MessagingDatabase {
|
||||
|
||||
long threadId = getThreadIdForMessage(messageId);
|
||||
|
||||
DatabaseFactory.getThreadDatabase(context).update(threadId);
|
||||
DatabaseFactory.getThreadDatabase(context).update(threadId, true);
|
||||
notifyConversationListeners(threadId);
|
||||
notifyConversationListListeners();
|
||||
|
||||
@@ -335,7 +335,7 @@ public class SmsDatabase extends MessagingDatabase {
|
||||
SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
||||
long newMessageId = db.insert(TABLE_NAME, null, contentValues);
|
||||
|
||||
DatabaseFactory.getThreadDatabase(context).update(record.getThreadId());
|
||||
DatabaseFactory.getThreadDatabase(context).update(record.getThreadId(), true);
|
||||
notifyConversationListeners(record.getThreadId());
|
||||
|
||||
jobManager.add(new TrimThreadJob(context, record.getThreadId()));
|
||||
@@ -372,7 +372,7 @@ public class SmsDatabase extends MessagingDatabase {
|
||||
SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
||||
long messageId = db.insert(TABLE_NAME, null, values);
|
||||
|
||||
DatabaseFactory.getThreadDatabase(context).update(threadId);
|
||||
DatabaseFactory.getThreadDatabase(context).update(threadId, true);
|
||||
notifyConversationListeners(threadId);
|
||||
jobManager.add(new TrimThreadJob(context, threadId));
|
||||
|
||||
@@ -450,7 +450,7 @@ public class SmsDatabase extends MessagingDatabase {
|
||||
DatabaseFactory.getThreadDatabase(context).setUnread(threadId);
|
||||
}
|
||||
|
||||
DatabaseFactory.getThreadDatabase(context).update(threadId);
|
||||
DatabaseFactory.getThreadDatabase(context).update(threadId, true);
|
||||
notifyConversationListeners(threadId);
|
||||
jobManager.add(new TrimThreadJob(context, threadId));
|
||||
|
||||
@@ -481,7 +481,7 @@ public class SmsDatabase extends MessagingDatabase {
|
||||
SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
||||
long messageId = db.insert(TABLE_NAME, ADDRESS, contentValues);
|
||||
|
||||
DatabaseFactory.getThreadDatabase(context).update(threadId);
|
||||
DatabaseFactory.getThreadDatabase(context).update(threadId, true);
|
||||
notifyConversationListeners(threadId);
|
||||
jobManager.add(new TrimThreadJob(context, threadId));
|
||||
|
||||
@@ -526,7 +526,7 @@ public class SmsDatabase extends MessagingDatabase {
|
||||
SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
||||
long threadId = getThreadIdForMessage(messageId);
|
||||
db.delete(TABLE_NAME, ID_WHERE, new String[] {messageId+""});
|
||||
boolean threadDeleted = DatabaseFactory.getThreadDatabase(context).update(threadId);
|
||||
boolean threadDeleted = DatabaseFactory.getThreadDatabase(context).update(threadId, false);
|
||||
notifyConversationListeners(threadId);
|
||||
return threadDeleted;
|
||||
}
|
||||
|
||||
@@ -197,7 +197,7 @@ public class SmsMigrator {
|
||||
}
|
||||
|
||||
ourSmsDatabase.endTransaction(transaction);
|
||||
DatabaseFactory.getThreadDatabase(context).update(ourThreadId);
|
||||
DatabaseFactory.getThreadDatabase(context).update(ourThreadId, true);
|
||||
DatabaseFactory.getThreadDatabase(context).notifyConversationListeners(ourThreadId);
|
||||
|
||||
} finally {
|
||||
|
||||
@@ -59,19 +59,21 @@ public class ThreadDatabase extends Database {
|
||||
public static final String SNIPPET = "snippet";
|
||||
private static final String SNIPPET_CHARSET = "snippet_cs";
|
||||
public static final String READ = "read";
|
||||
private static final String TYPE = "type";
|
||||
public static final String TYPE = "type";
|
||||
private static final String ERROR = "error";
|
||||
public static final String SNIPPET_TYPE = "snippet_type";
|
||||
private static final String SNIPPET_URI = "snippet_uri";
|
||||
public static final String SNIPPET_URI = "snippet_uri";
|
||||
public static final String ARCHIVED = "archived";
|
||||
|
||||
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY, " +
|
||||
DATE + " INTEGER DEFAULT 0, " + MESSAGE_COUNT + " INTEGER DEFAULT 0, " +
|
||||
RECIPIENT_IDS + " TEXT, " + SNIPPET + " TEXT, " + SNIPPET_CHARSET + " INTEGER DEFAULT 0, " +
|
||||
READ + " INTEGER DEFAULT 1, " + TYPE + " INTEGER DEFAULT 0, " + ERROR + " INTEGER DEFAULT 0, " +
|
||||
SNIPPET_TYPE + " INTEGER DEFAULT 0, " + SNIPPET_URI + " TEXT DEFAULT NULL);";
|
||||
SNIPPET_TYPE + " INTEGER DEFAULT 0, " + SNIPPET_URI + " TEXT DEFAULT NULL, " + ARCHIVED + " INTEGER DEFAULT 0);";
|
||||
|
||||
public static final String[] CREATE_INDEXS = {
|
||||
"CREATE INDEX IF NOT EXISTS thread_recipient_ids_index ON " + TABLE_NAME + " (" + RECIPIENT_IDS + ");",
|
||||
"CREATE INDEX IF NOT EXISTS archived_index ON " + TABLE_NAME + " (" + ARCHIVED + ");",
|
||||
};
|
||||
|
||||
public ThreadDatabase(Context context, SQLiteOpenHelper databaseHelper) {
|
||||
@@ -124,27 +126,36 @@ public class ThreadDatabase extends Database {
|
||||
return db.insert(TABLE_NAME, null, contentValues);
|
||||
}
|
||||
|
||||
private void updateThread(long threadId, long count, String body, @Nullable Uri attachment, long date, long type)
|
||||
private void updateThread(long threadId, long count, String body, @Nullable Uri attachment, long date, long type, boolean unarchive)
|
||||
{
|
||||
ContentValues contentValues = new ContentValues(4);
|
||||
ContentValues contentValues = new ContentValues(5);
|
||||
contentValues.put(DATE, date - date % 1000);
|
||||
contentValues.put(MESSAGE_COUNT, count);
|
||||
contentValues.put(SNIPPET, body);
|
||||
contentValues.put(SNIPPET_URI, attachment == null ? null : attachment.toString());
|
||||
contentValues.put(SNIPPET_TYPE, type);
|
||||
|
||||
if (unarchive) {
|
||||
contentValues.put(ARCHIVED, 0);
|
||||
}
|
||||
|
||||
SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
||||
db.update(TABLE_NAME, contentValues, ID + " = ?", new String[] {threadId + ""});
|
||||
notifyConversationListListeners();
|
||||
}
|
||||
|
||||
public void updateSnippet(long threadId, String snippet, @Nullable Uri attachment, long date, long type) {
|
||||
public void updateSnippet(long threadId, String snippet, @Nullable Uri attachment, long date, long type, boolean unarchive) {
|
||||
ContentValues contentValues = new ContentValues(3);
|
||||
|
||||
contentValues.put(DATE, date - date % 1000);
|
||||
contentValues.put(SNIPPET, snippet);
|
||||
contentValues.put(SNIPPET_TYPE, type);
|
||||
contentValues.put(SNIPPET_URI, attachment == null ? null : attachment.toString());
|
||||
|
||||
if (unarchive) {
|
||||
contentValues.put(ARCHIVED, 0);
|
||||
}
|
||||
|
||||
SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
||||
db.update(TABLE_NAME, contentValues, ID + " = ?", new String[] {threadId + ""});
|
||||
notifyConversationListListeners();
|
||||
@@ -217,7 +228,7 @@ public class ThreadDatabase extends Database {
|
||||
DatabaseFactory.getSmsDatabase(context).deleteMessagesInThreadBeforeDate(threadId, lastTweetDate);
|
||||
DatabaseFactory.getMmsDatabase(context).deleteMessagesInThreadBeforeDate(threadId, lastTweetDate);
|
||||
|
||||
update(threadId);
|
||||
update(threadId, false);
|
||||
notifyConversationListeners(threadId);
|
||||
}
|
||||
} finally {
|
||||
@@ -302,12 +313,60 @@ public class ThreadDatabase extends Database {
|
||||
}
|
||||
|
||||
public Cursor getConversationList() {
|
||||
SQLiteDatabase db = databaseHelper.getReadableDatabase();
|
||||
Cursor cursor = db.query(TABLE_NAME, null, null, null, null, null, DATE + " DESC");
|
||||
SQLiteDatabase db = databaseHelper.getReadableDatabase();
|
||||
Cursor cursor = db.query(TABLE_NAME, null, ARCHIVED + " = ?", new String[] {"0"}, null, null, DATE + " DESC");
|
||||
|
||||
setNotifyConverationListListeners(cursor);
|
||||
|
||||
return cursor;
|
||||
}
|
||||
|
||||
public Cursor getArchivedConversationList() {
|
||||
SQLiteDatabase db = databaseHelper.getReadableDatabase();
|
||||
Cursor cursor = db.query(TABLE_NAME, null, ARCHIVED + " = ?", new String[] {"1"}, null, null, DATE + " DESC");
|
||||
|
||||
setNotifyConverationListListeners(cursor);
|
||||
|
||||
return cursor;
|
||||
}
|
||||
|
||||
public int getArchivedConversationListCount() {
|
||||
SQLiteDatabase db = databaseHelper.getReadableDatabase();
|
||||
Cursor cursor = null;
|
||||
|
||||
try {
|
||||
cursor = db.query(TABLE_NAME, new String[] {"COUNT(*)"}, ARCHIVED + " = ?",
|
||||
new String[] {"1"}, null, null, null);
|
||||
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
return cursor.getInt(0);
|
||||
}
|
||||
|
||||
} finally {
|
||||
if (cursor != null) cursor.close();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void archiveConversation(long threadId) {
|
||||
SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
||||
ContentValues contentValues = new ContentValues(1);
|
||||
contentValues.put(ARCHIVED, 1);
|
||||
|
||||
db.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {threadId + ""});
|
||||
notifyConversationListListeners();
|
||||
}
|
||||
|
||||
public void unarchiveConversation(long threadId) {
|
||||
SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
||||
ContentValues contentValues = new ContentValues(1);
|
||||
contentValues.put(ARCHIVED, 0);
|
||||
|
||||
db.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {threadId + ""});
|
||||
notifyConversationListListeners();
|
||||
}
|
||||
|
||||
public void deleteConversation(long threadId) {
|
||||
DatabaseFactory.getSmsDatabase(context).deleteThread(threadId);
|
||||
DatabaseFactory.getMmsDatabase(context).deleteThread(threadId);
|
||||
@@ -317,7 +376,6 @@ public class ThreadDatabase extends Database {
|
||||
notifyConversationListListeners();
|
||||
}
|
||||
|
||||
|
||||
public void deleteConversations(Set<Long> selectedConversations) {
|
||||
DatabaseFactory.getSmsDatabase(context).deleteThreads(selectedConversations);
|
||||
DatabaseFactory.getMmsDatabase(context).deleteThreads(selectedConversations);
|
||||
@@ -399,7 +457,7 @@ public class ThreadDatabase extends Database {
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean update(long threadId) {
|
||||
public boolean update(long threadId, boolean unarchive) {
|
||||
MmsSmsDatabase mmsSmsDatabase = DatabaseFactory.getMmsSmsDatabase(context);
|
||||
long count = mmsSmsDatabase.getConversationCount(threadId);
|
||||
|
||||
@@ -421,7 +479,10 @@ public class ThreadDatabase extends Database {
|
||||
if (record.isPush()) timestamp = record.getDateSent();
|
||||
else timestamp = record.getDateReceived();
|
||||
|
||||
updateThread(threadId, count, record.getBody().getBody(), getAttachmentUriFor(record), timestamp, record.getType());
|
||||
updateThread(threadId, count, record.getBody().getBody(),
|
||||
getAttachmentUriFor(record), timestamp,
|
||||
record.getType(), unarchive);
|
||||
|
||||
notifyConversationListListeners();
|
||||
return false;
|
||||
} else {
|
||||
@@ -456,6 +517,7 @@ public class ThreadDatabase extends Database {
|
||||
public static final int DEFAULT = 2;
|
||||
public static final int BROADCAST = 1;
|
||||
public static final int CONVERSATION = 2;
|
||||
public static final int ARCHIVE = 3;
|
||||
}
|
||||
|
||||
public class Reader {
|
||||
@@ -486,10 +548,11 @@ public class ThreadDatabase extends Database {
|
||||
long read = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.READ));
|
||||
long type = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.SNIPPET_TYPE));
|
||||
int distributionType = cursor.getInt(cursor.getColumnIndexOrThrow(ThreadDatabase.TYPE));
|
||||
boolean archived = cursor.getInt(cursor.getColumnIndex(ThreadDatabase.ARCHIVED)) != 0;
|
||||
Uri snippetUri = getSnippetUri(cursor);
|
||||
|
||||
return new ThreadRecord(context, body, snippetUri, recipients, date, count,
|
||||
read == 1, threadId, type, distributionType);
|
||||
read == 1, threadId, type, distributionType, archived);
|
||||
}
|
||||
|
||||
private DisplayRecord.Body getPlaintextBody(Cursor cursor) {
|
||||
|
||||
@@ -2,30 +2,64 @@ package org.thoughtcrime.securesms.database.loaders;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.MatrixCursor;
|
||||
import android.database.MergeCursor;
|
||||
|
||||
import org.thoughtcrime.securesms.contacts.ContactAccessor;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
||||
import org.thoughtcrime.securesms.util.AbstractCursorLoader;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class ConversationListLoader extends AbstractCursorLoader {
|
||||
|
||||
private final String filter;
|
||||
private final boolean archived;
|
||||
|
||||
public ConversationListLoader(Context context, String filter) {
|
||||
public ConversationListLoader(Context context, String filter, boolean archived) {
|
||||
super(context);
|
||||
this.filter = filter;
|
||||
this.filter = filter;
|
||||
this.archived = archived;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor getCursor() {
|
||||
if (filter != null && filter.trim().length() != 0) {
|
||||
List<String> numbers = ContactAccessor.getInstance().getNumbersForThreadSearchFilter(context, filter);
|
||||
if (filter != null && filter.trim().length() != 0) return getFilteredConversationList(filter);
|
||||
else if (!archived) return getUnarchivedConversationList();
|
||||
else return getArchivedConversationList();
|
||||
}
|
||||
|
||||
return DatabaseFactory.getThreadDatabase(context).getFilteredConversationList(numbers);
|
||||
} else {
|
||||
return DatabaseFactory.getThreadDatabase(context).getConversationList();
|
||||
private Cursor getUnarchivedConversationList() {
|
||||
List<Cursor> cursorList = new LinkedList<>();
|
||||
cursorList.add(DatabaseFactory.getThreadDatabase(context).getConversationList());
|
||||
|
||||
int archivedCount = DatabaseFactory.getThreadDatabase(context)
|
||||
.getArchivedConversationListCount();
|
||||
|
||||
if (archivedCount > 0) {
|
||||
MatrixCursor switchToArchiveCursor = new MatrixCursor(new String[] {
|
||||
ThreadDatabase.ID, ThreadDatabase.DATE, ThreadDatabase.MESSAGE_COUNT,
|
||||
ThreadDatabase.RECIPIENT_IDS, ThreadDatabase.SNIPPET, ThreadDatabase.READ,
|
||||
ThreadDatabase.TYPE, ThreadDatabase.SNIPPET_TYPE, ThreadDatabase.SNIPPET_URI,
|
||||
ThreadDatabase.ARCHIVED}, 1);
|
||||
|
||||
switchToArchiveCursor.addRow(new Object[] {-1L, System.currentTimeMillis(), archivedCount,
|
||||
"-1", null, 1, ThreadDatabase.DistributionTypes.ARCHIVE, 0, null, 0});
|
||||
|
||||
cursorList.add(switchToArchiveCursor);
|
||||
}
|
||||
|
||||
return new MergeCursor(cursorList.toArray(new Cursor[0]));
|
||||
}
|
||||
|
||||
private Cursor getArchivedConversationList() {
|
||||
return DatabaseFactory.getThreadDatabase(context).getArchivedConversationList();
|
||||
}
|
||||
|
||||
private Cursor getFilteredConversationList(String filter) {
|
||||
List<String> numbers = ContactAccessor.getInstance().getNumbersForThreadSearchFilter(context, filter);
|
||||
return DatabaseFactory.getThreadDatabase(context).getFilteredConversationList(numbers);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,10 +44,11 @@ public class ThreadRecord extends DisplayRecord {
|
||||
private final long count;
|
||||
private final boolean read;
|
||||
private final int distributionType;
|
||||
private final boolean archived;
|
||||
|
||||
public ThreadRecord(@NonNull Context context, @NonNull Body body, @Nullable Uri snippetUri,
|
||||
@NonNull Recipients recipients, long date, long count, boolean read,
|
||||
long threadId, long snippetType, int distributionType)
|
||||
long threadId, long snippetType, int distributionType, boolean archived)
|
||||
{
|
||||
super(context, body, recipients, date, date, threadId, snippetType);
|
||||
this.context = context.getApplicationContext();
|
||||
@@ -55,6 +56,7 @@ public class ThreadRecord extends DisplayRecord {
|
||||
this.count = count;
|
||||
this.read = read;
|
||||
this.distributionType = distributionType;
|
||||
this.archived = archived;
|
||||
}
|
||||
|
||||
public @Nullable Uri getSnippetUri() {
|
||||
@@ -124,6 +126,10 @@ public class ThreadRecord extends DisplayRecord {
|
||||
return getDateReceived();
|
||||
}
|
||||
|
||||
public boolean isArchived() {
|
||||
return archived;
|
||||
}
|
||||
|
||||
public int getDistributionType() {
|
||||
return distributionType;
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ import org.thoughtcrime.securesms.contacts.ContactAccessor;
|
||||
import org.thoughtcrime.securesms.contacts.ContactIdentityManager;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.push.TextSecureCommunicationFactory;
|
||||
import org.thoughtcrime.securesms.util.ProgressDialogAsyncTask;
|
||||
import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.whispersystems.libaxolotl.util.guava.Optional;
|
||||
import org.whispersystems.textsecure.api.TextSecureAccountManager;
|
||||
|
||||
@@ -13,6 +13,7 @@ import android.widget.Toast;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.mms.PartAuthority;
|
||||
import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.thoughtcrime.securesms.util;
|
||||
package org.thoughtcrime.securesms.util.task;
|
||||
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
@@ -7,6 +7,7 @@ import android.os.AsyncTask;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
public abstract class ProgressDialogAsyncTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> {
|
||||
|
||||
private final WeakReference<Context> contextReference;
|
||||
private ProgressDialog progress;
|
||||
private final String title;
|
||||
@@ -0,0 +1,94 @@
|
||||
package org.thoughtcrime.securesms.util.task;
|
||||
|
||||
import android.app.ProgressDialog;
|
||||
import android.os.AsyncTask;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.design.widget.Snackbar;
|
||||
import android.view.View;
|
||||
|
||||
public abstract class SnackbarAsyncTask<Params>
|
||||
extends AsyncTask<Params, Void, Void>
|
||||
implements View.OnClickListener
|
||||
{
|
||||
|
||||
private final View view;
|
||||
private final String snackbarText;
|
||||
private final String snackbarActionText;
|
||||
private final int snackbarActionColor;
|
||||
private final int snackbarDuration;
|
||||
private final boolean showProgress;
|
||||
|
||||
private @Nullable Params reversibleParameter;
|
||||
private @Nullable ProgressDialog progressDialog;
|
||||
|
||||
public SnackbarAsyncTask(View view,
|
||||
String snackbarText,
|
||||
String snackbarActionText,
|
||||
int snackbarActionColor,
|
||||
int snackbarDuration,
|
||||
boolean showProgress)
|
||||
{
|
||||
this.view = view;
|
||||
this.snackbarText = snackbarText;
|
||||
this.snackbarActionText = snackbarActionText;
|
||||
this.snackbarActionColor = snackbarActionColor;
|
||||
this.snackbarDuration = snackbarDuration;
|
||||
this.showProgress = showProgress;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
if (this.showProgress) this.progressDialog = ProgressDialog.show(view.getContext(), "", "", true);
|
||||
else this.progressDialog = null;
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
@Override
|
||||
protected final Void doInBackground(Params... params) {
|
||||
this.reversibleParameter = params != null && params.length > 0 ?params[0] : null;
|
||||
executeAction(reversibleParameter);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void result) {
|
||||
if (this.showProgress && this.progressDialog != null) {
|
||||
this.progressDialog.dismiss();
|
||||
this.progressDialog = null;
|
||||
}
|
||||
|
||||
Snackbar.make(view, snackbarText, snackbarDuration)
|
||||
.setAction(snackbarActionText, this)
|
||||
.setActionTextColor(snackbarActionColor)
|
||||
.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
if (showProgress) progressDialog = ProgressDialog.show(view.getContext(), "", "", true);
|
||||
else progressDialog = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
reverseAction(reversibleParameter);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void result) {
|
||||
if (showProgress && progressDialog != null) {
|
||||
progressDialog.dismiss();
|
||||
progressDialog = null;
|
||||
}
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
protected abstract void executeAction(@Nullable Params parameter);
|
||||
protected abstract void reverseAction(@Nullable Params parameter);
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user