@ -173,6 +173,16 @@
|
||||
|
||||
</activity-alias>
|
||||
|
||||
<activity android:name=".ConversationListArchiveActivity"
|
||||
android:label="@string/AndroidManifeset_conversations_archive"
|
||||
android:launchMode="singleTask"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
|
||||
android:parentActivityName=".ConversationListActivity">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value="org.thoughtcrime.securesms.ConversationListActivity" />
|
||||
</activity>
|
||||
|
||||
<activity android:name=".ConversationActivity"
|
||||
android:windowSoftInputMode="stateUnchanged"
|
||||
android:launchMode="singleTask"
|
||||
|
BIN
res/drawable-hdpi/ic_archive_white_24dp.png
Normal file
After Width: | Height: | Size: 247 B |
BIN
res/drawable-hdpi/ic_unarchive_white_24dp.png
Normal file
After Width: | Height: | Size: 258 B |
BIN
res/drawable-hdpi/ic_unarchive_white_36dp.png
Normal file
After Width: | Height: | Size: 354 B |
BIN
res/drawable-mdpi/ic_archive_white_24dp.png
Normal file
After Width: | Height: | Size: 181 B |
BIN
res/drawable-mdpi/ic_unarchive_white_24dp.png
Normal file
After Width: | Height: | Size: 181 B |
BIN
res/drawable-mdpi/ic_unarchive_white_36dp.png
Normal file
After Width: | Height: | Size: 258 B |
11
res/drawable-v21/conversation_list_item_read_background.xml
Normal file
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="@color/textsecure_primary">
|
||||
<item android:id="@android:id/mask" android:drawable="@android:color/black" />
|
||||
<item>
|
||||
<selector>
|
||||
<item android:drawable="@color/textsecure_primary_alpha33" android:state_selected="true" />
|
||||
<item android:drawable="@color/conversation_list_item_background_read_light" />
|
||||
</selector>
|
||||
</item>
|
||||
</ripple>
|
@ -5,6 +5,7 @@
|
||||
<item>
|
||||
<selector>
|
||||
<item android:drawable="@color/textsecure_primary_alpha33" android:state_selected="true" />
|
||||
<item android:drawable="@color/conversation_list_item_background_read_dark" />
|
||||
</selector>
|
||||
</item>
|
||||
</ripple>
|
BIN
res/drawable-xhdpi/ic_archive_white_24dp.png
Normal file
After Width: | Height: | Size: 267 B |
BIN
res/drawable-xhdpi/ic_unarchive_white_24dp.png
Normal file
After Width: | Height: | Size: 273 B |
BIN
res/drawable-xhdpi/ic_unarchive_white_36dp.png
Normal file
After Width: | Height: | Size: 391 B |
BIN
res/drawable-xxhdpi/ic_archive_white_24dp.png
Normal file
After Width: | Height: | Size: 390 B |
BIN
res/drawable-xxhdpi/ic_unarchive_white_24dp.png
Normal file
After Width: | Height: | Size: 391 B |
BIN
res/drawable-xxhdpi/ic_unarchive_white_36dp.png
Normal file
After Width: | Height: | Size: 600 B |
BIN
res/drawable-xxxhdpi/ic_archive_white_24dp.png
Normal file
After Width: | Height: | Size: 489 B |
BIN
res/drawable-xxxhdpi/ic_archive_white_36dp.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
res/drawable-xxxhdpi/ic_archive_white_48dp.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
res/drawable-xxxhdpi/ic_unarchive_white_24dp.png
Normal file
After Width: | Height: | Size: 503 B |
BIN
res/drawable-xxxhdpi/ic_unarchive_white_36dp.png
Normal file
After Width: | Height: | Size: 753 B |
6
res/drawable/conversation_list_item_read_background.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@color/textsecure_primary_alpha33" android:state_selected="true" />
|
||||
<item android:drawable="@color/textsecure_primary_alpha33" android:state_pressed="true" />
|
||||
<item android:drawable="@color/conversation_list_item_background_read_light" />
|
||||
</selector>
|
@ -2,4 +2,5 @@
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@color/textsecure_primary_alpha33" android:state_selected="true" />
|
||||
<item android:drawable="@color/textsecure_primary_alpha33" android:state_pressed="true" />
|
||||
<item android:drawable="@color/conversation_list_item_background_read_dark" />
|
||||
</selector>
|
7
res/drawable/rounded_rectangle.xml
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" >
|
||||
<solid android:color="@color/transparent"/>
|
||||
<stroke android:width="1dp" android:color="#ffbbbbbb"/>
|
||||
<corners android:radius="5dp"/>
|
||||
<padding android:bottom="5dp" android:left="5dp" android:right="5dp" android:top="5dp"/>
|
||||
</shape>
|
@ -8,7 +8,6 @@
|
||||
android:layout_height="?android:attr/listPreferredItemHeight"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:background="@drawable/conversation_list_item_background"
|
||||
android:paddingLeft="48dp"
|
||||
android:paddingRight="20dp">
|
||||
|
||||
@ -18,7 +17,7 @@
|
||||
android:layout_height="40dp"
|
||||
android:foreground="@drawable/contact_photo_background"
|
||||
android:cropToPadding="true"
|
||||
tools:src="@color/md_material_blue_600"
|
||||
tools:src="@color/blue_600"
|
||||
android:layout_marginRight="10dp"
|
||||
android:contentDescription="@string/SingleContactSelectionActivity_contact_photo" />
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<android.support.design.widget.CoordinatorLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:fab="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="fill_parent"
|
||||
@ -27,7 +28,7 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<com.melnykov.fab.FloatingActionButton
|
||||
<android.support.design.widget.FloatingActionButton
|
||||
android:id="@+id/fab"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
@ -35,9 +36,6 @@
|
||||
android:layout_margin="16dp"
|
||||
android:src="@drawable/ic_create_white_24dp"
|
||||
android:focusable="true"
|
||||
android:contentDescription="@string/conversation_list_fragment__fab_content_description"
|
||||
fab:fab_colorNormal="?fab_color"
|
||||
fab:fab_colorPressed="@color/textsecure_primary_dark"
|
||||
fab:fab_colorRipple="@color/textsecure_primary_dark" />
|
||||
android:contentDescription="@string/conversation_list_fragment__fab_content_description"/>
|
||||
|
||||
</FrameLayout>
|
||||
</android.support.design.widget.CoordinatorLayout>
|
||||
|
17
res/layout/conversation_list_item_action.xml
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<org.thoughtcrime.securesms.ConversationListItemAction
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="70dp">
|
||||
|
||||
<TextView android:id="@+id/description"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
tools:text="Archived conversations (2)"/>
|
||||
|
||||
</org.thoughtcrime.securesms.ConversationListItemAction>
|
@ -4,7 +4,6 @@
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:background="@drawable/conversation_list_item_background"
|
||||
android:layout_height="70dp">
|
||||
|
||||
<org.thoughtcrime.securesms.components.AvatarImageView
|
||||
@ -62,6 +61,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/from"
|
||||
android:layout_toRightOf="@id/error"
|
||||
android:layout_toLeftOf="@+id/archived"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textColor="?attr/conversation_list_item_subject_color"
|
||||
android:fontFamily="sans-serif-light"
|
||||
@ -98,5 +98,19 @@
|
||||
tools:text="30 mins"
|
||||
android:singleLine="true"/>
|
||||
|
||||
<TextView android:id="@+id/archived"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/date"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignBaseline="@id/subject"
|
||||
android:layout_marginLeft="5dp"
|
||||
android:text="@string/conversation_list_item_view__archived"
|
||||
android:textColor="#ffbbbbbb"
|
||||
android:background="@drawable/rounded_rectangle"
|
||||
android:textSize="12sp"
|
||||
/>
|
||||
|
||||
|
||||
</RelativeLayout>
|
||||
</org.thoughtcrime.securesms.ConversationListItem>
|
||||
|
@ -7,9 +7,6 @@
|
||||
<item android:title="@string/conversation__menu_view_media"
|
||||
android:id="@+id/menu_view_media" />
|
||||
|
||||
<item android:title="@string/conversation__menu_delete_thread"
|
||||
android:id="@+id/menu_delete_thread" />
|
||||
|
||||
<item android:title="@string/conversation__menu_conversation_settings"
|
||||
android:id="@+id/menu_conversation_settings"/>
|
||||
|
||||
|
@ -5,10 +5,11 @@
|
||||
<item android:title="@string/conversation_list_batch__menu_delete_selected"
|
||||
android:id="@+id/menu_delete_selected"
|
||||
android:icon="?menu_trash_icon"
|
||||
app:showAsAction="ifRoom" />
|
||||
app:showAsAction="always" />
|
||||
|
||||
<item android:title="@string/conversation_list_batch__menu_select_all"
|
||||
android:id="@+id/menu_select_all"
|
||||
android:icon="?menu_selectall_icon" />
|
||||
android:icon="?menu_selectall_icon"
|
||||
app:showAsAction="always"/>
|
||||
|
||||
</menu>
|
10
res/menu/conversation_list_batch_archive.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item android:title="@string/conversation_list_batch__archive_selected"
|
||||
android:id="@+id/menu_archive_selected"
|
||||
android:icon="@drawable/ic_archive_white_24dp"
|
||||
app:showAsAction="always"/>
|
||||
|
||||
</menu>
|
10
res/menu/conversation_list_batch_unarchive.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item android:title="@string/conversation_list_batch__archive_selected"
|
||||
android:id="@+id/menu_archive_selected"
|
||||
android:icon="@drawable/ic_unarchive_white_24dp"
|
||||
app:showAsAction="always"/>
|
||||
|
||||
</menu>
|
@ -24,7 +24,7 @@
|
||||
|
||||
<color name="gray95_transparent50">#7F111111</color>
|
||||
|
||||
<color name="conversation_list_item_background_read_light">#ffffffff</color>
|
||||
<color name="conversation_list_item_background_read_light">@color/gray5</color>
|
||||
<color name="conversation_list_item_background_unread_light">#ffffffff</color>
|
||||
<color name="conversation_list_item_background_read_dark">#ff000000</color>
|
||||
<color name="conversation_list_item_background_unread_dark">#ff333333</color>
|
||||
|
@ -37,6 +37,7 @@
|
||||
|
||||
<dimen name="transfer_controls_expanded_width">150dp</dimen>
|
||||
<dimen name="transfer_controls_contracted_width">70dp</dimen>
|
||||
<dimen name="conversation_list_fragment_archive_padding">16dp</dimen>
|
||||
|
||||
<!-- RedPhone -->
|
||||
<dimen name="incoming_widget_outer_radius">135dip</dimen>
|
||||
|
@ -174,10 +174,18 @@
|
||||
</plurals>
|
||||
<string name="ConversationListFragment_deleting">Deleting</string>
|
||||
<string name="ConversationListFragment_deleting_selected_threads">Deleting selected threads...</string>
|
||||
<string name="ConversationListFragment_archived_conversations">Archived conversations</string>
|
||||
<string name="ConversationListFragment_undo">UNDO</string>
|
||||
<string name="ConversationListFragment_moved_conversation_to_inbox">Moved conversation to inbox</string>
|
||||
<string name="ConversationListFragment_archived_conversation">Archived conversation</string>
|
||||
<string name="ConversationListFragment_moved_conversations_to_inbox">Moved conversations to inbox</string>
|
||||
|
||||
<!-- ConversationListItem -->
|
||||
<string name="ConversationListItem_key_exchange_message">Key exchange message...</string>
|
||||
|
||||
<!-- ConversationListItemAction -->
|
||||
<string name="ConversationListItemAction_archived_conversations_d">Archived conversations (%d)</string>
|
||||
|
||||
<!-- CustomDefaultPreference -->
|
||||
<string name="CustomDefaultPreference_using_custom">Using custom: %s</string>
|
||||
<string name="CustomDefaultPreference_using_default">Using default: %s</string>
|
||||
@ -867,6 +875,7 @@
|
||||
<string name="AndroidManifest__message_details">Message details</string>
|
||||
<string name="AndroidManifest_manage_linked_devices">Manage linked devices</string>
|
||||
<string name="AndroidManifest__invite_friends">Invite friends</string>
|
||||
<string name="AndroidManifeset_conversations_archive">Conversations archive</string>
|
||||
|
||||
<!-- arrays.xml -->
|
||||
<string name="arrays__import_export">Import / export</string>
|
||||
@ -1048,6 +1057,7 @@
|
||||
<!-- conversation_list_batch -->
|
||||
<string name="conversation_list_batch__menu_delete_selected">Delete selected</string>
|
||||
<string name="conversation_list_batch__menu_select_all">Select all</string>
|
||||
<string name="conversation_list_batch__archive_selected">Archive selected</string>
|
||||
|
||||
<!-- conversation_list -->
|
||||
<string name="conversation_list__menu_search">Search</string>
|
||||
@ -1055,6 +1065,7 @@
|
||||
<!-- conversation_list_item_view -->
|
||||
<string name="conversation_list_item_view__contact_photo_image">Contact Photo Image</string>
|
||||
<string name="conversation_list_item_view__error_alert">Error alert</string>
|
||||
<string name="conversation_list_item_view__archived">Archived</string>
|
||||
|
||||
<!-- conversation_list_fragment -->
|
||||
<string name="conversation_list_fragment__fab_content_description">New conversation</string>
|
||||
@ -1151,6 +1162,7 @@
|
||||
<!-- transport_selection_list_item -->
|
||||
<string name="transport_selection_list_item__transport_icon">Transport icon</string>
|
||||
|
||||
|
||||
<!-- EOF -->
|
||||
|
||||
</resources>
|
||||
|
@ -95,7 +95,7 @@
|
||||
<item name="attachment_type_selector_background">@color/white</item>
|
||||
<item name="conversation_list_item_background_selected">@drawable/list_selected_holo_light</item>
|
||||
<item name="conversation_list_item_background_unread">@drawable/conversation_list_item_unread_background</item>
|
||||
<item name="conversation_list_item_background_read">@drawable/conversation_list_item_background</item>
|
||||
<item name="conversation_list_item_background_read">@drawable/conversation_list_item_read_background</item>
|
||||
<item name="conversation_list_item_count_color">#66333333</item>
|
||||
<item name="conversation_list_item_contact_color">#FF333333</item>
|
||||
<item name="conversation_list_item_subject_color">#FF444444</item>
|
||||
@ -205,12 +205,13 @@
|
||||
<item name="actionBarPopupTheme">@style/ThemeOverlay.AppCompat.Dark</item>
|
||||
<item name="android:textColor">@color/text_color_dark_theme</item>
|
||||
<item name="android:textColorSecondary">@color/text_color_secondary_dark_theme</item>
|
||||
<item name="colorAccent">@color/textsecure_primary_dark</item>
|
||||
<item name="colorControlActivated">@color/signal_primary_dark</item>
|
||||
<item name="colorControlHighlight">@color/signal_primary_dark</item>
|
||||
<item name="android:windowBackground">@color/black</item>
|
||||
<item name="conversation_list_item_background_selected">@drawable/list_selected_holo_dark</item>
|
||||
<item name="conversation_list_item_background_unread">@drawable/conversation_list_item_unread_background_dark</item>
|
||||
<item name="conversation_list_item_background_read">@drawable/conversation_list_item_background</item>
|
||||
<item name="conversation_list_item_background_read">@drawable/conversation_list_item_read_background_dark</item>
|
||||
<item name="conversation_list_item_count_color">#66dddddd</item>
|
||||
<item name="conversation_list_item_contact_color">#ffdddddd</item>
|
||||
<item name="conversation_list_item_subject_color">#ffdddddd</item>
|
||||
|
@ -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);
|
||||
|
||||
}
|