Compare commits

...

51 Commits

Author SHA1 Message Date
B. Petersen a2d6a00cd1 Update messenger-backend submodule. 2018-10-29 23:40:38 +01:00
Angelo Fuchs ffe69f7f4b Using DcEventDelegate instead of Object. 2018-10-29 23:01:57 +02:00
björn petersen 887cc3a1bb [WIP] Media gallery (#109) closes #78
* Update messenger-backend submodule.
* display a swipe able media gallery when one taps an image
* display a tiled gallery from chat-menu and from preview gallery
* changed some utility classes to handle the new situations
2018-10-29 20:55:49 +01:00
daniel.boehrs c192b719f5 show avatars for groups #55 2018-10-29 15:44:55 +01:00
daniel.boehrs ed0de74406 Adjust keyboard show / hide #92 2018-10-29 10:29:43 +01:00
B. Petersen ff1878511d show contact-avatar in deaddrop, tackles #95 2018-10-23 23:16:03 +02:00
B. Petersen a77cbee5f2 remove unused semi-transparency 2018-10-23 23:07:01 +02:00
B. Petersen e92fda0ca3 use plus-icon instead of pencil in chatlist, closes #107 2018-10-23 22:53:31 +02:00
B. Petersen a4c9e1699b improve 'no messages' hint on empty chats. 2018-10-23 21:51:14 +02:00
B. Petersen df87b64f66 upper-right align date in chatlist 2018-10-23 16:36:44 +02:00
B. Petersen b79e8e812d make status icons mutual exclusive 2018-10-23 16:24:19 +02:00
B. Petersen ec2deebda4 add space between columns in chatlist, closes #106 2018-10-23 16:09:09 +02:00
B. Petersen befbf84c59 remove unused thumbnail from chatlist 2018-10-23 14:30:08 +02:00
B. Petersen 7b205db200 do not show the date in bold on unread, too noisy. only show subject in bold and green unread counter. 2018-10-23 13:57:58 +02:00
B. Petersen e43c85b12b unify ripple color 2018-10-23 13:52:29 +02:00
B. Petersen 758c175b3d add horizontal border between chats in chatlist, tackles #106 2018-10-23 13:11:21 +02:00
B. Petersen fdceb0e080 draw verified-icon directly right of chat-title, closes #105 2018-10-23 12:00:56 +02:00
B. Petersen 3863ef91e1 comment 2018-10-23 09:51:35 +02:00
B. Petersen 6e07258fc7 make deaddrop-background compatible to selection-background 2018-10-23 07:04:15 +02:00
B. Petersen c07a0dedd6 bump version 2018-10-22 23:52:25 +02:00
B. Petersen e5b906184d do not draw buggy deaddrop background for now 2018-10-22 23:50:09 +02:00
B. Petersen 2dbd12403f implement archive/unarchive/delete/leave from chat-activity 2018-10-22 23:14:15 +02:00
daniel.boehrs 9906608956 fix group create crash #103 2018-10-22 19:08:35 +02:00
B. Petersen 5870f0cd02 implement group leaving 2018-10-22 18:09:58 +02:00
B. Petersen 3dc6ea1c85 cleanup chat-menu 2018-10-22 17:47:18 +02:00
B. Petersen e66ae137fe make 'Edit group' a first-class action, tackles #92 2018-10-22 17:06:00 +02:00
B. Petersen 4d32f5746c no special click-handler for ConversationUpdateItem 2018-10-22 16:32:26 +02:00
daniel.boehrs 6e5c6e3d4d Merge branch 'master' of https://github.com/deltachat/deltachat-android-ii 2018-10-22 16:23:54 +02:00
daniel.boehrs c459aa4476 set group image #92 2018-10-22 16:23:42 +02:00
B. Petersen bf4bcba78c remove RecipientModifiedListener from ConversationActivity and ConversationListActivity 2018-10-22 16:21:59 +02:00
B. Petersen ccb5e3285f Update messenger-backend submodule. 2018-10-22 16:12:34 +02:00
daniel.boehrs 5424d56801 edit groups #92 2018-10-22 15:20:24 +02:00
B. Petersen 5c15784f66 hide unused controls in contact-requests, tackles #95 2018-10-22 01:21:15 +02:00
B. Petersen 0abecd74c9 remove unused code 2018-10-22 00:56:42 +02:00
B. Petersen aa3df42f71 ask 'Chat with NAME?' on clicks on a contact request and offer several options, tackles #95. 2018-10-22 00:29:45 +02:00
B. Petersen cfe6e023cb handle msg_id and contact_id in chatlist-items 2018-10-21 23:57:00 +02:00
B. Petersen 6140b2aab4 add a function to get the msg_id from a chatlist-index 2018-10-21 23:55:09 +02:00
B. Petersen cc95e1fa1f show contact request in chatlist with different background 2018-10-21 23:04:58 +02:00
B. Petersen 643e8d80f7 typo 2018-10-21 15:02:12 +02:00
B. Petersen 84cc1c124a show correct title for contact requests 2018-10-21 15:01:33 +02:00
B. Petersen 22d8f3177f remove unused parameters on opening/creating chats 2018-10-21 14:43:44 +02:00
B. Petersen 471f4480ac remove now unused ConversationActivity.DISTRIBUTION_TYPE_EXTRA 2018-10-21 14:36:29 +02:00
B. Petersen bc387096f5 remove dependency from this.distributionType on draft saving 2018-10-21 14:30:41 +02:00
B. Petersen 93b5a345ea remove unused group-options 2018-10-21 14:22:59 +02:00
B. Petersen 50b18b7d65 remove now redundant ConversationActivity.ADDRESS_EXTRA 2018-10-21 14:15:55 +02:00
B. Petersen 004319db64 remove dependency to ConversationActivity.ADDRESS_EXTRA from ConversationFragment 2018-10-21 14:09:33 +02:00
B. Petersen 90b52cebf1 remove TIMING_EXTRA 2018-10-21 13:58:58 +02:00
B. Petersen f4a01a2bae re-add accidentially removed advanced-preference click-handler 2018-10-21 01:28:56 +02:00
B. Petersen 77ed15125d move invite/help to settings 2018-10-21 00:59:11 +02:00
B. Petersen 15ef0271fc add contact-request (aka deaddrop) menu entry, tackles #95 2018-10-21 00:02:19 +02:00
B. Petersen 1dd2ad04ba a click on a deaddrop-message opens a new chat, tackles #95 2018-10-20 23:52:54 +02:00
84 changed files with 901 additions and 1210 deletions
-1
View File
@@ -192,7 +192,6 @@
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize" />
<activity android:name=".GroupCreateActivity"
android:windowSoftInputMode="stateVisible"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".DatabaseUpgradeActivity"
+2 -2
View File
@@ -255,8 +255,8 @@ android {
}
defaultConfig {
versionCode 396
versionName "0.0.6"
versionCode 398
versionName "0.0.8"
applicationId "chat.delta.androidii"
minSdkVersion 14
+6
View File
@@ -726,6 +726,12 @@ JNIEXPORT jlong Java_com_b44t_messenger_DcChatlist_getChatCPtr(JNIEnv *env, jobj
}
JNIEXPORT jint Java_com_b44t_messenger_DcChatlist_getMsgId(JNIEnv *env, jobject obj, jint index)
{
return dc_chatlist_get_msg_id(get_dc_chatlist(env, obj), index);
}
JNIEXPORT jlong Java_com_b44t_messenger_DcChatlist_getMsgCPtr(JNIEnv *env, jobject obj, jint index)
{
dc_chatlist_t* chatlist = get_dc_chatlist(env, obj);
Binary file not shown.

Before

Width:  |  Height:  |  Size: 129 B

After

Width:  |  Height:  |  Size: 344 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 727 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 B

After

Width:  |  Height:  |  Size: 351 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 508 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 B

After

Width:  |  Height:  |  Size: 368 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 822 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 B

After

Width:  |  Height:  |  Size: 387 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

+4
View File
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@drawable/ic_help_white_24dp"
android:tint="?attr/pref_icon_tint"/>
+4
View File
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@drawable/ic_mood_white_24dp"
android:tint="?attr/pref_icon_tint"/>
-7
View File
@@ -69,13 +69,6 @@
android:text="@string/conversation_activity__enable_signal_messages"
android:visibility="gone"/>
<Button android:id="@+id/unblock_button"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="20dp"
android:text="@string/ConversationActivity_unblock"
android:visibility="gone"/>
<Button android:id="@+id/make_default_sms_button"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
+1 -1
View File
@@ -73,7 +73,7 @@
android:layout_gravity="bottom|end"
android:layout_margin="16dp"
app:backgroundTint="@color/signal_primary"
android:src="@drawable/ic_create_white_24dp"
android:src="@drawable/ic_add_white_24dp"
android:focusable="true"
android:contentDescription="@string/conversation_list_fragment__fab_content_description"/>
+95 -124
View File
@@ -8,9 +8,7 @@
android:focusable="true"
android:nextFocusRight="@+id/fab"
android:nextFocusLeft="@+id/container"
android:layout_height="72dp"
android:paddingLeft="16dp"
android:paddingRight="16dp">
android:layout_height="72dp">
<org.thoughtcrime.securesms.components.AvatarImageView
android:id="@+id/contact_photo_image"
@@ -23,150 +21,123 @@
android:cropToPadding="true"
tools:src="@drawable/ic_contact_picture"
android:contentDescription="@string/conversation_list_item_view__contact_photo_image"
android:layout_marginRight="12dp"
android:layout_marginEnd="12dp" />
android:layout_marginLeft="12dp"
android:layout_marginRight="12dp" />
<RelativeLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:layout_marginStart="4dp"
android:layout_centerVertical="true"
android:layout_toRightOf="@id/contact_photo_image"
android:layout_toEndOf="@id/contact_photo_image"
android:weightSum="1"
android:orientation="horizontal">
<LinearLayout
android:orientation="horizontal"
android:gravity="center_vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_toRightOf="@id/contact_photo_image"
android:layout_toEndOf="@id/contact_photo_image">
<RelativeLayout
android:id="@+id/from"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_toLeftOf="@+id/thumbnail"
android:layout_toStartOf="@+id/thumbnail">
<LinearLayout
android:orientation="vertical"
android:layout_weight="2"
android:layout_width="0dp"
android:layout_height="wrap_content">
<org.thoughtcrime.securesms.components.FromTextView
android:id="@+id/from_text"
style="@style/Signal.Text.Body"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
<LinearLayout
android:id="@+id/from"
android:orientation="horizontal"
android:layout_marginBottom="2dp"
android:layout_toLeftOf="@id/verified_indicator"
android:layout_toStartOf="@id/verified_indicator"
android:drawablePadding="5dp"
android:ellipsize="end"
android:fontFamily="sans-serif-medium"
android:maxLines="1"
android:textColor="?attr/conversation_list_item_contact_color"
tools:text="Jules Bonnot" />
<ImageView
android:id="@+id/verified_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/ic_verified"
android:visibility="gone"
android:layout_marginLeft="5dp"
android:layout_marginStart="5dp"
android:layout_marginTop="4dp"
android:layout_marginRight="5dp"
android:layout_marginEnd="5dp"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true" />
android:layout_height="wrap_content">
</RelativeLayout>
<org.thoughtcrime.securesms.components.FromTextView
android:id="@+id/from_text"
style="@style/Signal.Text.Body"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawablePadding="5dp"
android:ellipsize="end"
android:fontFamily="sans-serif-medium"
android:maxLines="1"
android:textColor="?attr/conversation_list_item_contact_color"
tools:text="Jules Bonnot" />
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
<ImageView
android:id="@+id/verified_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/ic_verified"
android:visibility="visible"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp" />
</LinearLayout>
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
android:id="@+id/subject"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/from"
android:layout_toLeftOf="@+id/status"
android:layout_toStartOf="@+id/status"
android:paddingRight="1dp"
style="@style/Signal.Text.Preview"
android:textColor="?attr/conversation_list_item_subject_color"
android:maxLines="1"
tools:text="Wheels arrive at 3pm flat. This is a somewhat longer message."
android:ellipsize="end" />
<org.thoughtcrime.securesms.components.ThumbnailView
android:id="@+id/thumbnail"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_gravity="center_horizontal"
android:layout_marginRight="5dp"
android:layout_marginLeft="5dp"
android:layout_toLeftOf="@+id/date"
android:layout_toStartOf="@+id/date"
android:layout_alignParentTop="true"
android:layout_marginTop="5dp"
android:contentDescription="@string/conversation_activity__attachment_thumbnail"
android:visibility="gone"
tools:src="@drawable/ic_video_light"
tools:visibility="visible" />
</LinearLayout>
<TextView android:id="@id/date"
android:layout_marginLeft="3dp"
android:layout_marginStart="3dp"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_alignBaseline="@+id/from"
android:layout_above="@id/subject"
style="@style/Signal.Text.Caption"
android:textColor="?attr/conversation_list_item_date_color"
android:textAllCaps="true"
tools:text="30 mins"
android:singleLine="true"/>
<LinearLayout
android:orientation="vertical"
android:gravity="end"
android:layout_marginLeft="24dp"
android:layout_marginStart="24dp"
android:layout_marginRight="12dp"
android:layout_marginEnd="12dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<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_alignParentEnd="true"
android:layout_alignBaseline="@id/subject"
android:layout_marginLeft="5dp"
android:layout_marginStart="5dp"
android:paddingLeft="6dp"
android:paddingRight="6dp"
android:paddingTop="2dp"
android:paddingBottom="2dp"
style="@style/Signal.Text.Caption"
android:text="@string/conversation_list_item_view__archived"
android:textColor="@color/core_white"
android:textAllCaps="true"
android:background="@drawable/archived_indicator_background" />
<TextView android:id="@id/date"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
style="@style/Signal.Text.Caption"
android:textColor="?attr/conversation_list_item_date_color"
android:textAllCaps="true"
tools:text="30 mins"
android:singleLine="true"/>
<LinearLayout
android:orientation="horizontal"
android:minHeight="20dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<FrameLayout android:id="@+id/status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:layout_below="@id/date"
android:layout_toLeftOf="@id/archived"
android:layout_toStartOf="@id/archived"
android:layout_alignWithParentIfMissing="true">
<TextView android:id="@+id/archived"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="6dp"
android:paddingRight="6dp"
android:paddingTop="2dp"
android:paddingBottom="2dp"
style="@style/Signal.Text.Caption"
android:text="@string/conversation_list_item_view__archived"
android:textColor="@color/core_white"
android:textAllCaps="true"
android:background="@drawable/archived_indicator_background" />
<org.thoughtcrime.securesms.components.DeliveryStatusView
android:id="@+id/delivery_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
app:iconColor="?attr/conversation_list_item_delivery_icon_color"/>
<org.thoughtcrime.securesms.components.DeliveryStatusView
android:id="@+id/delivery_status"
android:layout_marginTop="6dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:iconColor="?attr/conversation_list_item_delivery_icon_color"/>
<ImageView android:id="@+id/unread_indicator"
android:layout_width="24dp"
android:layout_height="24dp"/>
<ImageView android:id="@+id/unread_indicator"
android:layout_width="24dp"
android:layout_height="24dp"/>
</LinearLayout>
</FrameLayout>
</LinearLayout>
</RelativeLayout>
</LinearLayout>
<View android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_alignParentBottom="true"
android:background="@drawable/attachment_selector_shadow"/>
</org.thoughtcrime.securesms.ConversationListItem>
+28 -30
View File
@@ -39,7 +39,8 @@
tools:src="@drawable/ic_contact_picture"
android:contentDescription="@string/conversation_list_item_view__contact_photo_image"/>
<RelativeLayout
<LinearLayout
android:orientation="vertical"
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -47,35 +48,33 @@
android:layout_toEndOf="@id/contact_photo_image"
android:layout_toRightOf="@id/contact_photo_image">
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
android:id="@+id/title"
style="@style/TextSecure.TitleTextStyle"
<LinearLayout
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_toLeftOf="@id/verified_indicator"
android:layout_toStartOf="@id/verified_indicator"
android:drawablePadding="5dp"
android:ellipsize="end"
android:maxLines="1"
android:textSize="18dp"
android:transitionName="recipient_name"
tools:text="Jules Bonnot" />
android:layout_height="wrap_content">
<ImageView
android:id="@+id/verified_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_marginStart="5dp"
android:layout_marginTop="5dp"
android:layout_marginRight="5dp"
android:layout_marginEnd="5dp"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:src="@drawable/ic_verified"
android:visibility="gone" />
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
android:id="@+id/title"
style="@style/TextSecure.TitleTextStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawablePadding="5dp"
android:ellipsize="end"
android:maxLines="1"
android:textSize="18dp"
android:transitionName="recipient_name"
tools:text="Jules Bonnot" />
<ImageView
android:id="@+id/verified_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:src="@drawable/ic_verified"
android:visibility="gone" />
</LinearLayout>
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
android:id="@+id/subtitle"
@@ -86,9 +85,8 @@
android:maxLines="1"
android:textDirection="ltr"
android:textSize="13dp"
android:layout_below="@id/title"
tools:text="(123) 123-1234" />
</RelativeLayout>
</LinearLayout>
</org.thoughtcrime.securesms.ConversationTitleView>
+3 -3
View File
@@ -1,13 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:title="@string/conversation__menu_view_profile"
android:id="@+id/menu_conversation_settings"/>
<item android:title="@string/conversation__menu_add_attachment"
android:id="@+id/menu_add_attachment" />
<item android:title="@string/conversation__menu_view_all_media"
android:id="@+id/menu_view_media" />
<item android:title="@string/conversation__menu_conversation_settings"
android:id="@+id/menu_conversation_settings"/>
</menu>
-10
View File
@@ -1,10 +0,0 @@
<?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:id="@+id/menu_add_to_contacts"
android:title="@string/conversation_add_to_contacts__menu_add_to_contacts"
app:showAsAction="never" />
</menu>
+7
View File
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:title="@string/conversation__menu_archive_chat"
android:id="@+id/menu_archive_chat"/>
</menu>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:title="@string/conversation_insecure__invite"
android:id="@+id/menu_invite"/>
<item android:title="@string/conversation__menu_delete_chat"
android:id="@+id/menu_delete_chat"/>
</menu>
-9
View File
@@ -1,9 +0,0 @@
<?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_expiring_off__disappearing_messages"
android:id="@+id/menu_expiring_messages_off"
android:icon="@drawable/ic_timer_off_white_24dp"/>
</menu>
-10
View File
@@ -1,10 +0,0 @@
<?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:id="@+id/menu_expiring_messages"
app:actionLayout="@layout/expiration_timer_menu"
app:showAsAction="always"
android:title="@string/menu_conversation_expiring_on__messages_expiring"/>
</menu>
-9
View File
@@ -1,9 +0,0 @@
<?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/convesation_group_options__recipients_list"
android:id="@+id/menu_group_recipients"
android:icon="?menu_group_icon"
app:showAsAction="ifRoom" />
</menu>
@@ -1,17 +0,0 @@
<?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_group_options__delivery"
android:id="@+id/menu_group_delivery"
android:icon="?menu_split_icon"
app:showAsAction="ifRoom" >
<menu>
<group android:id="@+id/distribution_group"
android:checkableBehavior="single">
<item android:id="@+id/menu_distribution_conversation" android:title="@string/conversation_group_options__conversation" android:checked="true" />
<item android:id="@+id/menu_distribution_broadcast" android:title="@string/conversation_group_options__broadcast" />
</group>
</menu>
</item>
</menu>
+2 -1
View File
@@ -3,7 +3,8 @@
<item android:id="@+id/menu_edit_group"
android:title="@string/conversation__menu_edit_group"
app:showAsAction="collapseActionView" />
android:icon="?menu_group_icon"
app:showAsAction="ifRoom" />
<item android:id="@+id/menu_leave"
android:title="@string/conversation__menu_leave_group"
-5
View File
@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:title="@string/conversation_secure_verified__menu_reset_secure_session"
android:id="@+id/menu_reset_secure_session"/>
</menu>
+7
View File
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:title="@string/conversation__menu_unarchive_chat"
android:id="@+id/menu_archive_chat"/>
</menu>
+3 -6
View File
@@ -13,16 +13,13 @@
<item android:title="@string/text_secure_normal__menu_new_chat"
android:id="@+id/menu_new_chat" />
<item android:title="@string/menu_deaddrop"
android:id="@+id/menu_deaddrop" />
<item android:title="@string/text_secure_normal__menu_clear_passphrase"
android:id="@+id/menu_clear_passphrase" />
<item android:title="@string/text_secure_normal__invite_friends"
android:id="@+id/menu_invite" />
<item android:title="@string/text_secure_normal__menu_settings"
android:id="@+id/menu_settings" />
<item android:title="@string/text_secure_normal__help"
android:id="@+id/menu_help"/>
</menu>
+1
View File
@@ -7,6 +7,7 @@
<attr name="attachment_document_icon_large" format="reference" />
<attr name="conversation_list_item_background" format="reference"/>
<attr name="conversation_list_deaddrop_bg_color" format="reference|color"/>
<attr name="conversation_list_item_contact_color" format="reference|color"/>
<attr name="conversation_list_item_subject_color" format="reference|color"/>
<attr name="conversation_list_item_delivery_icon_color" format="reference|color"/>
+12 -3
View File
@@ -172,6 +172,7 @@
<string name="ConversationActivity_quoted_contact_message">%1$s %2$s</string>
<string name="ConversationActivity_NoMessagesHint">Send message to %1$s:\n\n• It is okay if %2$s does not use Delta Chat.\n\n• Delivering the first message may take a while.</string>
<string name="ConversationActivity_MsgNewGroupDraftHint">Compose the first message, allowing others to reply within this group.\n\n• It is okay if not all members use Delta Chat.\n\n• Delivering the first message may take a while.</string>
<string name="ConversationActivity_ask_delete_chat">Delete this chat? It will no longer be shown in the chat list, its messages will remain on the server.</string>
<!-- ConversationAdapter -->
<plurals name="ConversationAdapter_n_unread_messages">
@@ -232,9 +233,9 @@
<item quantity="one">Delete selected chats?</item>
<item quantity="other">Delete selected chats?</item>
</plurals>
<plurals name="ConversationListFragment_this_will_permanently_delete_all_n_selected_conversations">
<item quantity="one">This will permanently delete the selected chats.</item>
<item quantity="other">This will permanently delete all %1$d selected chats.</item>
<plurals name="ConversationListFragment_ask_selected_chats">
<item quantity="one">Delete the selected chat? It will no longer be shown in the chat list, its messages will remain on the server.</item>
<item quantity="other">Delete %1%d selected chats? They will no longer be shown in the chat list, their messages will remain on the server.</item>
</plurals>
<string name="ConversationListFragment_deleting">Deleting</string>
<string name="ConversationListFragment_deleting_selected_conversations">Deleting selected chats...</string>
@@ -1524,5 +1525,13 @@
<string name="copy_to_clipboard">Copy to clipboard</string>
<string name="open">Open</string>
<string name="done">Done</string>
<string name="menu_deaddrop">Contact requests</string>
<string name="not_now">Not now</string>
<string name="never">Never</string>
<string name="conversation__menu_view_profile">View profile</string>
<string name="conversation__menu_archive_chat">Archive chat</string>
<string name="conversation__menu_unarchive_chat">Unarchive chat</string>
<string name="conversation__menu_delete_chat">Delete chat</string>
<string name="conversation__no_messages">No messages.</string>
</resources>
+2
View File
@@ -132,6 +132,7 @@
<item name="attachment_document_icon_large">@drawable/ic_document_large_light</item>
<item name="conversation_list_item_background">@drawable/conversation_list_item_background</item>
<item name="conversation_list_deaddrop_bg_color">#ffcccccc</item>
<item name="conversation_list_item_contact_color">@color/core_light_90</item>
<item name="conversation_list_item_subject_color">@color/core_light_60</item>
<item name="conversation_list_item_delivery_icon_color">@color/core_light_35</item>
@@ -275,6 +276,7 @@
<item name="attachment_document_icon_large">@drawable/ic_document_large_dark</item>
<item name="conversation_list_item_background">@drawable/conversation_list_item_background_dark</item>
<item name="conversation_list_deaddrop_bg_color">#ff333333</item>
<item name="conversation_list_item_contact_color">#ffdddddd</item>
<item name="conversation_list_item_subject_color">#ffdddddd</item>
<item name="conversation_list_item_delivery_icon_color">@color/core_dark_55</item>
+8
View File
@@ -25,4 +25,12 @@
android:title="@string/preferences__advanced"
android:icon="@drawable/ic_advanced_24dp"/>
<Preference android:key="preference_category_invite"
android:title="@string/text_secure_normal__invite_friends"
android:icon="@drawable/ic_invite_24dp"/>
<Preference android:key="preference_category_help"
android:title="@string/text_secure_normal__help"
android:icon="@drawable/ic_help_24dp"/>
</PreferenceScreen>
+1
View File
@@ -40,6 +40,7 @@ public class DcChatlist {
public native int getCnt ();
public native int getChatId (int index);
public @NonNull DcChat getChat (int index) { return new DcChat(getChatCPtr(index)); }
public native int getMsgId (int index);
public @NonNull DcMsg getMsg (int index) { return new DcMsg(getMsgCPtr(index)); }
public @NonNull DcLot getSummary(int index, DcChat chat) { return new DcLot(getSummaryCPtr(index, chat==null? null : chat.getChatCPtr())); }
+9 -9
View File
@@ -28,16 +28,16 @@ import java.util.Enumeration;
import java.util.Hashtable;
public class DcEventCenter {
private Hashtable<Integer, ArrayList<Object>> allObservers = new Hashtable<>();
private Hashtable<Integer, ArrayList<DcEventDelegate>> allObservers = new Hashtable<>();
private final Object LOCK = new Object();
public interface DcEventDelegate {
void handleEvent(int eventId, Object data1, Object data2);
}
public void addObserver(Object observer, int eventId) {
public void addObserver(DcEventDelegate observer, int eventId) {
synchronized (LOCK) {
ArrayList<Object> idObservers = allObservers.get(eventId);
ArrayList<DcEventDelegate> idObservers = allObservers.get(eventId);
if (idObservers == null) {
allObservers.put(eventId, (idObservers = new ArrayList<>()));
}
@@ -45,9 +45,9 @@ public class DcEventCenter {
}
}
public void removeObserver(Object observer, int eventId) {
public void removeObserver(DcEventDelegate observer, int eventId) {
synchronized (LOCK) {
ArrayList<Object> idObservers = allObservers.get(eventId);
ArrayList<DcEventDelegate> idObservers = allObservers.get(eventId);
if (idObservers != null) {
idObservers.remove(observer);
}
@@ -59,7 +59,7 @@ public class DcEventCenter {
Enumeration<Integer> enumKey = allObservers.keys();
while(enumKey.hasMoreElements()) {
Integer eventId = enumKey.nextElement();
ArrayList<Object> idObservers = allObservers.get(eventId);
ArrayList<DcEventDelegate> idObservers = allObservers.get(eventId);
if (idObservers != null) {
idObservers.remove(observer);
}
@@ -69,11 +69,11 @@ public class DcEventCenter {
public void sendToObservers(int eventId, Object data1, Object data2) {
synchronized (LOCK) {
ArrayList<Object> idObservers = allObservers.get(eventId);
ArrayList<DcEventDelegate> idObservers = allObservers.get(eventId);
if (idObservers != null) {
for (int i = 0; i < idObservers.size(); i++) {
Object observer = idObservers.get(i);
((DcEventDelegate) observer).handleEvent(eventId, data1, data2);
DcEventDelegate observer = idObservers.get(i);
observer.handleEvent(eventId, data1, data2);
}
}
}
@@ -0,0 +1,44 @@
package com.b44t.messenger;
/**
* Contains a list of media entries, their respective positions and ability to move through it.
*/
public class DcMediaGalleryElement {
final int[] mediaMsgs;
int position;
final DcContext context;
public DcMediaGalleryElement(int[] mediaMsgs, int position, DcContext context, boolean leftIsRecent) {
this.mediaMsgs = mediaMsgs;
this.position = position;
this.context = context;
// normal state is left is recent. If the ui needs right to be recent we reverse the order here.
if (!leftIsRecent) {
for (int ii = 0; ii < mediaMsgs.length / 2; ii++) {
int tmp = mediaMsgs[ii];
int opposite = mediaMsgs.length - 1 - ii;
mediaMsgs[ii] = mediaMsgs[opposite];
mediaMsgs[opposite] = tmp;
}
}
}
public int getCount() {
return mediaMsgs.length;
}
public int getPosition() {
return position;
}
public void moveToPosition(int newPosition) {
if(newPosition < 0 || newPosition >= mediaMsgs.length)
throw new IllegalArgumentException("can't move outside of known area.");
position = newPosition;
}
public DcMsg getMessage() {
return context.getMsg(mediaMsgs[position]);
}
}
+8 -2
View File
@@ -31,8 +31,7 @@ import org.thoughtcrime.securesms.database.model.Quote;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientProvider;
import java.util.Collections;
import java.util.List;
import java.io.File;
import java.util.Set;
public class DcMsg {
@@ -54,6 +53,7 @@ public class DcMsg {
public final static int DC_STATE_OUT_DELIVERED = 26;
public final static int DC_STATE_OUT_MDN_RCVD = 28;
public static final int DC_MSG_NO_ID = 0;
public final static int DC_MSG_ID_MARKER1 = 1;
public final static int DC_MSG_ID_DAYMARKER = 9;
@@ -111,6 +111,12 @@ public class DcMsg {
public native void setDimension (int width, int height);
public native void setDuration (int duration);
public File getFileAsFile() {
if(getFile()==null)
throw new AssertionError("expected a file to be present.");
return new File(getFile());
}
// aliases and higher-level tools
public static int[] msgSetToIds(final Set<DcMsg> dcMsgs) {
int cnt = dcMsgs==null? 0 : dcMsgs.size();
@@ -18,11 +18,13 @@
package org.thoughtcrime.securesms;
import android.annotation.TargetApi;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.os.Build.VERSION;
import android.os.Bundle;
@@ -33,6 +35,7 @@ import android.support.v4.app.FragmentTransaction;
import android.support.v4.content.ContextCompat;
import android.support.v4.graphics.drawable.DrawableCompat;
import android.support.v7.preference.Preference;
import android.widget.Toast;
import org.thoughtcrime.securesms.preferences.AdvancedPreferenceFragment;
import org.thoughtcrime.securesms.preferences.AppProtectionPreferenceFragment;
@@ -65,6 +68,8 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
private static final String PREFERENCE_CATEGORY_APPEARANCE = "preference_category_appearance";
private static final String PREFERENCE_CATEGORY_CHATS = "preference_category_chats";
private static final String PREFERENCE_CATEGORY_ADVANCED = "preference_category_advanced";
private static final String PREFERENCE_CATEGORY_INVITE = "preference_category_invite";
private static final String PREFERENCE_CATEGORY_HELP = "preference_category_help";
public static final int REQUEST_CODE_SET_BACKGROUND = 11;
@@ -147,6 +152,10 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_CHATS));
this.findPreference(PREFERENCE_CATEGORY_ADVANCED)
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_ADVANCED));
this.findPreference(PREFERENCE_CATEGORY_INVITE)
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_INVITE));
this.findPreference(PREFERENCE_CATEGORY_HELP)
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_HELP));
if (VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
tintIcons(getActivity());
@@ -179,37 +188,36 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
.setSummary(ChatsPreferenceFragment.getSummary(getActivity()));
}
private void setCategoryVisibility() {
}
@TargetApi(11)
private void tintIcons(Context context) {
Drawable sms = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_textsms_white_24dp));
Drawable notifications = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_notifications_white_24dp));
Drawable privacy = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_security_white_24dp));
Drawable appearance = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_brightness_6_white_24dp));
Drawable chats = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_forum_white_24dp));
Drawable devices = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_laptop_white_24dp));
Drawable advanced = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_advanced_white_24dp));
Drawable invite = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_mood_white_24dp));
Drawable help = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_help_white_24dp));
int[] tintAttr = new int[]{R.attr.pref_icon_tint};
TypedArray typedArray = context.obtainStyledAttributes(tintAttr);
int color = typedArray.getColor(0, 0x0);
typedArray.recycle();
DrawableCompat.setTint(sms, color);
DrawableCompat.setTint(notifications, color);
DrawableCompat.setTint(privacy, color);
DrawableCompat.setTint(appearance, color);
DrawableCompat.setTint(chats, color);
DrawableCompat.setTint(devices, color);
DrawableCompat.setTint(advanced, color);
DrawableCompat.setTint(invite, color);
DrawableCompat.setTint(help, color);
this.findPreference(PREFERENCE_CATEGORY_NOTIFICATIONS).setIcon(notifications);
this.findPreference(PREFERENCE_CATEGORY_APP_PROTECTION).setIcon(privacy);
this.findPreference(PREFERENCE_CATEGORY_APPEARANCE).setIcon(appearance);
this.findPreference(PREFERENCE_CATEGORY_CHATS).setIcon(chats);
this.findPreference(PREFERENCE_CATEGORY_ADVANCED).setIcon(advanced);
this.findPreference(PREFERENCE_CATEGORY_INVITE).setIcon(invite);
this.findPreference(PREFERENCE_CATEGORY_HELP).setIcon(help);
}
private class CategoryClickListener implements Preference.OnPreferenceClickListener {
@@ -239,6 +247,16 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
case PREFERENCE_CATEGORY_ADVANCED:
fragment = new AdvancedPreferenceFragment();
break;
case PREFERENCE_CATEGORY_INVITE:
startActivity(new Intent(getActivity(), InviteActivity.class));
break;
case PREFERENCE_CATEGORY_HELP:
try {
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.help_url))));
} catch (ActivityNotFoundException e) {
Toast.makeText(getActivity(), R.string.ConversationListActivity_there_is_no_browser_installed_on_your_device, Toast.LENGTH_LONG).show();
}
break;
default:
throw new AssertionError();
}
@@ -14,6 +14,7 @@ import java.util.Set;
public interface BindableConversationListItem extends Unbindable {
public void bind(@NonNull ThreadRecord thread,
int msgId,
@NonNull DcLot dcSummary,
@NonNull GlideRequests glideRequests, @NonNull Locale locale,
@NonNull Set<Long> selectedThreads, boolean batchMode);
@@ -25,7 +25,6 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.database.Cursor;
import android.graphics.Color;
import android.graphics.PorterDuff.Mode;
import android.graphics.drawable.Drawable;
@@ -36,7 +35,6 @@ import android.os.Bundle;
import android.os.Vibrator;
import android.provider.Browser;
import android.provider.ContactsContract;
import android.provider.MediaStore;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.view.MenuItemCompat;
@@ -59,7 +57,6 @@ import android.view.View.OnKeyListener;
import android.view.WindowManager;
import android.view.inputmethod.EditorInfo;
import android.webkit.MimeTypeMap;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
@@ -67,6 +64,7 @@ import android.widget.TextView;
import android.widget.Toast;
import com.b44t.messenger.DcChat;
import com.b44t.messenger.DcContact;
import com.b44t.messenger.DcContext;
import com.b44t.messenger.DcEventCenter;
import com.b44t.messenger.DcMsg;
@@ -115,7 +113,6 @@ import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
import org.thoughtcrime.securesms.events.ReminderUpdateEvent;
import org.thoughtcrime.securesms.giph.ui.GiphyActivity;
import org.thoughtcrime.securesms.jobs.MultiDeviceBlockedUpdateJob;
import org.thoughtcrime.securesms.mms.AttachmentManager;
import org.thoughtcrime.securesms.mms.AttachmentManager.MediaType;
import org.thoughtcrime.securesms.mms.AudioSlide;
@@ -137,12 +134,9 @@ import org.thoughtcrime.securesms.profiles.GroupShareProfileView;
import org.thoughtcrime.securesms.providers.PersistentBlobProvider;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
import org.thoughtcrime.securesms.scribbles.ScribbleActivity;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.sms.OutgoingEndSessionMessage;
import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
import org.thoughtcrime.securesms.util.CharacterCalculator.CharacterState;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicTheme;
@@ -187,7 +181,6 @@ import static org.whispersystems.signalservice.internal.push.SignalServiceProtos
public class ConversationActivity extends PassphraseRequiredActionBarActivity
implements ConversationFragment.ConversationFragmentListener,
AttachmentManager.AttachmentListener,
RecipientModifiedListener,
DcEventCenter.DcEventDelegate,
OnKeyboardShownListener,
AttachmentDrawerListener,
@@ -196,12 +189,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
{
private static final String TAG = ConversationActivity.class.getSimpleName();
public static final String ADDRESS_EXTRA = "address";
public static final String THREAD_ID_EXTRA = "thread_id";
public static final String IS_ARCHIVED_EXTRA = "is_archived";
public static final String TEXT_EXTRA = "draft_text";
public static final String DISTRIBUTION_TYPE_EXTRA = "distribution_type";
public static final String TIMING_EXTRA = "timing";
public static final String LAST_SEEN_EXTRA = "last_seen";
public static final String STARTING_POSITION_EXTRA = "starting_position";
@@ -225,7 +215,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
protected ConversationTitleView titleView;
private TextView charactersLeft;
private ConversationFragment fragment;
private Button unblockButton;
private InputAwareLayout container;
private View composePanel;
protected Stub<ReminderView> reminderView;
@@ -245,11 +234,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private ApplicationDcContext dcContext;
private DcChat dcChat = new DcChat(0);
private long threadId;
private int distributionType;
private boolean archived;
private final boolean isSecureText = true;
private boolean isDefaultSms = true;
private boolean isMmsEnabled = true;
private boolean isSecurityInitialized = false;
@@ -358,14 +345,11 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
titleView.setTitle(glideRequests, dcChat);
titleView.setVerified(dcChat.isVerified());
setBlockedUserState(recipient, isSecureText, isDefaultSms);
setGroupShareProfileReminder(recipient);
calculateCharactersRemaining();
MessageNotifier.setVisibleThread(threadId);
markThreadAsRead();
Log.w(TAG, "onResume() Finished: " + (System.currentTimeMillis() - getIntent().getLongExtra(TIMING_EXTRA, 0)));
}
@Override
@@ -403,7 +387,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Override
protected void onDestroy() {
saveDraft();
if (recipient != null) recipient.removeListener(this);
if (securityUpdateReceiver != null) unregisterReceiver(securityUpdateReceiver);
dcContext.eventCenter.removeObservers(this);
super.onDestroy();
@@ -451,9 +434,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
break;
case GROUP_EDIT:
recipient = Recipient.from(this, data.getParcelableExtra(GroupCreateActivity.GROUP_ADDRESS_EXTRA), true);
recipient.addListener(this);
dcChat = dcContext.getChat((int)threadId);
titleView.setTitle(glideRequests, dcChat);
setBlockedUserState(recipient, isSecureText, isDefaultSms);
supportInvalidateOptionsMenu();
break;
case TAKE_PHOTO:
@@ -463,7 +445,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
break;
case ADD_CONTACT:
recipient = Recipient.from(this, recipient.getAddress(), true);
recipient.addListener(this);
fragment.reloadList();
break;
case PICK_LOCATION:
@@ -504,50 +485,33 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
MenuInflater inflater = this.getMenuInflater();
menu.clear();
if (isSecureText) {
if (recipient.getExpireMessages() > 0) {
inflater.inflate(R.menu.conversation_expiring_on, menu);
final MenuItem item = menu.findItem(R.id.menu_expiring_messages);
final View actionView = MenuItemCompat.getActionView(item);
final TextView badgeView = actionView.findViewById(R.id.expiration_badge);
badgeView.setText(ExpirationUtil.getExpirationAbbreviatedDisplayValue(this, recipient.getExpireMessages()));
actionView.setOnClickListener(v -> onOptionsItemSelected(item));
} else {
inflater.inflate(R.menu.conversation_expiring_off, menu);
}
if(threadId==DcChat.DC_CHAT_ID_DEADDROP) {
return true;
}
if (isGroupConversation()) {
inflater.inflate(R.menu.conversation_group_options, menu);
if (!isPushGroupConversation()) {
inflater.inflate(R.menu.conversation_mms_group_options, menu);
if (distributionType == ThreadDatabase.DistributionTypes.BROADCAST) {
menu.findItem(R.id.menu_distribution_broadcast).setChecked(true);
} else {
menu.findItem(R.id.menu_distribution_conversation).setChecked(true);
}
} else if (isActiveGroup()) {
inflater.inflate(R.menu.conversation_push_group_options, menu);
}
if (recipient != null && recipient.isMuted()) {
inflater.inflate(R.menu.conversation_muted, menu);
}
else {
inflater.inflate(R.menu.conversation_unmuted, menu);
}
inflater.inflate(R.menu.conversation, menu);
if (isSingleConversation() && isSecureText) {
inflater.inflate(R.menu.conversation_secure, menu);
} else if (isSingleConversation()) {
inflater.inflate(R.menu.conversation_insecure, menu);
if (isGroupConversation()) {
if (isActiveGroup()) {
inflater.inflate(R.menu.conversation_push_group_options, menu);
}
}
if (recipient != null && recipient.isMuted()) inflater.inflate(R.menu.conversation_muted, menu);
else inflater.inflate(R.menu.conversation_unmuted, menu);
if (isSingleConversation() && getRecipient().getContactUri() == null) {
inflater.inflate(R.menu.conversation_add_to_contacts, menu);
if( dcChat.getArchived()==0 ) {
inflater.inflate(R.menu.conversation_archive, menu);
}
else {
inflater.inflate(R.menu.conversation_unarchive, menu);
}
inflater.inflate(R.menu.conversation_delete, menu);
super.onPrepareOptionsMenu(menu);
return true;
@@ -557,21 +521,16 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
public boolean onOptionsItemSelected(MenuItem item) {
super.onOptionsItemSelected(item);
switch (item.getItemId()) {
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;
case R.id.menu_reset_secure_session: handleResetSecureSession(); return true;
case R.id.menu_group_recipients: handleDisplayGroupRecipients(); return true;
case R.id.menu_distribution_broadcast: handleDistributionBroadcastEnabled(item); return true;
case R.id.menu_distribution_conversation: handleDistributionConversationEnabled(item); return true;
case R.id.menu_edit_group: handleEditPushGroup(); return true;
case R.id.menu_leave: handleLeavePushGroup(); return true;
case R.id.menu_mute_notifications: handleMuteNotifications(); return true;
case R.id.menu_unmute_notifications: handleUnmuteNotifications(); return true;
case R.id.menu_conversation_settings: handleConversationSettings(); return true;
case R.id.menu_expiring_messages_off:
case R.id.menu_expiring_messages: handleSelectMessageExpiration(); return true;
case android.R.id.home: handleReturnToConversationList(); return true;
case R.id.menu_add_attachment: handleAddAttachment(); return true;
case R.id.menu_view_media: handleViewMedia(); return true;
case R.id.menu_edit_group: handleEditPushGroup(); return true;
case R.id.menu_leave: handleLeaveGroup(); return true;
case R.id.menu_archive_chat: handleArchiveChat(); return true;
case R.id.menu_delete_chat: handleDeleteChat(); return true;
case R.id.menu_mute_notifications: handleMuteNotifications(); return true;
case R.id.menu_unmute_notifications: handleUnmuteNotifications(); return true;
case R.id.menu_conversation_settings: handleConversationSettings(); return true;
case android.R.id.home: handleReturnToConversationList(); return true;
}
return false;
@@ -608,32 +567,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
finish();
}
private void handleSelectMessageExpiration() {
if (isPushGroupConversation() && !isActiveGroup()) {
return;
}
//noinspection CodeBlock2Expr
ExpirationDialog.show(this, recipient.getExpireMessages(), expirationTime -> {
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
DatabaseFactory.getRecipientDatabase(ConversationActivity.this).setExpireMessages(recipient, expirationTime);
OutgoingExpirationUpdateMessage outgoingMessage = new OutgoingExpirationUpdateMessage(getRecipient(), System.currentTimeMillis(), expirationTime * 1000L);
MessageSender.send(ConversationActivity.this, outgoingMessage, threadId, false, null);
return null;
}
@Override
protected void onPostExecute(Void result) {
invalidateOptionsMenu();
if (fragment != null) fragment.setLastSeen(0);
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
});
}
private void handleMuteNotifications() {
MuteDialog.show(this, until -> {
recipient.setMuted(until);
@@ -673,101 +606,42 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
private void handleUnblock() {
//noinspection CodeBlock2Expr
new AlertDialog.Builder(this)
.setTitle(R.string.ConversationActivity_unblock_this_contact_question)
.setMessage(R.string.ConversationActivity_you_will_once_again_be_able_to_receive_messages_and_calls_from_this_contact)
.setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(R.string.ConversationActivity_unblock, (dialog, which) -> {
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
DatabaseFactory.getRecipientDatabase(ConversationActivity.this)
.setBlocked(recipient, false);
ApplicationContext.getInstance(ConversationActivity.this)
.getJobManager()
.add(new MultiDeviceBlockedUpdateJob(ConversationActivity.this));
return null;
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}).show();
}
private void handleResetSecureSession() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.ConversationActivity_reset_secure_session_question);
builder.setIconAttribute(R.attr.dialog_alert_icon);
builder.setCancelable(true);
builder.setMessage(R.string.ConversationActivity_this_may_help_if_youre_having_encryption_problems);
builder.setPositiveButton(R.string.ConversationActivity_reset, (dialog, which) -> {
if (isSingleConversation()) {
final Context context = getApplicationContext();
OutgoingEndSessionMessage endSessionMessage =
new OutgoingEndSessionMessage(new OutgoingTextMessage(getRecipient(), "TERMINATE", 0, -1));
new AsyncTask<OutgoingEndSessionMessage, Void, Long>() {
@Override
protected Long doInBackground(OutgoingEndSessionMessage... messages) {
return MessageSender.send(context, messages[0], threadId, false, null);
}
@Override
protected void onPostExecute(Long result) {
sendComplete(result);
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, endSessionMessage);
}
});
builder.setNegativeButton(android.R.string.cancel, null);
builder.show();
}
private void handleViewMedia() {
Intent intent = new Intent(this, MediaOverviewActivity.class);
intent.putExtra(MediaOverviewActivity.ADDRESS_EXTRA, recipient.getAddress());
startActivity(intent);
}
private void handleLeavePushGroup() {
if (getRecipient() == null) {
Toast.makeText(this, getString(R.string.ConversationActivity_invalid_recipient),
Toast.LENGTH_LONG).show();
return;
private void handleLeaveGroup() {
new AlertDialog.Builder(this)
.setMessage(getString(R.string.ConversationActivity_are_you_sure_you_want_to_leave_this_group))
.setPositiveButton(R.string.yes, (dialog, which) -> {
dcContext.removeContactFromChat((int)threadId, DcContact.DC_CONTACT_ID_SELF);
Toast.makeText(this, getString(R.string.done), Toast.LENGTH_SHORT).show();
})
.setNegativeButton(R.string.no, null)
.show();
}
private void handleArchiveChat() {
int doArchive = dcContext.getChat((int)threadId).getArchived()==0? 1: 0;
dcContext.archiveChat((int)threadId, doArchive);
Toast.makeText(this, getString(R.string.done), Toast.LENGTH_SHORT).show();
if( doArchive == 1 ) {
finish();
}
}
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(getString(R.string.ConversationActivity_leave_group));
builder.setIconAttribute(R.attr.dialog_info_icon);
builder.setCancelable(true);
builder.setMessage(getString(R.string.ConversationActivity_are_you_sure_you_want_to_leave_this_group));
builder.setPositiveButton(R.string.yes, (dialog, which) -> {
Context self = ConversationActivity.this;
try {
String groupId = getRecipient().getAddress().toGroupString();
DatabaseFactory.getGroupDatabase(self).setActive(groupId, false);
GroupContext context = GroupContext.newBuilder()
.setId(ByteString.copyFrom(GroupUtil.getDecodedId(groupId)))
.setType(GroupContext.Type.QUIT)
.build();
OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(getRecipient(), context, null, System.currentTimeMillis(), 0, null, Collections.emptyList());
MessageSender.send(self, outgoingMessage, threadId, false, null);
DatabaseFactory.getGroupDatabase(self).remove(groupId, Address.fromSerialized(TextSecurePreferences.getLocalNumber(self)));
initializeEnabledCheck();
} catch (IOException e) {
Log.w(TAG, e);
Toast.makeText(self, R.string.ConversationActivity_error_leaving_group, Toast.LENGTH_LONG).show();
}
});
builder.setNegativeButton(R.string.no, null);
builder.show();
private void handleDeleteChat() {
new AlertDialog.Builder(this)
.setMessage(getString(R.string.ConversationActivity_ask_delete_chat))
.setPositiveButton(R.string.yes, (dialog, which) -> {
dcContext.deleteChat((int)threadId);
Toast.makeText(this, getString(R.string.done), Toast.LENGTH_SHORT).show();
finish();
})
.setNegativeButton(R.string.no, null)
.show();
}
private void handleEditPushGroup() {
@@ -776,72 +650,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
startActivityForResult(intent, GROUP_EDIT);
}
private void handleDistributionBroadcastEnabled(MenuItem item) {
distributionType = ThreadDatabase.DistributionTypes.BROADCAST;
item.setChecked(true);
if (threadId != -1) {
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
DatabaseFactory.getThreadDatabase(ConversationActivity.this)
.setDistributionType(threadId, ThreadDatabase.DistributionTypes.BROADCAST);
return null;
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
}
private void handleDistributionConversationEnabled(MenuItem item) {
distributionType = ThreadDatabase.DistributionTypes.CONVERSATION;
item.setChecked(true);
if (threadId != -1) {
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
DatabaseFactory.getThreadDatabase(ConversationActivity.this)
.setDistributionType(threadId, ThreadDatabase.DistributionTypes.CONVERSATION);
return null;
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
}
private void handleDisplayGroupRecipients() {
//TODO: i think this button is not needed and the group options just go to the profile
//new GroupMembersDialog(this, getRecipient()).display();
}
private void handleAddToContacts() {
if (recipient.getAddress().isGroup()) return;
try {
final Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
if (recipient.getAddress().isEmail()) {
intent.putExtra(ContactsContract.Intents.Insert.EMAIL, recipient.getAddress().toEmailString());
} else {
intent.putExtra(ContactsContract.Intents.Insert.PHONE, recipient.getAddress().toPhoneString());
}
intent.setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE);
startActivityForResult(intent, ADD_CONTACT);
} catch (ActivityNotFoundException e) {
Log.w(TAG, e);
}
}
private boolean handleDisplayQuickContact() {
if (recipient.getAddress().isGroup()) return false;
if (recipient.getContactUri() != null) {
ContactsContract.QuickContact.showQuickContact(ConversationActivity.this, titleView, recipient.getContactUri(), ContactsContract.QuickContact.MODE_LARGE, null);
} else {
handleAddToContacts();
}
return true;
}
private void handleAddAttachment() {
if (attachmentTypeSelector == null) {
attachmentTypeSelector = new AttachmentTypeSelector(this, getSupportLoaderManager(), new AttachmentTypeListener());
@@ -863,7 +671,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
calculateCharactersRemaining();
supportInvalidateOptionsMenu();
setBlockedUserState(recipient, isSecureText, isDefaultSms);
}
///// Initializers
@@ -1002,7 +809,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
composeText = ViewUtil.findById(this, R.id.embedded_text_editor);
charactersLeft = ViewUtil.findById(this, R.id.space_left);
emojiDrawerStub = ViewUtil.findStubById(this, R.id.emoji_drawer_stub);
unblockButton = ViewUtil.findById(this, R.id.unblock_button);
composePanel = ViewUtil.findById(this, R.id.bottom_panel);
container = ViewUtil.findById(this, R.id.layout_container);
reminderView = ViewUtil.findStubById(this, R.id.reminder_stub);
@@ -1039,9 +845,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
});
titleView.setOnClickListener(v -> handleConversationSettings());
titleView.setOnLongClickListener(v -> handleDisplayQuickContact());
titleView.setOnBackClickedListener(view -> super.onBackPressed());
unblockButton.setOnClickListener(v -> handleUnblock());
composeText.setOnKeyListener(composeKeyPressedListener);
composeText.addTextChangedListener(composeKeyPressedListener);
@@ -1073,13 +877,10 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
private void initializeResources() {
if (recipient != null) recipient.removeListener(this);
threadId = getIntent().getLongExtra(THREAD_ID_EXTRA, -1);
dcChat = dcContext.getChat((int)threadId);
recipient = dcContext.getRecipient(dcChat);
archived = getIntent().getBooleanExtra(IS_ARCHIVED_EXTRA, false);
distributionType = getIntent().getIntExtra(DISTRIBUTION_TYPE_EXTRA, ThreadDatabase.DistributionTypes.DEFAULT);
glideRequests = GlideApp.with(this);
@@ -1089,20 +890,10 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
conversationContainer.setClipToPadding(true);
}
recipient.addListener(this);
}
public void onModified(final Recipient recipient) {
Log.w(TAG, "onModified(" + recipient.getAddress().serialize() + ")");
Util.runOnMain(() -> {
Log.w(TAG, "onModifiedRun(): " + recipient.getRegistered());
titleView.setTitle(glideRequests, dcChat);
setBlockedUserState(recipient, isSecureText, isDefaultSms);
setGroupShareProfileReminder(recipient);
updateReminders();
initializeSecurity(isSecureText, isDefaultSms);
invalidateOptionsMenu();
});
if(threadId==DcChat.DC_CHAT_ID_DEADDROP) {
composePanel.setVisibility(View.GONE);
titleView.hideAvatar();
}
}
private void initializeReceivers() {
@@ -1229,7 +1020,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
final Drafts drafts = getDraftsForCurrentState();
final long thisThreadId = this.threadId;
final int thisDistributionType = this.distributionType;
new AsyncTask<Long, Void, Long>() {
@Override
@@ -1238,9 +1028,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
DraftDatabase draftDatabase = DatabaseFactory.getDraftDatabase(ConversationActivity.this);
long threadId = params[0];
if (drafts.size() > 0) {
if (threadId == -1) threadId = threadDatabase.getThreadIdFor(getRecipient(), thisDistributionType);
if (drafts.size() > 0 && threadId>0) {
draftDatabase.insertDrafts(threadId, drafts);
threadDatabase.updateSnippet(threadId, drafts.getSnippet(ConversationActivity.this),
drafts.getUriSnippet(),
@@ -1262,16 +1050,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
return future;
}
private void setBlockedUserState(Recipient recipient, boolean isSecureText, boolean isDefaultSms) {
if (false) { // TODO: do we need a unblock button here? typically, the chatlist is closed when blocked
unblockButton.setVisibility(View.VISIBLE);
composePanel.setVisibility(View.GONE);
} else {
composePanel.setVisibility(View.VISIBLE);
unblockButton.setVisibility(View.GONE);
}
}
private void setGroupShareProfileReminder(@NonNull Recipient recipient) {
if (recipient.isPushGroupRecipient() && !recipient.isProfileSharing()) {
groupShareProfileView.get().setRecipient(recipient);
@@ -1887,7 +1665,11 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Override
public void handleEvent(int eventId, Object data1, Object data2) {
if (eventId==DcContext.DC_EVENT_CHAT_MODIFIED || eventId==DcContext.DC_EVENT_CONTACTS_CHANGED) {
onModified(recipient);
titleView.setTitle(glideRequests, dcChat);
setGroupShareProfileReminder(recipient);
updateReminders();
initializeSecurity(isSecureText, isDefaultSms);
invalidateOptionsMenu();
}
}
}
@@ -55,6 +55,7 @@ import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import com.b44t.messenger.DcChat;
import com.b44t.messenger.DcContact;
import com.b44t.messenger.DcContext;
import com.b44t.messenger.DcEventCenter;
@@ -68,7 +69,9 @@ import org.thoughtcrime.securesms.connect.DcMsgListLoader;
import org.thoughtcrime.securesms.contactshare.Contact;
import org.thoughtcrime.securesms.contactshare.ContactUtil;
import org.thoughtcrime.securesms.contactshare.SharedContactDetailsActivity;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob;
import org.thoughtcrime.securesms.mms.GlideApp;
@@ -166,8 +169,16 @@ public class ConversationFragment extends Fragment
}
private void setNoMessageText() {
if(getListAdapter().isGroupChat()){
noMessageTextView.setText(R.string.ConversationActivity_MsgNewGroupDraftHint);
if(threadId==DcChat.DC_CHAT_ID_DEADDROP) {
noMessageTextView.setText(R.string.conversation__no_messages);
}
else if(getListAdapter().isGroupChat()){
if( dcContext.getChat((int)threadId).isUnpromoted() ) {
noMessageTextView.setText(R.string.ConversationActivity_MsgNewGroupDraftHint);
}
else {
noMessageTextView.setText(R.string.conversation__no_messages);
}
}else{
String name = getListAdapter().getChatName();
String message = getString(R.string.ConversationActivity_NoMessagesHint, name, name);
@@ -225,8 +236,8 @@ public class ConversationFragment extends Fragment
}
private void initializeResources() {
this.recipient = Recipient.from(getActivity(), getActivity().getIntent().getParcelableExtra(ConversationActivity.ADDRESS_EXTRA), true);
this.threadId = this.getActivity().getIntent().getLongExtra(ConversationActivity.THREAD_ID_EXTRA, -1);
this.recipient = Recipient.from(getActivity(), Address.fromChat((int)this.threadId), true);
this.lastSeen = this.getActivity().getIntent().getLongExtra(ConversationActivity.LAST_SEEN_EXTRA, -1);
this.startingPosition = this.getActivity().getIntent().getIntExtra(ConversationActivity.STARTING_POSITION_EXTRA, -1);
this.firstLoad = true;
@@ -740,6 +751,21 @@ public class ConversationFragment extends Fragment
actionMode.setTitle(String.valueOf(getListAdapter().getSelectedItems().size()));
}
}
else if(threadId==DcChat.DC_CHAT_ID_DEADDROP) {
new AlertDialog.Builder(getActivity())
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
int chatId = dcContext.createChatByMsgId(messageRecord.getId());
if( chatId != 0 ) {
Intent intent = new Intent(getActivity(), ConversationActivity.class);
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, (long)chatId);
startActivity(intent);
}
})
.setNegativeButton(android.R.string.cancel, null)
.setMessage(getActivity().getString(R.string.new_conversation_activity__ask_start_chat_with, dcContext.getContact(messageRecord.getFromId()).getDisplayName()))
.show();
}
else if(messageRecord.isSetupMessage()) {
querySetupCode(messageRecord,null);
}
@@ -683,11 +683,9 @@ public class ConversationItem extends LinearLayout
} else if (MediaPreviewActivity.isContentTypeSupported(slide.getContentType()) && slide.getUri() != null) {
Intent intent = new Intent(context, MediaPreviewActivity.class);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setDataAndType(slide.getUri(), slide.getContentType());
intent.putExtra(MediaPreviewActivity.DC_MSG_ID, slide.getDcMsgId());
intent.putExtra(MediaPreviewActivity.ADDRESS_EXTRA, conversationRecipient.getAddress());
intent.putExtra(MediaPreviewActivity.OUTGOING_EXTRA, messageRecord.isOutgoing());
intent.putExtra(MediaPreviewActivity.DATE_EXTRA, messageRecord.getTimestamp());
intent.putExtra(MediaPreviewActivity.SIZE_EXTRA, slide.asAttachment().getSize());
intent.putExtra(MediaPreviewActivity.LEFT_IS_RECENT_EXTRA, false);
context.startActivity(intent);
@@ -17,9 +17,7 @@
package org.thoughtcrime.securesms;
import android.Manifest;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.widget.Toolbar;
import android.support.v7.widget.TooltipCompat;
@@ -28,8 +26,8 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.Toast;
import com.b44t.messenger.DcChat;
import com.google.zxing.integration.android.IntentIntegrator;
import com.google.zxing.integration.android.IntentResult;
@@ -37,7 +35,6 @@ import org.thoughtcrime.securesms.components.SearchToolbar;
import org.thoughtcrime.securesms.lock.RegistrationLockDialog;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.qr.QrScanHandler;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.search.SearchFragment;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.util.DynamicLanguage;
@@ -158,13 +155,12 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
super.onOptionsItemSelected(item);
switch (item.getItemId()) {
case R.id.menu_new_chat: createChat(); return true;
case R.id.menu_settings: handleDisplaySettings(); return true;
case R.id.menu_clear_passphrase: handleClearPassphrase(); return true;
case R.id.menu_invite: handleInvite(); return true;
case R.id.menu_help: handleHelp(); return true;
case R.id.menu_qr_scan: handleQrScan(); return true;
case R.id.menu_qr_show: handleQrShow(); return true;
case R.id.menu_new_chat: createChat(); return true;
case R.id.menu_settings: handleDisplaySettings(); return true;
case R.id.menu_clear_passphrase: handleClearPassphrase(); return true;
case R.id.menu_qr_scan: handleQrScan(); return true;
case R.id.menu_qr_show: handleQrShow(); return true;
case R.id.menu_deaddrop: handleDeaddrop(); return true;
}
return false;
@@ -180,18 +176,15 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
}
@Override
public void onCreateConversation(long threadId, Recipient recipient, int distributionType, long lastSeen) {
openConversation(threadId, recipient, distributionType, lastSeen, -1);
public void onCreateConversation(long threadId, long lastSeen) {
openConversation(threadId, lastSeen, -1);
}
public void openConversation(long threadId, Recipient recipient, int distributionType, long lastSeen, int startingPosition) {
public void openConversation(long threadId, long lastSeen, int startingPosition) {
searchToolbar.clearFocus();
Intent intent = new Intent(this, ConversationActivity.class);
intent.putExtra(ConversationActivity.ADDRESS_EXTRA, recipient.getAddress());
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId);
intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, distributionType);
intent.putExtra(ConversationActivity.TIMING_EXTRA, System.currentTimeMillis());
intent.putExtra(ConversationActivity.LAST_SEEN_EXTRA, lastSeen);
intent.putExtra(ConversationActivity.STARTING_POSITION_EXTRA, startingPosition);
@@ -216,6 +209,12 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
startActivity(intent);
}
private void handleDeaddrop() {
Intent intent = new Intent(this, ConversationActivity.class);
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, (long)DcChat.DC_CHAT_ID_DEADDROP);
startActivity(intent);
}
private void handleDisplaySettings() {
Intent preferencesIntent = new Intent(this, ApplicationPreferencesActivity.class);
startActivity(preferencesIntent);
@@ -227,18 +226,6 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
startService(intent);
}
private void handleInvite() {
startActivity(new Intent(this, InviteActivity.class));
}
private void handleHelp() {
try {
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.help_url))));
} catch (ActivityNotFoundException e) {
Toast.makeText(this, R.string.ConversationListActivity_there_is_no_browser_installed_on_your_device, Toast.LENGTH_LONG).show();
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == IntentIntegrator.REQUEST_CODE) {
@@ -48,6 +48,7 @@ class ConversationListAdapter extends RecyclerView.Adapter {
private static final int MESSAGE_TYPE_SWITCH_ARCHIVE = 1;
private static final int MESSAGE_TYPE_THREAD = 2;
private static final int MESSAGE_TYPE_INBOX_ZERO = 3;
private static final int MESSAGE_TYPE_DEADDROP = 4; // DEADDROP and THREAD share the same class, however, for DEADDROP it is modified on construction so it cannot be reused
private final @NonNull ApplicationDcContext dcContext;
private @NonNull DcChatlist dcChatlist;
@@ -131,14 +132,17 @@ class ConversationListAdapter extends RecyclerView.Adapter {
ViewHolder holder = (ViewHolder)viewHolder;
DcChat chat = dcContext.getChat(dcChatlist.getChatId(i));
DcLot summary = dcChatlist.getSummary(i, chat);
holder.getItem().bind(dcContext.getThreadRecord(summary, chat), summary, glideRequests, locale, batchSet, batchMode);
holder.getItem().bind(dcContext.getThreadRecord(summary, chat), dcChatlist.getMsgId(i), summary, glideRequests, locale, batchSet, batchMode);
}
@Override
public int getItemViewType(int i) {
int chatId = dcChatlist.getChatId(i);
if (chatId == DcChat.DC_CHAT_ID_ARCHIVED_LINK) {
if (chatId==DcChat.DC_CHAT_ID_DEADDROP) {
return MESSAGE_TYPE_DEADDROP;
}
else if (chatId == DcChat.DC_CHAT_ID_ARCHIVED_LINK) {
return MESSAGE_TYPE_SWITCH_ARCHIVE;
} else if(chatId == DcChat.DC_CHAT_ID_ALLDONE_HINT) {
return MESSAGE_TYPE_INBOX_ZERO;
@@ -51,12 +51,10 @@ public class ConversationListArchiveActivity extends PassphraseRequiredActionBar
}
@Override
public void onCreateConversation(long threadId, Recipient recipient, int distributionType, long lastSeenTime) {
public void onCreateConversation(long threadId, long lastSeenTime) {
Intent intent = new Intent(this, ConversationActivity.class);
intent.putExtra(ConversationActivity.ADDRESS_EXTRA, recipient.getAddress());
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId);
intent.putExtra(ConversationActivity.IS_ARCHIVED_EXTRA, true);
intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, distributionType);
intent.putExtra(ConversationActivity.LAST_SEEN_EXTRA, lastSeenTime);
startActivity(intent);
@@ -19,6 +19,7 @@ package org.thoughtcrime.securesms;
import android.annotation.SuppressLint;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
@@ -50,7 +51,9 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.b44t.messenger.DcChat;
import com.b44t.messenger.DcChatlist;
import com.b44t.messenger.DcContact;
import com.b44t.messenger.DcContext;
import com.b44t.messenger.DcEventCenter;
@@ -270,10 +273,7 @@ public class ConversationListFragment extends Fragment
final DcContext dcContext = DcHelper.getContext(getActivity());
int conversationsCount = getListAdapter().getBatchSelections().size();
AlertDialog.Builder alert = new AlertDialog.Builder(getActivity());
alert.setIconAttribute(R.attr.dialog_alert_icon);
alert.setTitle(getActivity().getResources().getQuantityString(R.plurals.ConversationListFragment_delete_selected_conversations,
conversationsCount, conversationsCount));
alert.setMessage(getActivity().getResources().getQuantityString(R.plurals.ConversationListFragment_this_will_permanently_delete_all_n_selected_conversations,
alert.setMessage(getActivity().getResources().getQuantityString(R.plurals.ConversationListFragment_ask_selected_chats,
conversationsCount, conversationsCount));
alert.setCancelable(true);
@@ -322,8 +322,8 @@ public class ConversationListFragment extends Fragment
actionMode.setTitle(String.valueOf(getListAdapter().getBatchSelections().size()));
}
private void handleCreateConversation(long threadId, Recipient recipient, int distributionType, long lastSeen) {
((ConversationSelectedListener)getActivity()).onCreateConversation(threadId, recipient, distributionType, lastSeen);
private void handleCreateConversation(long threadId, long lastSeen) {
((ConversationSelectedListener)getActivity()).onCreateConversation(threadId, lastSeen);
}
@Override
@@ -368,8 +368,30 @@ public class ConversationListFragment extends Fragment
@Override
public void onItemClick(ConversationListItem item) {
if (actionMode == null) {
handleCreateConversation(item.getThreadId(), item.getRecipient(),
item.getDistributionType(), item.getLastSeen());
long threadId = item.getThreadId();
if (threadId==DcChat.DC_CHAT_ID_DEADDROP) {
DcContext dcContext = DcHelper.getContext(getActivity());
int msgId = item.getMsgId();
int contactId = item.getContactId();
DcContact contact = dcContext.getContact(contactId);
new AlertDialog.Builder(getActivity())
.setMessage(getActivity().getString(R.string.new_conversation_activity__ask_start_chat_with, contact.getNameNAddr()))
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
int belongingChatId = dcContext.createChatByMsgId(msgId);
if( belongingChatId != 0 ) {
handleCreateConversation(belongingChatId, 0);
}
})
.setNegativeButton(R.string.not_now, null)
.setNeutralButton(R.string.never, (dialog, which) -> {
dcContext.blockContact(contactId, 1);
})
.show();
return;
}
handleCreateConversation(threadId, item.getLastSeen());
} else {
ConversationListAdapter adapter = (ConversationListAdapter)list.getAdapter();
adapter.toggleThreadInBatchSet(item.getThreadId());
@@ -399,7 +421,7 @@ public class ConversationListFragment extends Fragment
}
public interface ConversationSelectedListener {
void onCreateConversation(long threadId, Recipient recipient, int distributionType, long lastSeen);
void onCreateConversation(long threadId, long lastSeen);
void onSwitchToArchive();
}
@@ -506,6 +528,12 @@ public class ConversationListFragment extends Fragment
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, threadId);
} else {
if (threadId==DcChat.DC_CHAT_ID_DEADDROP) {
int contactId = ((ConversationListItem)viewHolder.itemView).getContactId();
dcContext.marknoticedContact(contactId);
return;
}
new SnackbarAsyncTask<Long>(getView(),
getResources().getQuantityString(R.plurals.ConversationListFragment_conversations_archived, 1, 1),
getString(R.string.ConversationListFragment_undo),
@@ -17,10 +17,9 @@
package org.thoughtcrime.securesms;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.Typeface;
import android.graphics.drawable.RippleDrawable;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.support.annotation.NonNull;
@@ -38,21 +37,23 @@ import android.widget.TextView;
import com.amulyakhare.textdrawable.TextDrawable;
import com.annimon.stream.Stream;
import com.b44t.messenger.DcChat;
import com.b44t.messenger.DcContact;
import com.b44t.messenger.DcContext;
import com.b44t.messenger.DcLot;
import com.b44t.messenger.DcMsg;
import org.thoughtcrime.securesms.components.AvatarImageView;
import org.thoughtcrime.securesms.components.DeliveryStatusView;
import org.thoughtcrime.securesms.components.FromTextView;
import org.thoughtcrime.securesms.components.ThumbnailView;
import org.thoughtcrime.securesms.connect.ApplicationDcContext;
import org.thoughtcrime.securesms.connect.DcHelper;
import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
import org.thoughtcrime.securesms.search.model.MessageResult;
import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.ThemeUtil;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.ViewUtil;
import java.util.Collections;
@@ -61,8 +62,7 @@ import java.util.Locale;
import java.util.Set;
public class ConversationListItem extends RelativeLayout
implements RecipientModifiedListener,
BindableConversationListItem, Unbindable
implements BindableConversationListItem, Unbindable
{
@SuppressWarnings("unused")
private final static String TAG = ConversationListItem.class.getSimpleName();
@@ -74,6 +74,7 @@ public class ConversationListItem extends RelativeLayout
private Set<Long> selectedThreads;
private Recipient recipient;
private long threadId;
private int msgId;
private GlideRequests glideRequests;
private TextView subjectView;
private FromTextView fromView;
@@ -86,9 +87,6 @@ public class ConversationListItem extends RelativeLayout
private int unreadCount;
private AvatarImageView contactPhotoImage;
private ThumbnailView thumbnailView;
private int distributionType;
public ConversationListItem(Context context) {
this(context, null);
@@ -106,11 +104,9 @@ public class ConversationListItem extends RelativeLayout
this.dateView = findViewById(R.id.date);
this.deliveryStatusIndicator = findViewById(R.id.delivery_status);
this.contactPhotoImage = findViewById(R.id.contact_photo_image);
this.thumbnailView = findViewById(R.id.thumbnail);
this.archivedView = findViewById(R.id.archived);
this.unreadIndicator = findViewById(R.id.unread_indicator);
this.verifiedIndicator = findViewById(R.id.verified_indicator);
thumbnailView.setClickable(false);
ViewUtil.setTextViewGravityStart(this.fromView, getContext());
ViewUtil.setTextViewGravityStart(this.subjectView, getContext());
@@ -118,16 +114,18 @@ public class ConversationListItem extends RelativeLayout
@Override
public void bind(@NonNull ThreadRecord thread,
int msgId,
@NonNull DcLot dcSummary,
@NonNull GlideRequests glideRequests,
@NonNull Locale locale,
@NonNull Set<Long> selectedThreads,
boolean batchMode)
{
bind(thread, dcSummary, glideRequests, locale, selectedThreads, batchMode, null);
bind(thread, msgId, dcSummary, glideRequests, locale, selectedThreads, batchMode, null);
}
public void bind(@NonNull ThreadRecord thread,
int msgId,
@NonNull DcLot dcSummary,
@NonNull GlideRequests glideRequests,
@NonNull Locale locale,
@@ -139,12 +137,11 @@ public class ConversationListItem extends RelativeLayout
this.selectedThreads = selectedThreads;
this.recipient = thread.getRecipient();
this.threadId = thread.getThreadId();
this.msgId = msgId;
this.glideRequests = glideRequests;
this.unreadCount = thread.getUnreadCount();
this.distributionType = thread.getDistributionType();
this.lastSeen = thread.getLastSeen();
this.recipient.addListener(this);
if (highlightSubstring != null) {
this.fromView.setText(getHighlightedSpan(locale, recipient.getName(), highlightSubstring));
} else {
@@ -159,23 +156,21 @@ public class ConversationListItem extends RelativeLayout
if (thread.getDate() > 0) {
CharSequence date = DateUtils.getBriefRelativeTimeSpanString(getContext(), locale, thread.getDate());
dateView.setText(date);
dateView.setTypeface(unreadCount == 0 ? LIGHT_TYPEFACE : BOLD_TYPEFACE);
dateView.setTextColor(unreadCount == 0 ? ThemeUtil.getThemedColor(getContext(), R.attr.conversation_list_item_date_color)
: ThemeUtil.getThemedColor(getContext(), R.attr.conversation_list_item_unread_color));
}
if (thread.isArchived()) {
this.archivedView.setVisibility(View.VISIBLE);
} else {
this.archivedView.setVisibility(View.GONE);
}
setStatusIcons(thread);
setThumbnailSnippet(thread);
setBatchState(batchMode);
setRippleColor(recipient);
setUnreadIndicator(thread);
this.contactPhotoImage.setAvatar(glideRequests, recipient, true);
setBgColor();
if(threadId==DcChat.DC_CHAT_ID_DEADDROP) {
ApplicationDcContext dcContext = DcHelper.getContext(getContext());
DcContact dcContact = dcContext.getContact(dcContext.getMsg(msgId).getFromId());
this.contactPhotoImage.setAvatar(glideRequests, dcContext.getRecipient(dcContact), true);
}
else {
this.contactPhotoImage.setAvatar(glideRequests, recipient, true);
}
verifiedIndicator.setVisibility(thread.isVerified() ? VISIBLE : GONE);
}
@@ -188,18 +183,15 @@ public class ConversationListItem extends RelativeLayout
this.recipient = contact;
this.glideRequests = glideRequests;
this.recipient.addListener(this);
fromView.setText(getHighlightedSpan(locale, recipient.getName(), highlightSubstring));
subjectView.setText(getHighlightedSpan(locale, contact.getAddress().toPhoneString(), highlightSubstring));
dateView.setText("");
archivedView.setVisibility(GONE);
unreadIndicator.setVisibility(GONE);
deliveryStatusIndicator.setNone();
thumbnailView.setVisibility(GONE);
setBatchState(false);
setRippleColor(contact);
setBgColor();
contactPhotoImage.setAvatar(glideRequests, recipient, true);
}
@@ -212,24 +204,20 @@ public class ConversationListItem extends RelativeLayout
this.recipient = messageResult.recipient;
this.glideRequests = glideRequests;
this.recipient.addListener(this);
fromView.setText(recipient, true);
subjectView.setText(getHighlightedSpan(locale, messageResult.bodySnippet, highlightSubstring));
dateView.setText(DateUtils.getBriefRelativeTimeSpanString(getContext(), locale, messageResult.receivedTimestampMs));
archivedView.setVisibility(GONE);
unreadIndicator.setVisibility(GONE);
deliveryStatusIndicator.setNone();
thumbnailView.setVisibility(GONE);
setBatchState(false);
setRippleColor(recipient);
setBgColor();
contactPhotoImage.setAvatar(glideRequests, recipient, true);
}
@Override
public void unbind() {
if (this.recipient != null) this.recipient.removeListener(this);
}
private void setBatchState(boolean batch) {
@@ -244,87 +232,76 @@ public class ConversationListItem extends RelativeLayout
return threadId;
}
public int getUnreadCount() {
return unreadCount;
public int getMsgId() {
return msgId;
}
public int getDistributionType() {
return distributionType;
public int getContactId() {
DcContext dcContext = DcHelper.getContext(getContext());
return dcContext.getMsg(msgId).getFromId();
}
public long getLastSeen() {
return lastSeen;
}
private void setThumbnailSnippet(ThreadRecord thread) {
if (thread.getSnippetUri() != null) {
this.thumbnailView.setVisibility(View.VISIBLE);
this.thumbnailView.setImageResource(glideRequests, thread.getSnippetUri());
LayoutParams subjectParams = (RelativeLayout.LayoutParams)this.subjectView.getLayoutParams();
subjectParams.addRule(RelativeLayout.LEFT_OF, R.id.thumbnail);
if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) {
subjectParams.addRule(RelativeLayout.START_OF, R.id.thumbnail);
}
this.subjectView.setLayoutParams(subjectParams);
this.post(new ThumbnailPositioner(thumbnailView, archivedView, deliveryStatusIndicator, dateView));
} else {
this.thumbnailView.setVisibility(View.GONE);
LayoutParams subjectParams = (RelativeLayout.LayoutParams)this.subjectView.getLayoutParams();
subjectParams.addRule(RelativeLayout.LEFT_OF, R.id.status);
if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) {
subjectParams.addRule(RelativeLayout.START_OF, R.id.status);
}
this.subjectView.setLayoutParams(subjectParams);
}
}
private void setStatusIcons(ThreadRecord thread) {
int state = dcSummary.getState();
if (state==DcMsg.DC_STATE_IN_FRESH || state==DcMsg.DC_STATE_IN_NOTICED) {
if (thread.isArchived())
{
// archived
this.archivedView.setVisibility(View.VISIBLE);
deliveryStatusIndicator.setNone();
} else if (state==DcMsg.DC_STATE_OUT_ERROR) {
deliveryStatusIndicator.setFailed();
} else {
if(state==DcMsg.DC_STATE_OUT_MDN_RCVD) {
deliveryStatusIndicator.setRead();
}
else if(state==DcMsg.DC_STATE_OUT_DELIVERED) {
deliveryStatusIndicator.setSent();
}
else if (state==DcMsg.DC_STATE_OUT_PENDING){
deliveryStatusIndicator.setPending();
}
else {
deliveryStatusIndicator.setNone();
}
}
}
private void setRippleColor(Recipient recipient) {
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
((RippleDrawable)(getBackground()).mutate())
.setColor(ColorStateList.valueOf(recipient.getColor().toConversationColor(getContext())));
}
}
private void setUnreadIndicator(ThreadRecord thread) {
if (thread.getUnreadCount() == 0) {
unreadIndicator.setVisibility(View.GONE);
return;
}
else
{
this.archivedView.setVisibility(View.GONE);
int state = dcSummary.getState();
if (state==DcMsg.DC_STATE_IN_FRESH || state==DcMsg.DC_STATE_IN_NOTICED)
{
// incoming
deliveryStatusIndicator.setNone();
int unreadCount = thread.getUnreadCount();
if(unreadCount==0) {
unreadIndicator.setVisibility(View.GONE);
}
else {
unreadIndicator.setImageDrawable(TextDrawable.builder()
.beginConfig()
.width(ViewUtil.dpToPx(getContext(), 24))
.height(ViewUtil.dpToPx(getContext(), 24))
.textColor(Color.WHITE)
.bold()
.endConfig()
.buildRound(String.valueOf(unreadCount), getResources().getColor(R.color.green_A700)));
unreadIndicator.setVisibility(View.VISIBLE);
}
}
else
{
// outgoing
unreadIndicator.setVisibility(View.GONE);
if (state == DcMsg.DC_STATE_OUT_ERROR) {
deliveryStatusIndicator.setFailed();
} else if (state == DcMsg.DC_STATE_OUT_MDN_RCVD) {
deliveryStatusIndicator.setRead();
} else if (state == DcMsg.DC_STATE_OUT_DELIVERED) {
deliveryStatusIndicator.setSent();
} else if (state == DcMsg.DC_STATE_OUT_PENDING) {
deliveryStatusIndicator.setPending();
} else {
deliveryStatusIndicator.setNone();
}
}
}
}
unreadIndicator.setImageDrawable(TextDrawable.builder()
.beginConfig()
.width(ViewUtil.dpToPx(getContext(), 24))
.height(ViewUtil.dpToPx(getContext(), 24))
.textColor(Color.WHITE)
.bold()
.endConfig()
.buildRound(String.valueOf(thread.getUnreadCount()), getResources().getColor(R.color.green_A700)));
unreadIndicator.setVisibility(View.VISIBLE);
private void setBgColor() {
if(threadId==DcChat.DC_CHAT_ID_DEADDROP) {
TypedArray ta = getContext().obtainStyledAttributes(new int[] { R.attr.conversation_list_deaddrop_bg_color});
setBackgroundColor(ta.getColor(0, 0xffffffff));
ta.recycle();
}
}
private Spanned getHighlightedSpan(@NonNull Locale locale,
@@ -364,50 +341,4 @@ public class ConversationListItem extends RelativeLayout
return spanned;
}
@Override
public void onModified(final Recipient recipient) {
Util.runOnMain(() -> {
fromView.setText(recipient, unreadCount == 0);
contactPhotoImage.setAvatar(glideRequests, recipient, true);
setRippleColor(recipient);
});
}
private static class ThumbnailPositioner implements Runnable {
private final View thumbnailView;
private final View archivedView;
private final View deliveryStatusView;
private final View dateView;
ThumbnailPositioner(View thumbnailView, View archivedView, View deliveryStatusView, View dateView) {
this.thumbnailView = thumbnailView;
this.archivedView = archivedView;
this.deliveryStatusView = deliveryStatusView;
this.dateView = dateView;
}
@Override
public void run() {
LayoutParams thumbnailParams = (RelativeLayout.LayoutParams)thumbnailView.getLayoutParams();
if (archivedView.getVisibility() == View.VISIBLE &&
(archivedView.getWidth() + deliveryStatusView.getWidth()) > dateView.getWidth())
{
thumbnailParams.addRule(RelativeLayout.LEFT_OF, R.id.status);
if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) {
thumbnailParams.addRule(RelativeLayout.START_OF, R.id.status);
}
} else {
thumbnailParams.addRule(RelativeLayout.LEFT_OF, R.id.date);
if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) {
thumbnailParams.addRule(RelativeLayout.START_OF, R.id.date);
}
}
thumbnailView.setLayoutParams(thumbnailParams);
}
}
}
@@ -42,7 +42,7 @@ public class ConversationListItemAction extends LinearLayout implements Bindable
}
@Override
public void bind(@NonNull ThreadRecord thread, @NonNull DcLot dcSummary, @NonNull GlideRequests glideRequests, @NonNull Locale locale, @NonNull Set<Long> selectedThreads, boolean batchMode) {
public void bind(@NonNull ThreadRecord thread, int msgId, @NonNull DcLot dcSummary, @NonNull GlideRequests glideRequests, @NonNull Locale locale, @NonNull Set<Long> selectedThreads, boolean batchMode) {
this.description.setText(thread.getRecipient().getName());
}
@@ -41,7 +41,7 @@ public class ConversationListItemInboxZero extends LinearLayout implements Binda
}
@Override
public void bind(@NonNull ThreadRecord thread, @NonNull DcLot dcSummary, @NonNull GlideRequests glideRequests, @NonNull Locale locale, @NonNull Set<Long> selectedThreads, boolean batchMode) {
public void bind(@NonNull ThreadRecord thread, int msgId, @NonNull DcLot dcSummary, @NonNull GlideRequests glideRequests, @NonNull Locale locale, @NonNull Set<Long> selectedThreads, boolean batchMode) {
}
}
@@ -82,7 +82,6 @@ public class ConversationPopupActivity extends ConversationActivity {
public void onSuccess(Long result) {
ActivityOptionsCompat transition = ActivityOptionsCompat.makeScaleUpAnimation(getWindow().getDecorView(), 0, 0, getWindow().getAttributes().width, getWindow().getAttributes().height);
Intent intent = new Intent(ConversationPopupActivity.this, ConversationActivity.class);
intent.putExtra(ConversationActivity.ADDRESS_EXTRA, getRecipient().getAddress());
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, result);
if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {
@@ -73,6 +73,10 @@ public class ConversationTitleView extends RelativeLayout {
this.verified.setVisibility(verified ? View.VISIBLE : View.GONE);
}
public void hideAvatar() {
avatar.setVisibility(View.GONE);
}
@Override
public void setOnClickListener(@Nullable OnClickListener listener) {
this.content.setOnClickListener(listener);
@@ -67,8 +67,6 @@ public class ConversationUpdateItem extends LinearLayout
this.title = findViewById(R.id.conversation_update_title);
this.body = findViewById(R.id.conversation_update_body);
this.date = findViewById(R.id.conversation_update_date);
this.setOnClickListener(new InternalClickListener(null));
}
@Override
@@ -202,58 +200,10 @@ public class ConversationUpdateItem extends LinearLayout
Util.runOnMain(() -> bind(messageRecord, locale));
}
@Override
public void setOnClickListener(View.OnClickListener l) {
super.setOnClickListener(new InternalClickListener(l));
}
@Override
public void unbind() {
if (sender != null) {
sender.removeListener(this);
}
}
private class InternalClickListener implements View.OnClickListener {
@Nullable private final View.OnClickListener parent;
InternalClickListener(@Nullable View.OnClickListener parent) {
this.parent = parent;
}
@Override
public void onClick(View v) {
if ((!messageRecord.isIdentityUpdate() &&
!messageRecord.isIdentityDefault() &&
!messageRecord.isIdentityVerified()) ||
!batchSelected.isEmpty())
{
if (parent != null) parent.onClick(v);
return;
}
final Recipient sender = ConversationUpdateItem.this.sender;
IdentityUtil.getRemoteIdentityKey(getContext(), sender).addListener(new ListenableFuture.Listener<Optional<IdentityRecord>>() {
@Override
public void onSuccess(Optional<IdentityRecord> result) {
if (result.isPresent()) {
Intent intent = new Intent(getContext(), VerifyIdentityActivity.class);
intent.putExtra(VerifyIdentityActivity.ADDRESS_EXTRA, sender.getAddress());
intent.putExtra(VerifyIdentityActivity.IDENTITY_EXTRA, new IdentityKeyParcelable(result.get().getIdentityKey()));
intent.putExtra(VerifyIdentityActivity.VERIFIED_EXTRA, result.get().getVerifiedStatus() == IdentityDatabase.VerifiedStatus.VERIFIED);
getContext().startActivity(intent);
}
}
@Override
public void onFailure(ExecutionException e) {
Log.w(TAG, e);
}
});
}
}
}
@@ -25,10 +25,12 @@ import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.SparseBooleanArray;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.WindowManager;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.ListView;
@@ -47,11 +49,11 @@ import org.thoughtcrime.securesms.contacts.ContactsCursorLoader.DisplayMode;
import org.thoughtcrime.securesms.contacts.avatars.ContactColors;
import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.FileUtils;
import org.thoughtcrime.securesms.util.SelectedRecipientsAdapter;
import org.thoughtcrime.securesms.util.SelectedRecipientsAdapter.OnRecipientDeletedListener;
import org.thoughtcrime.securesms.util.ViewUtil;
@@ -59,10 +61,14 @@ import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask;
import org.whispersystems.libsignal.util.guava.Optional;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import static com.b44t.messenger.DcContact.DC_CONTACT_ID_SELF;
/**
* Activity to create and update groups
*
@@ -93,7 +99,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
private TextView creatingText;
private Bitmap avatarBmp;
private CircularProgressButton verifyButton;
private boolean editGroup;
private Integer editGroupChatId = null;
@Override
protected void onPreCreate() {
@@ -109,8 +115,8 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
createVerified = getIntent().getBooleanExtra(GROUP_CREATE_VERIFIED_EXTRA, false);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeAsUpIndicator(R.drawable.ic_close_white_24dp);
initializeResources();
initializeExistingGroup();
initializeResources();
}
@Override
@@ -127,7 +133,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
groupName.setEnabled(true);
String title;
if(editGroup) {
if(isEdit()) {
title = getString(R.string.GroupCreateActivity_actionbar_edit_title);
}
else if(createVerified) {
@@ -173,8 +179,6 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
@Override
protected void onPostExecute(List<Result> results) {
if (activity.isFinishing()) return;
ApplicationDcContext dcContext = DcHelper.getContext(activity);
for (Result result : results) {
Recipient recipient = result.recipient.get();
Address address = recipient.getAddress();
@@ -201,16 +205,34 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
verifyButton.setOnClickListener(new ShowQrButtonListener());
verifyButton.setVisibility(View.VISIBLE);
}
avatar.setImageDrawable(new ResourceContactPhoto(R.drawable.ic_group_white_24dp).asDrawable(this, ContactColors.UNKNOWN_COLOR.toConversationColor(this)));
initializeAvatarView();
}
private void initializeAvatarView() {
boolean imageLoaded = false;
if (editGroupChatId != null) {
String avatarPath = dcContext.getChat(editGroupChatId).getProfileImage();
File avatarFile = new File(avatarPath);
if (avatarFile.exists()) {
imageLoaded = true;
GlideApp.with(this)
.load(avatarFile)
.circleCrop()
.into(avatar);
}
}
if (!imageLoaded) {
avatar.setImageDrawable(new ResourceContactPhoto(R.drawable.ic_group_white_24dp).asDrawable(this, ContactColors.UNKNOWN_COLOR.toConversationColor(this)));
}
avatar.setOnClickListener(view -> Crop.pickImage(GroupCreateActivity.this));
}
private void initializeExistingGroup() {
final Address groupAddress = getIntent().getParcelableExtra(GROUP_ADDRESS_EXTRA);
if (groupAddress != null) {
editGroup = true;
int chatId = groupAddress.getDcChatId();
new FillExistingGroupInfoAsyncTask(this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, chatId);
editGroupChatId = groupAddress.getDcChatId();
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
new FillExistingGroupInfoAsyncTask(this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, editGroupChatId);
}
}
@@ -232,7 +254,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
finish();
return true;
case R.id.menu_create_group:
if (editGroup) handleGroupUpdate();
if (isEdit()) handleGroupUpdate();
else handleGroupCreate();
return true;
}
@@ -248,49 +270,102 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
private void handleGroupCreate() {
String groupName = getGroupName();
if(groupName==null) {
Toast.makeText(this, getString(R.string.GroupCreateActivity_please_enter_group_name), Toast.LENGTH_LONG).show();
return;
}
if (showGroupNameEmptyToast(groupName)) return;
int chatId = dcContext.createGroupChat(createVerified, groupName);
Set<Recipient> members = getAdapter().getRecipients();
for(Recipient member : members) {
Address address = member.getAddress();
Set<Recipient> recipients = getAdapter().getRecipients();
for(Recipient recipient : recipients) {
Address address = recipient.getAddress();
if(address.isDcContact()) {
int contactId = address.getDcContactId();
dcContext.addContactToChat(chatId, contactId);
}
}
// TODO: handle avatarBmp
setGroupAvatar(chatId);
Intent intent = new Intent(this, ConversationActivity.class);
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, (long)chatId);
intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, ThreadDatabase.DistributionTypes.DEFAULT);
intent.putExtra(ConversationActivity.ADDRESS_EXTRA, Address.fromChat(chatId));
startActivity(intent);
finish();
}
private void handleGroupUpdate() {
int chatId = 0;// TODO: get correct id from groupToUpdate.get().id; or so
String groupName = getGroupName();
if(groupName!=null) {
dcContext.setChatName(chatId, groupName);
private void setGroupAvatar(int chatId) {
if(avatarBmp == null) {
return;
}
String avatarPath = dcContext.getChat(chatId).getProfileImage();
if (avatarPath != null && !avatarPath.isEmpty()) {
File oldImage = new File(avatarPath);
if (oldImage.exists()) {
//noinspection ResultOfMethodCallIgnored
oldImage.delete();
}
}
avatarPath = FileUtils.getFilePathForChatAvatar(this, chatId, System.currentTimeMillis());
FileOutputStream outStream;
try {
outStream = new FileOutputStream(avatarPath);
avatarBmp.compress(Bitmap.CompressFormat.JPEG, 85, outStream);
dcContext.setChatProfileImage(chatId, avatarPath);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
// TODO: compare dcContext.getChatContacts(chatId); against getAdapter().getRecipients();
// and add/remove contacts
private boolean showGroupNameEmptyToast(String groupName) {
if(groupName == null) {
Toast.makeText(this, getString(R.string.GroupCreateActivity_please_enter_group_name), Toast.LENGTH_LONG).show();
return true;
}
return false;
}
private void handleGroupUpdate() {
if(editGroupChatId == null) {
return;
}
String groupName = getGroupName();
if (showGroupNameEmptyToast(groupName)) {
return;
}
dcContext.setChatName(editGroupChatId, groupName);
updateGroupParticipants();
// TODO: handle avatarBmp
setGroupAvatar(editGroupChatId);
Intent intent = new Intent();
Recipient recipient = dcContext.getRecipient(ApplicationDcContext.RECIPIENT_TYPE_CHAT, editGroupChatId);
intent.putExtra(GroupCreateActivity.GROUP_ADDRESS_EXTRA, recipient.getAddress());
setResult(RESULT_OK, intent);
finish();
}
private void updateGroupParticipants() {
SparseBooleanArray currentChatContactIds = new SparseBooleanArray();
for(int chatContactId : dcContext.getChatContacts(editGroupChatId)) {
currentChatContactIds.put(chatContactId, chatContactId == DC_CONTACT_ID_SELF);
}
Set<Recipient> recipients = getAdapter().getRecipients();
for(Recipient recipient : recipients) {
Address address = recipient.getAddress();
if(address.isDcContact()) {
int contactId = address.getDcContactId();
if(currentChatContactIds.indexOfKey(contactId) < 0) {
dcContext.addContactToChat(editGroupChatId, contactId);
} else {
currentChatContactIds.put(contactId, true);
}
}
}
for(int index = 0; index < currentChatContactIds.size(); index++) {
if (!currentChatContactIds.valueAt(index)) {
dcContext.removeContactFromChat(editGroupChatId, currentChatContactIds.keyAt(index));
}
}
}
private SelectedRecipientsAdapter getAdapter() {
return (SelectedRecipientsAdapter)lv.getAdapter();
}
@@ -330,28 +405,33 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
new Crop(data.getData()).output(outputFile).asSquare().start(this);
break;
case Crop.REQUEST_CROP:
GlideApp.with(this)
.asBitmap()
.load(Crop.getOutput(data))
.skipMemoryCache(true)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.centerCrop()
.override(AVATAR_SIZE, AVATAR_SIZE)
.into(new SimpleTarget<Bitmap>() {
@Override
public void onResourceReady(@NonNull Bitmap resource, Transition<? super Bitmap> transition) {
setAvatar(Crop.getOutput(data), resource);
}
});
setAvatarView(data);
}
}
private void setAvatarView(Intent data) {
final Uri output = Crop.getOutput(data);
GlideApp.with(this)
.asBitmap()
.load(output)
.skipMemoryCache(true)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.centerCrop()
.override(AVATAR_SIZE, AVATAR_SIZE)
.into(new SimpleTarget<Bitmap>() {
@Override
public void onResourceReady(@NonNull Bitmap resource, Transition<? super Bitmap> transition) {
setAvatar(output, resource);
}
});
}
private class AddRecipientButtonListener implements View.OnClickListener {
@Override
public void onClick(View v) {
Intent intent = new Intent(GroupCreateActivity.this, ContactMultiSelectionActivity.class);
intent.putExtra(ContactSelectionListFragment.SELECT_VERIFIED_EXTRA, createVerified);
if (editGroup) {
if (isEdit()) {
intent.putExtra(ContactSelectionListFragment.DISPLAY_MODE, DisplayMode.FLAG_PUSH);
} else {
intent.putExtra(ContactSelectionListFragment.DISPLAY_MODE, DisplayMode.FLAG_PUSH | DisplayMode.FLAG_SMS);
@@ -383,8 +463,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
@Override
protected Recipient doInBackground(Integer... recipientIds) {
Integer recipientsId = recipientIds[0];
Recipient recipient = activity.dcContext.getRecipient(ApplicationDcContext.RECIPIENT_TYPE_CHAT, recipientsId);
return recipient;
return activity.dcContext.getRecipient(ApplicationDcContext.RECIPIENT_TYPE_CHAT, recipientsId);
}
@Override
@@ -397,6 +476,17 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
private void fllExistingGroup(Recipient recipient) {
List<Recipient> participants = recipient.getParticipants();
if (!isFinishing()) {
Recipient ownAddress = null;
for(Recipient participant : participants) {
if(participant.getAddress().getDcContactId() == DC_CONTACT_ID_SELF) {
ownAddress = participant;
} else {
addSelectedContacts(participant);
}
}
if (ownAddress != null) {
participants.remove(ownAddress);
}
groupName.setText(recipient.getName());
SelectedRecipientsAdapter adapter = new SelectedRecipientsAdapter(this, participants);
adapter.setOnRecipientDeletedListener(this);
@@ -415,19 +505,8 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
.into(avatar);
}
private static class GroupData {
String id;
Set<Recipient> recipients;
Bitmap avatarBmp;
byte[] avatarBytes;
String name;
public GroupData(String id, Set<Recipient> recipients, Bitmap avatarBmp, byte[] avatarBytes, String name) {
this.id = id;
this.recipients = recipients;
this.avatarBmp = avatarBmp;
this.avatarBytes = avatarBytes;
this.name = name;
}
private boolean isEdit() {
return editGroupChatId != null;
}
}
@@ -1,136 +0,0 @@
package org.thoughtcrime.securesms;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.AsyncTask;
import android.provider.ContactsContract;
import android.support.v7.app.AlertDialog;
import android.text.TextUtils;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.Util;
import java.util.LinkedList;
import java.util.List;
public class GroupMembersDialog extends AsyncTask<Void, Void, List<Recipient>> {
private static final String TAG = GroupMembersDialog.class.getSimpleName();
private final Recipient recipient;
private final Context context;
public GroupMembersDialog(Context context, Recipient recipient) {
this.recipient = recipient;
this.context = context;
}
@Override
public void onPreExecute() {}
@Override
protected List<Recipient> doInBackground(Void... params) {
return DatabaseFactory.getGroupDatabase(context).getGroupMembers(recipient.getAddress().toGroupString(), true);
}
@Override
public void onPostExecute(List<Recipient> members) {
GroupMembers groupMembers = new GroupMembers(members);
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.ConversationActivity_group_members);
builder.setIconAttribute(R.attr.group_members_dialog_icon);
builder.setCancelable(true);
builder.setItems(groupMembers.getRecipientStrings(), new GroupMembersOnClickListener(context, groupMembers));
builder.setPositiveButton(android.R.string.ok, null);
builder.show();
}
public void display() {
executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
private static class GroupMembersOnClickListener implements DialogInterface.OnClickListener {
private final GroupMembers groupMembers;
private final Context context;
public GroupMembersOnClickListener(Context context, GroupMembers members) {
this.context = context;
this.groupMembers = members;
}
@Override
public void onClick(DialogInterface dialogInterface, int item) {
Recipient recipient = groupMembers.get(item);
if (recipient.getContactUri() != null) {
Intent intent = new Intent(context, RecipientPreferenceActivity.class);
intent.putExtra(RecipientPreferenceActivity.ADDRESS_EXTRA, recipient.getAddress());
context.startActivity(intent);
} else {
final Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
if (recipient.getAddress().isEmail()) {
intent.putExtra(ContactsContract.Intents.Insert.EMAIL, recipient.getAddress().toEmailString());
} else {
intent.putExtra(ContactsContract.Intents.Insert.PHONE, recipient.getAddress().toPhoneString());
}
intent.setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE);
context.startActivity(intent);
}
}
}
/**
* Wraps a List of Recipient (just like @class Recipients),
* but with focus on the order of the Recipients.
* So that the order of the RecipientStrings[] matches
* the internal order.
*
* @author Christoph Haefner
*/
private class GroupMembers {
private final String TAG = GroupMembers.class.getSimpleName();
private final LinkedList<Recipient> members = new LinkedList<>();
public GroupMembers(List<Recipient> recipients) {
for (Recipient recipient : recipients) {
if (isLocalNumber(recipient)) {
members.push(recipient);
} else {
members.add(recipient);
}
}
}
public String[] getRecipientStrings() {
List<String> recipientStrings = new LinkedList<>();
for (Recipient recipient : members) {
if (isLocalNumber(recipient)) {
recipientStrings.add(context.getString(R.string.GroupMembersDialog_me));
} else {
String name = recipient.toShortString();
if (recipient.getName() == null && !TextUtils.isEmpty(recipient.getProfileName())) {
name += " ~" + recipient.getProfileName();
}
recipientStrings.add(name);
}
}
return recipientStrings.toArray(new String[members.size()]);
}
public Recipient get(int index) {
return members.get(index);
}
private boolean isLocalNumber(Recipient recipient) {
return Util.isOwnNumber(context, recipient.getAddress());
}
}
}
@@ -23,10 +23,10 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.b44t.messenger.DcMsg;
import com.codewaves.stickyheadergrid.StickyHeaderGridAdapter;
import org.thoughtcrime.securesms.components.ThumbnailView;
import org.thoughtcrime.securesms.database.MediaDatabase.MediaRecord;
import org.thoughtcrime.securesms.database.loaders.BucketedThreadMediaLoader.BucketedThreadMedia;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.mms.Slide;
@@ -46,7 +46,7 @@ class MediaGalleryAdapter extends StickyHeaderGridAdapter {
private final GlideRequests glideRequests;
private final Locale locale;
private final ItemClickListener itemClickListener;
private final Set<MediaRecord> selected;
private final Set<DcMsg> selected;
private BucketedThreadMedia media;
@@ -105,10 +105,10 @@ class MediaGalleryAdapter extends StickyHeaderGridAdapter {
@Override
public void onBindItemViewHolder(ItemViewHolder viewHolder, int section, int offset) {
MediaRecord mediaRecord = media.get(section, offset);
DcMsg mediaRecord = media.get(section, offset);
ThumbnailView thumbnailView = ((ViewHolder)viewHolder).imageView;
View selectedIndicator = ((ViewHolder)viewHolder).selectedIndicator;
Slide slide = MediaUtil.getSlideForAttachment(context, mediaRecord.getAttachment());
Slide slide = MediaUtil.getSlideForMsg(context, mediaRecord);
if (slide != null) {
thumbnailView.setImageResource(glideRequests, slide, false, false);
@@ -133,7 +133,7 @@ class MediaGalleryAdapter extends StickyHeaderGridAdapter {
return media.getSectionItemCount(section);
}
public void toggleSelection(@NonNull MediaRecord mediaRecord) {
public void toggleSelection(@NonNull DcMsg mediaRecord) {
if (!selected.remove(mediaRecord)) {
selected.add(mediaRecord);
}
@@ -145,7 +145,7 @@ class MediaGalleryAdapter extends StickyHeaderGridAdapter {
}
@NonNull
public Collection<MediaRecord> getSelectedMedia() {
public Collection<DcMsg> getSelectedMedia() {
return new HashSet<>(selected);
}
@@ -155,7 +155,7 @@ class MediaGalleryAdapter extends StickyHeaderGridAdapter {
}
interface ItemClickListener {
void onMediaClicked(@NonNull MediaRecord mediaRecord);
void onMediaLongClicked(MediaRecord mediaRecord);
void onMediaClicked(@NonNull DcMsg mediaRecord);
void onMediaLongClicked(DcMsg mediaRecord);
}
}
@@ -47,17 +47,18 @@ import android.view.ViewGroup;
import android.view.Window;
import android.widget.TextView;
import com.b44t.messenger.DcContext;
import com.b44t.messenger.DcMsg;
import com.codewaves.stickyheadergrid.StickyHeaderGridLayoutManager;
import org.thoughtcrime.securesms.connect.DcHelper;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter;
import org.thoughtcrime.securesms.database.MediaDatabase;
import org.thoughtcrime.securesms.database.loaders.BucketedThreadMediaLoader;
import org.thoughtcrime.securesms.database.loaders.BucketedThreadMediaLoader.BucketedThreadMedia;
import org.thoughtcrime.securesms.database.loaders.ThreadMediaLoader;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.AttachmentUtil;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
import org.thoughtcrime.securesms.util.DynamicTheme;
@@ -257,7 +258,7 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity
}
@Override
public void onMediaClicked(@NonNull MediaDatabase.MediaRecord mediaRecord) {
public void onMediaClicked(@NonNull DcMsg mediaRecord) {
if (actionMode != null) {
handleMediaMultiSelectClick(mediaRecord);
} else {
@@ -265,7 +266,7 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity
}
}
private void handleMediaMultiSelectClick(@NonNull MediaDatabase.MediaRecord mediaRecord) {
private void handleMediaMultiSelectClick(@NonNull DcMsg mediaRecord) {
MediaGalleryAdapter adapter = getListAdapter();
adapter.toggleSelection(mediaRecord);
@@ -277,8 +278,8 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity
}
}
private void handleMediaPreviewClick(@NonNull MediaDatabase.MediaRecord mediaRecord) {
if (mediaRecord.getAttachment().getDataUri() == null) {
private void handleMediaPreviewClick(@NonNull DcMsg mediaRecord) {
if (mediaRecord.getFile() == null) {
return;
}
@@ -288,18 +289,15 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity
}
Intent intent = new Intent(context, MediaPreviewActivity.class);
intent.putExtra(MediaPreviewActivity.DATE_EXTRA, mediaRecord.getDate());
intent.putExtra(MediaPreviewActivity.SIZE_EXTRA, mediaRecord.getAttachment().getSize());
intent.putExtra(MediaPreviewActivity.DC_MSG_ID, mediaRecord.getId());
intent.putExtra(MediaPreviewActivity.ADDRESS_EXTRA, recipient.getAddress());
intent.putExtra(MediaPreviewActivity.OUTGOING_EXTRA, mediaRecord.isOutgoing());
intent.putExtra(MediaPreviewActivity.LEFT_IS_RECENT_EXTRA, true);
intent.setDataAndType(mediaRecord.getAttachment().getDataUri(), mediaRecord.getContentType());
context.startActivity(intent);
}
@Override
public void onMediaLongClicked(MediaDatabase.MediaRecord mediaRecord) {
public void onMediaLongClicked(DcMsg mediaRecord) {
if (actionMode == null) {
((MediaGalleryAdapter) recyclerView.getAdapter()).toggleSelection(mediaRecord);
recyclerView.getAdapter().notifyDataSetChanged();
@@ -309,7 +307,7 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity
}
@SuppressLint("StaticFieldLeak")
private void handleDeleteMedia(@NonNull Collection<MediaDatabase.MediaRecord> mediaRecords) {
private void handleDeleteMedia(@NonNull Collection<DcMsg> mediaRecords) {
int recordCount = mediaRecords.size();
Resources res = getContext().getResources();
String confirmTitle = res.getQuantityString(R.plurals.MediaOverviewActivity_Media_delete_confirm_title,
@@ -324,25 +322,26 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity
builder.setTitle(confirmTitle);
builder.setMessage(confirmMessage);
builder.setCancelable(true);
final DcContext dcContext = DcHelper.getContext(getContext());
builder.setPositiveButton(R.string.delete, (dialogInterface, i) -> {
new ProgressDialogAsyncTask<MediaDatabase.MediaRecord, Void, Void>(getContext(),
new ProgressDialogAsyncTask<DcMsg, Void, Void>(getContext(),
R.string.MediaOverviewActivity_Media_delete_progress_title,
R.string.MediaOverviewActivity_Media_delete_progress_message)
{
@Override
protected Void doInBackground(MediaDatabase.MediaRecord... records) {
protected Void doInBackground(DcMsg... records) {
if (records == null || records.length == 0) {
return null;
}
for (MediaDatabase.MediaRecord record : records) {
AttachmentUtil.deleteAttachment(getContext(), record.getAttachment());
for (DcMsg record : records) {
dcContext.deleteMsgs(new int[]{record.getId()});
}
return null;
}
}.execute(mediaRecords.toArray(new MediaDatabase.MediaRecord[mediaRecords.size()]));
}.execute(mediaRecords.toArray(new DcMsg[mediaRecords.size()]));
});
builder.setNegativeButton(android.R.string.cancel, null);
builder.show();
@@ -20,9 +20,7 @@ import android.Manifest;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build.VERSION;
@@ -32,7 +30,6 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.support.v4.util.Pair;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AlertDialog;
@@ -48,21 +45,20 @@ import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.Toast;
import org.thoughtcrime.securesms.attachments.AttachmentId;
import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
import com.b44t.messenger.DcContext;
import com.b44t.messenger.DcMediaGalleryElement;
import com.b44t.messenger.DcMsg;
import org.thoughtcrime.securesms.components.MediaView;
import org.thoughtcrime.securesms.components.viewpager.ExtendedOnPageChangedListener;
import org.thoughtcrime.securesms.connect.DcHelper;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MediaDatabase.MediaRecord;
import org.thoughtcrime.securesms.database.loaders.PagingMediaLoader;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
import org.thoughtcrime.securesms.util.AttachmentUtil;
import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.SaveAttachmentTask;
@@ -75,22 +71,29 @@ import java.util.WeakHashMap;
/**
* Activity for displaying media attachments in-app
*/
public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity implements RecipientModifiedListener, LoaderManager.LoaderCallbacks<Pair<Cursor, Integer>> {
public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity
implements RecipientModifiedListener, LoaderManager.LoaderCallbacks<DcMediaGalleryElement> {
private final static String TAG = MediaPreviewActivity.class.getSimpleName();
public static final String ADDRESS_EXTRA = "address";
public static final String DATE_EXTRA = "date";
public static final String SIZE_EXTRA = "size";
public static final String OUTGOING_EXTRA = "outgoing";
public static final String LEFT_IS_RECENT_EXTRA = "left_is_recent";
public static final String DC_MSG_ID = "dc_msg_id";
/** USE ONLY IF YOU HAVE NO MESSAGE ID! */
public static final String DATE_EXTRA = "date";
/** USE ONLY IF YOU HAVE NO MESSAGE ID! */
public static final String SIZE_EXTRA = "size";
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
@Nullable
private DcMsg messageRecord;
private DcContext dcContext;
private MediaItem initialMedia;
private ViewPager mediaPager;
private Uri initialMediaUri;
private String initialMediaType;
private long initialMediaSize;
private Recipient conversationRecipient;
private boolean leftIsRecent;
@@ -181,32 +184,51 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
private void initializeResources() {
Address address = getIntent().getParcelableExtra(ADDRESS_EXTRA);
initialMediaUri = getIntent().getData();
initialMediaType = getIntent().getType();
initialMediaSize = getIntent().getLongExtra(SIZE_EXTRA, 0);
final Context context = getApplicationContext();
this.dcContext = DcHelper.getContext(context);
final int msgId = getIntent().getIntExtra(DC_MSG_ID, DcMsg.DC_MSG_NO_ID);
if(msgId == DcMsg.DC_MSG_NO_ID) {
messageRecord = null;
long date = getIntent().getLongExtra(DATE_EXTRA, 0);
long size = getIntent().getLongExtra(SIZE_EXTRA, 0);
initialMedia = new MediaItem(null, getIntent().getData(), getIntent().getType(),
DcMsg.DC_MSG_NO_ID, date, size, false);
if (address != null) {
conversationRecipient = Recipient.from(context, address, false);
} else {
conversationRecipient = null;
}
} else {
messageRecord = dcContext.getMsg(msgId);
initialMedia = new MediaItem(Recipient.from(context, msgId), Uri.fromFile(messageRecord.getFileAsFile()),
messageRecord.getFilemime(), messageRecord.getId(), messageRecord.getDateReceived(),
messageRecord.getFilebytes(), messageRecord.isOutgoing());
conversationRecipient = Recipient.from(context, msgId);
}
leftIsRecent = getIntent().getBooleanExtra(LEFT_IS_RECENT_EXTRA, false);
restartItem = -1;
if (address != null) {
conversationRecipient = Recipient.from(this, address, true);
} else {
conversationRecipient = null;
}
}
private void initializeMedia() {
if (!isContentTypeSupported(initialMediaType)) {
// if you search for the place where the media are loaded, go to 'onCreateLoader'.
if (!isContentTypeSupported(initialMedia.type)) {
Log.w(TAG, "Unsupported media type sent to MediaPreviewActivity, finishing.");
Toast.makeText(getApplicationContext(), R.string.MediaPreviewActivity_unssuported_media_type, Toast.LENGTH_LONG).show();
finish();
}
Log.w(TAG, "Loading Part URI: " + initialMediaUri);
Log.w(TAG, "Loading Part URI: " + initialMedia);
if (conversationRecipient != null) {
if (messageRecord != null) {
getSupportLoaderManager().restartLoader(0, null, this);
} else {
mediaPager.setAdapter(new SingleItemPagerAdapter(this, GlideApp.with(this), getWindow(), initialMediaUri, initialMediaType, initialMediaSize));
mediaPager.setAdapter(new SingleItemPagerAdapter(this, GlideApp.with(this),
getWindow(), initialMedia.uri, initialMedia.type, initialMedia.size));
}
}
@@ -261,7 +283,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
@SuppressLint("StaticFieldLeak")
private void deleteMedia() {
MediaItem mediaItem = getCurrentMediaItem();
if (mediaItem == null || mediaItem.attachment == null) {
if (mediaItem == null || mediaItem.msgId == DcMsg.DC_MSG_NO_ID) {
return;
}
@@ -275,11 +297,10 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... voids) {
if (mediaItem.attachment == null) {
if (mediaItem.msgId == DcMsg.DC_MSG_NO_ID) {
return null;
}
AttachmentUtil.deleteAttachment(MediaPreviewActivity.this.getApplicationContext(),
mediaItem.attachment);
dcContext.deleteMsgs(new int[]{mediaItem.msgId});
return null;
}
}.execute();
@@ -340,25 +361,26 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
}
@Override
public Loader<Pair<Cursor, Integer>> onCreateLoader(int id, Bundle args) {
return new PagingMediaLoader(this, conversationRecipient, initialMediaUri, leftIsRecent);
public Loader<DcMediaGalleryElement> onCreateLoader(int id, Bundle args) {
return new PagingMediaLoader(this, messageRecord, false);
}
@Override
public void onLoadFinished(Loader<Pair<Cursor, Integer>> loader, @Nullable Pair<Cursor, Integer> data) {
public void onLoadFinished(Loader<DcMediaGalleryElement> loader, @Nullable DcMediaGalleryElement data) {
if (data != null) {
@SuppressWarnings("ConstantConditions")
CursorPagerAdapter adapter = new CursorPagerAdapter(this, GlideApp.with(this), getWindow(), data.first, data.second, leftIsRecent);
DcMediaPagerAdapter adapter = new DcMediaPagerAdapter(this, GlideApp.with(this),
getWindow(), data, leftIsRecent);
mediaPager.setAdapter(adapter);
adapter.setActive(true);
if (restartItem < 0) mediaPager.setCurrentItem(data.second);
if (restartItem < 0) mediaPager.setCurrentItem(data.getPosition());
else mediaPager.setCurrentItem(restartItem);
}
}
@Override
public void onLoaderReset(Loader<Pair<Cursor, Integer>> loader) {
public void onLoaderReset(Loader<DcMediaGalleryElement> loader) {
}
@@ -450,7 +472,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
@Override
public MediaItem getMediaItemFor(int position) {
return new MediaItem(null, null, uri, mediaType, -1, true);
return new MediaItem(null, uri, mediaType, DcMsg.DC_MSG_NO_ID, -1, -1, true);
}
@Override
@@ -459,29 +481,29 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
}
}
private static class CursorPagerAdapter extends PagerAdapter implements MediaItemAdapter {
private static class DcMediaPagerAdapter extends PagerAdapter implements MediaItemAdapter {
private final WeakHashMap<Integer, MediaView> mediaViews = new WeakHashMap<>();
private final Context context;
private final GlideRequests glideRequests;
private final Window window;
private final Cursor cursor;
private final DcMediaGalleryElement gallery;
private final boolean leftIsRecent;
private boolean active;
private int autoPlayPosition;
CursorPagerAdapter(@NonNull Context context, @NonNull GlideRequests glideRequests,
@NonNull Window window, @NonNull Cursor cursor, int autoPlayPosition,
DcMediaPagerAdapter(@NonNull Context context, @NonNull GlideRequests glideRequests,
@NonNull Window window, @NonNull DcMediaGalleryElement gallery,
boolean leftIsRecent)
{
this.context = context.getApplicationContext();
this.glideRequests = glideRequests;
this.window = window;
this.cursor = cursor;
this.autoPlayPosition = autoPlayPosition;
this.gallery = gallery;
this.leftIsRecent = leftIsRecent;
this.autoPlayPosition = gallery.getPosition();
}
public void setActive(boolean active) {
@@ -492,7 +514,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
@Override
public int getCount() {
if (!active) return 0;
else return cursor.getCount();
else return gallery.getCount();
}
@Override
@@ -509,13 +531,14 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
autoPlayPosition = -1;
cursor.moveToPosition(cursorPosition);
gallery.moveToPosition(cursorPosition);
MediaRecord mediaRecord = MediaRecord.from(context, cursor);
DcMsg msg = gallery.getMessage();
try {
//noinspection ConstantConditions
mediaView.set(glideRequests, window, mediaRecord.getAttachment().getDataUri(), mediaRecord.getAttachment().getContentType(), mediaRecord.getAttachment().getSize(), autoplay);
mediaView.set(glideRequests, window, Uri.fromFile(msg.getFileAsFile()),
msg.getFilemime(), msg.getFilebytes(), autoplay);
} catch (IOException e) {
Log.w(TAG, e);
}
@@ -536,18 +559,18 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
}
public MediaItem getMediaItemFor(int position) {
cursor.moveToPosition(getCursorPosition(position));
MediaRecord mediaRecord = MediaRecord.from(context, cursor);
Address address = mediaRecord.getAddress();
gallery.moveToPosition(getCursorPosition(position));
DcMsg msg = gallery.getMessage();
if (mediaRecord.getAttachment().getDataUri() == null) throw new AssertionError();
if (msg.getFile() == null) throw new AssertionError();
return new MediaItem(address != null ? Recipient.from(context, address,true) : null,
mediaRecord.getAttachment(),
mediaRecord.getAttachment().getDataUri(),
mediaRecord.getContentType(),
mediaRecord.getDate(),
mediaRecord.isOutgoing());
return new MediaItem(Recipient.from(context, msg.getId()),
Uri.fromFile(msg.getFileAsFile()),
msg.getFilemime(),
msg.getId(),
msg.getDateReceived(),
msg.getFilebytes(),
msg.isOutgoing());
}
@Override
@@ -558,30 +581,33 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
private int getCursorPosition(int position) {
if (leftIsRecent) return position;
else return cursor.getCount() - 1 - position;
else return gallery.getCount() - 1 - position;
}
}
private static class MediaItem {
private final @Nullable Recipient recipient;
private final @Nullable DatabaseAttachment attachment;
private final @NonNull Uri uri;
private final @NonNull String type;
private final int msgId;
private final long date;
private final long size;
private final boolean outgoing;
private MediaItem(@Nullable Recipient recipient,
@Nullable DatabaseAttachment attachment,
@NonNull Uri uri,
@NonNull String type,
int msgId,
long date,
long size,
boolean outgoing)
{
this.recipient = recipient;
this.attachment = attachment;
this.uri = uri;
this.type = type;
this.msgId = msgId;
this.date = date;
this.size = size;
this.outgoing = outgoing;
}
}
@@ -98,12 +98,10 @@ public class NewConversationActivity extends ContactSelectionActivity {
final DcContext dcContext = DcHelper.getContext(this);
Intent intent = new Intent(this, ConversationActivity.class);
intent.putExtra(ConversationActivity.ADDRESS_EXTRA, Address.fromChat(chatId));
intent.putExtra(ConversationActivity.TEXT_EXTRA, getIntent().getStringExtra(ConversationActivity.TEXT_EXTRA));
intent.setDataAndType(getIntent().getData(), getIntent().getType());
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, (long)chatId);
intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, ThreadDatabase.DistributionTypes.DEFAULT);
startActivity(intent);
finish();
}
@@ -167,8 +167,11 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
Intent intent = new Intent(RecipientPreferenceActivity.this, MediaPreviewActivity.class);
intent.putExtra(MediaPreviewActivity.ADDRESS_EXTRA, address);
intent.putExtra(MediaPreviewActivity.OUTGOING_EXTRA, mediaRecord.isOutgoing());
intent.putExtra(MediaPreviewActivity.DATE_EXTRA, mediaRecord.getDate());
intent.putExtra(MediaPreviewActivity.SIZE_EXTRA, mediaRecord.getAttachment().getSize());
Log.e(TAG, "MEDIA PREVIEW NOT IMPLEMENTED HERE must have dcMsgId in Media Record first!");
if(true)
throw new IllegalStateException("Media Preview not implemented");
/* intent.putExtra(MediaPreviewActivity.DATE_EXTRA, mediaRecord.getDate());
intent.putExtra(MediaPreviewActivity.SIZE_EXTRA, mediaRecord.getAttachment().getSize());*/
intent.putExtra(MediaPreviewActivity.LEFT_IS_RECENT_EXTRA, ViewCompat.getLayoutDirection(threadPhotoRailView) == ViewCompat.LAYOUT_DIRECTION_LTR);
intent.setDataAndType(mediaRecord.getAttachment().getDataUri(), mediaRecord.getContentType());
startActivity(intent);
@@ -192,13 +195,13 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
}
private void setHeader(@NonNull Recipient recipient) {
glideRequests.load(recipient.getContactPhoto())
glideRequests.load(recipient.getContactPhoto(this))
.fallback(recipient.getFallbackContactPhoto().asCallCard(this))
.error(recipient.getFallbackContactPhoto().asCallCard(this))
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(this.avatar);
if (recipient.getContactPhoto() == null) this.avatar.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
if (recipient.getContactPhoto(this) == null) this.avatar.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
else this.avatar.setScaleType(ImageView.ScaleType.CENTER_CROP);
this.avatar.setBackgroundColor(recipient.getColor().toActionBarColor(this));
@@ -240,15 +240,13 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
contactsFragment.getView().setVisibility(View.VISIBLE);
progressWheel.setVisibility(View.GONE);
} else {
createConversation(threadId, address, distributionType);
createConversation(threadId);
}
}
private void createConversation(long threadId, Address address, int distributionType) {
private void createConversation(long threadId) {
final Intent intent = getBaseShareIntent(ConversationActivity.class);
intent.putExtra(ConversationActivity.ADDRESS_EXTRA, address);
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId);
intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, distributionType);
isPassingAlongMedia = true;
startActivity(intent);
@@ -275,7 +273,7 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
public void onContactSelected(int specialId, String number) {
Recipient recipient = Recipient.from(this, Address.fromExternal(this, number), true);
long existingThread = DatabaseFactory.getThreadDatabase(this).getThreadIdIfExistsFor(recipient);
createConversation(existingThread, recipient.getAddress(), ThreadDatabase.DistributionTypes.DEFAULT);
createConversation(existingThread);
}
@Override
@@ -1,19 +1,17 @@
package org.thoughtcrime.securesms.components;
import android.content.Context;
import android.content.Intent;
import android.content.res.TypedArray;
import android.provider.ContactsContract;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.widget.AppCompatImageView;
import android.util.AttributeSet;
import android.view.View;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.contacts.avatars.ContactColors;
import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto;
import org.thoughtcrime.securesms.contacts.avatars.GeneratedContactPhoto;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.recipients.Recipient;
@@ -49,7 +47,8 @@ public class AvatarImageView extends AppCompatImageView {
public void setAvatar(@NonNull GlideRequests requestManager, @Nullable Recipient recipient, boolean quickContactEnabled) {
if (recipient != null) {
requestManager.load(recipient.getContactPhoto())
ContactPhoto contactPhoto = recipient.getContactPhoto(getContext());
requestManager.load(contactPhoto)
.fallback(recipient.getFallbackContactPhotoDrawable(getContext(), inverted))
.error(recipient.getFallbackContactPhotoDrawable(getContext(), inverted))
.diskCacheStrategy(DiskCacheStrategy.ALL)
@@ -293,7 +293,7 @@ public class WebRtcCallScreen extends FrameLayout implements RecipientModifiedLi
this.recipient.addListener(this);
GlideApp.with(getContext().getApplicationContext())
.load(recipient.getContactPhoto())
.load(recipient.getContactPhoto(getContext()))
.fallback(recipient.getFallbackContactPhoto().asCallCard(getContext()))
.error(recipient.getFallbackContactPhoto().asCallCard(getContext()))
.diskCacheStrategy(DiskCacheStrategy.ALL)
@@ -415,6 +415,7 @@ public class ApplicationDcContext extends DcContext {
case DC_EVENT_GET_STRING:
String s;
switch( (int)data1 ) { // the integers are defined in the core and used only here, an enum or sth. like that won't have a big benefit
case 8: s = context.getString(R.string.menu_deaddrop); break;
case 13: s = context.getString(R.string.default_status_text); break;
case 42: s = context.getString(R.string.autocrypt__asm_subject); break;
case 43: s = context.getString(R.string.autocrypt__asm_general_body); break;
@@ -0,0 +1,4 @@
package org.thoughtcrime.securesms;
public class IncomingMessageNotifier {
}
@@ -6,37 +6,32 @@ import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import org.thoughtcrime.securesms.connect.DcHelper;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.util.Conversions;
import org.whispersystems.libsignal.util.guava.Optional;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
public class GroupRecordContactPhoto implements ContactPhoto {
private final @NonNull Address address;
private final long avatarId;
private final int chatId;
public GroupRecordContactPhoto(@NonNull Address address, long avatarId) {
this.address = address;
this.avatarId = avatarId;
private final Address address;
private final String path;
public GroupRecordContactPhoto(Context context, Address address) {
this.address = address;
chatId = address.getDcChatId();
path = DcHelper.getContext(context).getChat(chatId).getProfileImage();
}
@Override
public InputStream openInputStream(Context context) throws IOException {
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
Optional<GroupDatabase.GroupRecord> groupRecord = groupDatabase.getGroup(address.toGroupString());
if (groupRecord.isPresent() && groupRecord.get().getAvatar() != null) {
return new ByteArrayInputStream(groupRecord.get().getAvatar());
}
throw new IOException("Couldn't load avatar for group: " + address.toGroupString());
return new FileInputStream(path);
}
@Override
@@ -52,7 +47,8 @@ public class GroupRecordContactPhoto implements ContactPhoto {
@Override
public void updateDiskCacheKey(MessageDigest messageDigest) {
messageDigest.update(address.serialize().getBytes());
messageDigest.update(Conversions.longToByteArray(avatarId));
messageDigest.update(Conversions.longToByteArray(chatId));
messageDigest.update(path.getBytes());
}
@Override
@@ -60,12 +56,12 @@ public class GroupRecordContactPhoto implements ContactPhoto {
if (other == null || !(other instanceof GroupRecordContactPhoto)) return false;
GroupRecordContactPhoto that = (GroupRecordContactPhoto)other;
return this.address.equals(that.address) && this.avatarId == that.avatarId;
return this.address.equals(that.address) && this.chatId == that.chatId && this.path.equals(that.path);
}
@Override
public int hashCode() {
return this.address.hashCode() ^ (int) avatarId;
return this.address.hashCode() ^ chatId;
}
}
@@ -288,7 +288,7 @@ public class ContactRepository {
@WorkerThread
private @Nullable AvatarInfo getRecipientAvatarInfo(@NonNull Address address) {
Recipient recipient = Recipient.from(context, address, false);
ContactPhoto contactPhoto = recipient.getContactPhoto();
ContactPhoto contactPhoto = recipient.getContactPhoto(context);
if (contactPhoto != null) {
Uri avatarUri = contactPhoto.getUri(context);
@@ -47,6 +47,9 @@ public class Address implements Parcelable, Comparable<Address> {
public static final Address UNKNOWN = new Address("Unknown");
private final static String DC_CHAT_PREFIX = "dc:";
private final static String DC_CONTACT_PREFIX = "dcc:";
private static final String TAG = Address.class.getSimpleName();
private static final AtomicReference<Pair<String, ExternalAddressFormatter>> cachedFormatter = new AtomicReference<>();
@@ -54,11 +57,11 @@ public class Address implements Parcelable, Comparable<Address> {
private final String address;
public static Address fromChat(int chatId) {
return new Address("dc:" + chatId);
return new Address(DC_CHAT_PREFIX + chatId);
}
public static Address fromContact(int contactId) {
return new Address("dcc:" + contactId);
return new Address(DC_CONTACT_PREFIX + contactId);
}
private Address(@NonNull String address) {
@@ -134,9 +137,9 @@ public class Address implements Parcelable, Comparable<Address> {
return !isGroup() && !isEmail();
}
public boolean isDcChat() { return address.startsWith("dc:"); };
public boolean isDcChat() { return address.startsWith(DC_CHAT_PREFIX); };
public boolean isDcContact() { return address.startsWith("dcc:"); };
public boolean isDcContact() { return address.startsWith(DC_CONTACT_PREFIX); };
public @NonNull String toGroupString() {
if (!isGroup()) throw new AssertionError("Not group: " + address);
@@ -155,12 +158,12 @@ public class Address implements Parcelable, Comparable<Address> {
public int getDcChatId() {
if(!isDcChat()) throw new AssertionError("Not dc chat: " + address);
return Integer.valueOf(address.substring("dc:".length()));
return Integer.valueOf(address.substring(DC_CHAT_PREFIX.length()));
}
public int getDcContactId() {
if(!isDcContact()) throw new AssertionError("Not dc contact: " + address);
return Integer.valueOf(address.substring("dcc:".length()));
return Integer.valueOf(address.substring(DC_CONTACT_PREFIX.length()));
}
@Override
@@ -2,19 +2,16 @@ package org.thoughtcrime.securesms.database.loaders;
import android.content.Context;
import android.database.ContentObserver;
import android.database.Cursor;
import android.support.annotation.NonNull;
import android.support.v4.content.AsyncTaskLoader;
import com.annimon.stream.Stream;
import com.b44t.messenger.DcContext;
import com.b44t.messenger.DcMsg;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.connect.DcHelper;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.Database;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MediaDatabase;
import org.thoughtcrime.securesms.recipients.Recipient;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
@@ -33,12 +30,10 @@ public class BucketedThreadMediaLoader extends AsyncTaskLoader<BucketedThreadMed
private static final String TAG = BucketedThreadMediaLoader.class.getSimpleName();
private final Address address;
private final ContentObserver observer;
public BucketedThreadMediaLoader(@NonNull Context context, @NonNull Address address) {
super(context);
this.address = address;
this.observer = new ForceLoadContentObserver();
onContentChanged();
}
@@ -57,19 +52,15 @@ public class BucketedThreadMediaLoader extends AsyncTaskLoader<BucketedThreadMed
@Override
protected void onAbandon() {
DatabaseFactory.getMediaDatabase(getContext()).unsubscribeToMediaChanges(observer);
}
@Override
public BucketedThreadMedia loadInBackground() {
BucketedThreadMedia result = new BucketedThreadMedia(getContext());
long threadId = DatabaseFactory.getThreadDatabase(getContext()).getThreadIdFor(Recipient.from(getContext(), address, true));
DatabaseFactory.getMediaDatabase(getContext()).subscribeToMediaChanges(observer);
try (Cursor cursor = DatabaseFactory.getMediaDatabase(getContext()).getGalleryMediaForThread(threadId)) {
while (cursor != null && cursor.moveToNext()) {
result.add(MediaDatabase.MediaRecord.from(getContext(), cursor));
}
DcContext context = DcHelper.getContext(getContext());
int[] messages = context.getChatMedia(address.getDcChatId(), DcMsg.DC_MSG_GIF, DcMsg.DC_MSG_IMAGE);
for(int nextId : messages) {
result.add(context.getMsg(nextId));
}
return result;
@@ -86,7 +77,7 @@ public class BucketedThreadMediaLoader extends AsyncTaskLoader<BucketedThreadMed
private final TimeBucket[] TIME_SECTIONS;
public BucketedThreadMedia(@NonNull Context context) {
this.TODAY = new TimeBucket(context.getString(R.string.BucketedThreadMedia_Today), TimeBucket.addToCalendar(Calendar.DAY_OF_YEAR, -1), TimeBucket.addToCalendar(Calendar.DAY_OF_YEAR, 1000));
this.TODAY = new TimeBucket(context.getString(R.string.BucketedThreadMedia_Today), TimeBucket.addToCalendar(Calendar.DAY_OF_YEAR, -1), TimeBucket.addToCalendar(Calendar.YEAR, 1000));
this.YESTERDAY = new TimeBucket(context.getString(R.string.BucketedThreadMedia_Yesterday), TimeBucket.addToCalendar(Calendar.DAY_OF_YEAR, -2), TimeBucket.addToCalendar(Calendar.DAY_OF_YEAR, -1));
this.THIS_WEEK = new TimeBucket(context.getString(R.string.BucketedThreadMedia_This_week), TimeBucket.addToCalendar(Calendar.DAY_OF_YEAR, -7), TimeBucket.addToCalendar(Calendar.DAY_OF_YEAR, -2));
this.THIS_MONTH = new TimeBucket(context.getString(R.string.BucketedThreadMedia_This_month), TimeBucket.addToCalendar(Calendar.DAY_OF_YEAR, -30), TimeBucket.addToCalendar(Calendar.DAY_OF_YEAR, -7));
@@ -94,16 +85,14 @@ public class BucketedThreadMediaLoader extends AsyncTaskLoader<BucketedThreadMed
this.OLDER = new MonthBuckets();
}
public void add(MediaDatabase.MediaRecord mediaRecord) {
public void add(DcMsg imageMessage) {
for (TimeBucket timeSection : TIME_SECTIONS) {
if (timeSection.inRange(mediaRecord.getDate())) {
timeSection.add(mediaRecord);
if (timeSection.inRange(imageMessage.getTimestamp())) {
timeSection.add(imageMessage);
return;
}
}
OLDER.add(mediaRecord);
OLDER.add(imageMessage);
}
public int getSectionCount() {
@@ -120,7 +109,7 @@ public class BucketedThreadMediaLoader extends AsyncTaskLoader<BucketedThreadMed
else return OLDER.getSectionItemCount(section - activeTimeBuckets.size());
}
public MediaDatabase.MediaRecord get(int section, int item) {
public DcMsg get(int section, int item) {
List<TimeBucket> activeTimeBuckets = Stream.of(TIME_SECTIONS).filter(timeBucket -> !timeBucket.isEmpty()).toList();
if (section < activeTimeBuckets.size()) return activeTimeBuckets.get(section).getItem(item);
@@ -136,24 +125,24 @@ public class BucketedThreadMediaLoader extends AsyncTaskLoader<BucketedThreadMed
private static class TimeBucket {
private final List<MediaDatabase.MediaRecord> records = new LinkedList<>();
private final List<DcMsg> records = new LinkedList<>();
private final long startTime;
private final long endtime;
private final long endTime;
private final String name;
TimeBucket(String name, long startTime, long endtime) {
TimeBucket(String name, long startTime, long endTime) {
this.name = name;
this.startTime = startTime;
this.endtime = endtime;
this.endTime = endTime;
}
void add(MediaDatabase.MediaRecord record) {
void add(DcMsg record) {
this.records.add(record);
}
boolean inRange(long timestamp) {
return timestamp > startTime && timestamp <= endtime;
return timestamp > startTime && timestamp <= endTime;
}
boolean isEmpty() {
@@ -164,7 +153,7 @@ public class BucketedThreadMediaLoader extends AsyncTaskLoader<BucketedThreadMed
return records.size();
}
MediaDatabase.MediaRecord getItem(int position) {
DcMsg getItem(int position) {
return records.get(position);
}
@@ -181,11 +170,11 @@ public class BucketedThreadMediaLoader extends AsyncTaskLoader<BucketedThreadMed
private static class MonthBuckets {
private final Map<Date, List<MediaDatabase.MediaRecord>> months = new HashMap<>();
private final Map<Date, List<DcMsg>> months = new HashMap<>();
void add(MediaDatabase.MediaRecord record) {
void add(DcMsg record) {
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(record.getDate());
calendar.setTimeInMillis(record.getTimestamp());
int year = calendar.get(Calendar.YEAR) - 1900;
int month = calendar.get(Calendar.MONTH);
@@ -194,7 +183,7 @@ public class BucketedThreadMediaLoader extends AsyncTaskLoader<BucketedThreadMed
if (months.containsKey(date)) {
months.get(date).add(record);
} else {
List<MediaDatabase.MediaRecord> list = new LinkedList<>();
List<DcMsg> list = new LinkedList<>();
list.add(record);
months.put(date, list);
}
@@ -208,7 +197,7 @@ public class BucketedThreadMediaLoader extends AsyncTaskLoader<BucketedThreadMed
return months.get(getSection(section)).size();
}
MediaDatabase.MediaRecord getItem(int section, int position) {
DcMsg getItem(int section, int position) {
return months.get(getSection(section)).get(position);
}
@@ -2,50 +2,50 @@ package org.thoughtcrime.securesms.database.loaders;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.util.Pair;
import android.util.Log;
import org.thoughtcrime.securesms.attachments.AttachmentId;
import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.mms.PartAuthority;
import org.thoughtcrime.securesms.recipients.Recipient;
import com.b44t.messenger.DcContext;
import com.b44t.messenger.DcMediaGalleryElement;
import com.b44t.messenger.DcMsg;
import org.thoughtcrime.securesms.connect.DcHelper;
import org.thoughtcrime.securesms.util.AsyncLoader;
public class PagingMediaLoader extends AsyncLoader<Pair<Cursor, Integer>> {
public class PagingMediaLoader extends AsyncLoader<DcMediaGalleryElement> {
@SuppressWarnings("unused")
private static final String TAG = PagingMediaLoader.class.getSimpleName();
private final Recipient recipient;
private final Uri uri;
private final DcMsg msg;
private final boolean leftIsRecent;
public PagingMediaLoader(@NonNull Context context, @NonNull Recipient recipient, @NonNull Uri uri, boolean leftIsRecent) {
public PagingMediaLoader(@NonNull Context context, @NonNull DcMsg msg, boolean leftIsRecent) {
super(context);
this.recipient = recipient;
this.uri = uri;
this.msg = msg;
this.leftIsRecent = leftIsRecent;
}
@Nullable
@Override
public Pair<Cursor, Integer> loadInBackground() {
long threadId = DatabaseFactory.getThreadDatabase(getContext()).getThreadIdFor(recipient);
Cursor cursor = DatabaseFactory.getMediaDatabase(getContext()).getGalleryMediaForThread(threadId);
while (cursor != null && cursor.moveToNext()) {
AttachmentId attachmentId = new AttachmentId(cursor.getLong(cursor.getColumnIndexOrThrow(AttachmentDatabase.ROW_ID)), cursor.getLong(cursor.getColumnIndexOrThrow(AttachmentDatabase.UNIQUE_ID)));
Uri attachmentUri = PartAuthority.getAttachmentDataUri(attachmentId);
if (attachmentUri.equals(uri)) {
return new Pair<>(cursor, leftIsRecent ? cursor.getPosition() : cursor.getCount() - 1 - cursor.getPosition());
public DcMediaGalleryElement loadInBackground() {
DcContext context = DcHelper.getContext(getContext());
int[] mediaMessages = context.getChatMedia(msg.getChatId(), DcMsg.DC_MSG_GIF, DcMsg.DC_MSG_IMAGE);
// first id is the oldest message.
int currentIndex = -1;
for(int ii = 0; ii < mediaMessages.length; ii++) {
if(mediaMessages[ii] == msg.getId()) {
currentIndex = ii;
break;
}
}
return null;
if(currentIndex == -1) {
currentIndex = 0;
DcMsg unfound = context.getMsg(msg.getId());
Log.d(TAG, "did not find message in list: " + unfound.getId() + " / " + unfound.getFile() + " / " + unfound.getText());
}
DcMediaGalleryElement retVal = new DcMediaGalleryElement(mediaMessages, currentIndex, context, leftIsRecent);
return retVal;
}
}
@@ -486,9 +486,8 @@ public class AttachmentManager {
if (MediaPreviewActivity.isContentTypeSupported(slide.getContentType()) && slide.getUri() != null) {
Intent intent = new Intent(context, MediaPreviewActivity.class);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.putExtra(MediaPreviewActivity.SIZE_EXTRA, slide.asAttachment().getSize());
intent.putExtra(MediaPreviewActivity.DC_MSG_ID, slide.getDcMsgId());
intent.putExtra(MediaPreviewActivity.OUTGOING_EXTRA, true);
intent.setDataAndType(slide.getUri(), slide.getContentType());
context.startActivity(intent);
}
@@ -107,7 +107,6 @@ public class MessageNotifier {
sendInThreadNotification(context, recipient);
} else {
Intent intent = new Intent(context, ConversationActivity.class);
intent.putExtra(ConversationActivity.ADDRESS_EXTRA, recipient.getAddress());
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId);
intent.setData((Uri.parse("custom://" + System.currentTimeMillis())));
@@ -69,7 +69,6 @@ public class NotificationItem {
public PendingIntent getPendingIntent(Context context) {
Intent intent = new Intent(context, ConversationActivity.class);
Recipient notifyRecipients = threadRecipient != null ? threadRecipient : conversationRecipient;
if (notifyRecipients != null) intent.putExtra(ConversationActivity.ADDRESS_EXTRA, notifyRecipients.getAddress());
intent.putExtra("thread_id", threadId);
intent.setData((Uri.parse("custom://"+System.currentTimeMillis())));
@@ -164,7 +164,6 @@ public class NotificationState {
if (threads.size() != 1) throw new AssertionError("We only support replies to single thread notifications! " + threads.size());
Intent intent = new Intent(context, ConversationPopupActivity.class);
intent.putExtra(ConversationActivity.ADDRESS_EXTRA, recipient.getAddress());
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, (long)threads.toArray()[0]);
intent.setData((Uri.parse("custom://"+System.currentTimeMillis())));
@@ -22,7 +22,6 @@ import org.thoughtcrime.securesms.contacts.avatars.ContactColors;
import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto;
import org.thoughtcrime.securesms.contacts.avatars.FallbackContactPhoto;
import org.thoughtcrime.securesms.contacts.avatars.GeneratedContactPhoto;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.mms.Slide;
@@ -65,7 +64,7 @@ public class SingleRecipientNotificationBuilder extends AbstractNotificationBuil
addPerson(recipient.getContactUri().toString());
}
ContactPhoto contactPhoto = recipient.getContactPhoto();
ContactPhoto contactPhoto = recipient.getContactPhoto(context);
FallbackContactPhoto fallbackContactPhoto = recipient.getFallbackContactPhoto();
if (contactPhoto != null) {
@@ -184,7 +184,6 @@ public class QrScanHandler implements DcEventCenter.DcEventDelegate {
dcContext.endCaptureNextError();
if (newChatId != 0) {
Intent intent = new Intent(activity, ConversationActivity.class);
intent.putExtra(ConversationActivity.ADDRESS_EXTRA, Address.fromChat(newChatId));
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, (long)newChatId);
activity.startActivity(intent);
} else if (!errorString.isEmpty()) {
@@ -94,6 +94,10 @@ public class Recipient implements RecipientModifiedListener {
private @Nullable String profileAvatar;
private boolean profileSharing;
public static @NonNull Recipient from(@NonNull Context context, int dcMsgId) {
ApplicationDcContext dcContext = DcHelper.getContext(context);
return dcContext.getRecipient(dcContext.getChat(dcContext.getMsg(dcMsgId).getChatId()));
}
@SuppressWarnings("ConstantConditions")
public static @NonNull Recipient from(@NonNull Context context, @NonNull Address address, boolean asynchronous) {
@@ -101,6 +105,8 @@ public class Recipient implements RecipientModifiedListener {
ApplicationDcContext dcContext = DcHelper.getContext(context);
if(address.isDcContact()) {
return dcContext.getRecipient(dcContext.getContact(address.getDcContactId()));
} else if (address.isDcChat()) {
return dcContext.getRecipient(dcContext.getChat(address.getDcChatId()));
}
else if(address.isEmail()) {
int contactId = dcContext.lookupContactIdByAddr(address.toEmailString());
@@ -383,7 +389,7 @@ public class Recipient implements RecipientModifiedListener {
}
public boolean isGroupRecipient() {
return address.isGroup();
return participants.size() > 1;
}
public boolean isMmsGroupRecipient() {
@@ -437,8 +443,8 @@ public class Recipient implements RecipientModifiedListener {
else return new GeneratedContactPhoto("#");
}
public synchronized @Nullable ContactPhoto getContactPhoto() {
if (isGroupRecipient() && groupAvatarId != null) return new GroupRecordContactPhoto(address, groupAvatarId);
public synchronized @Nullable ContactPhoto getContactPhoto(Context context) {
if (isGroupRecipient()) return new GroupRecordContactPhoto(context, getAddress());
else if (systemContactPhoto != null) return new SystemContactPhoto(address, systemContactPhoto, 0);
else if (profileAvatar != null) return new ProfileContactPhoto(address.toEmailString(), profileAvatar);
else return null;
@@ -137,8 +137,6 @@ public class SearchFragment extends Fragment implements SearchListAdapter.EventL
if (conversationList != null) {
conversationList.onCreateConversation(threadRecord.getThreadId(),
threadRecord.getRecipient(),
threadRecord.getDistributionType(),
threadRecord.getLastSeen());
}
}
@@ -146,12 +144,10 @@ public class SearchFragment extends Fragment implements SearchListAdapter.EventL
@Override
public void onContactClicked(@NonNull Recipient contact) {
Intent intent = new Intent(getContext(), ConversationActivity.class);
intent.putExtra(ConversationActivity.ADDRESS_EXTRA, contact.getAddress());
long existingThread = DatabaseFactory.getThreadDatabase(getContext()).getThreadIdIfExistsFor(contact);
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, existingThread);
intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, ThreadDatabase.DistributionTypes.DEFAULT);
startActivity(intent);
}
@@ -172,8 +168,6 @@ public class SearchFragment extends Fragment implements SearchListAdapter.EventL
ConversationListActivity conversationList = (ConversationListActivity) getActivity();
if (conversationList != null) {
conversationList.openConversation(message.threadId,
message.recipient,
ThreadDatabase.DistributionTypes.DEFAULT,
-1,
startingPosition);
}
@@ -57,7 +57,7 @@ class SearchListAdapter extends RecyclerView.Adapter<SearchListAdapter.Search
ThreadRecord conversationResult = getConversationResult(position);
if (conversationResult != null) {
holder.bind(conversationResult, new DcLot(0), glideRequests, eventListener, locale, searchResult.getQuery());
holder.bind(conversationResult, 0, new DcLot(0), glideRequests, eventListener, locale, searchResult.getQuery());
return;
}
@@ -160,13 +160,14 @@ class SearchListAdapter extends RecyclerView.Adapter<SearchListAdapter.Search
}
void bind(@NonNull ThreadRecord conversationResult,
int msgId,
@NonNull DcLot summary,
@NonNull GlideRequests glideRequests,
@NonNull EventListener eventListener,
@NonNull Locale locale,
@Nullable String query)
{
root.bind(conversationResult, summary, glideRequests, locale, Collections.emptySet(), false, query);
root.bind(conversationResult, msgId, summary, glideRequests, locale, Collections.emptySet(), false, query);
root.setOnClickListener(view -> eventListener.onConversationClicked(conversationResult));
}
@@ -51,11 +51,11 @@ public class DirectShareService extends ChooserTargetService {
Bitmap avatar;
if (recipient.getContactPhoto() != null) {
if (recipient.getContactPhoto(getApplicationContext()) != null) {
try {
avatar = GlideApp.with(this)
.asBitmap()
.load(recipient.getContactPhoto())
.load(recipient.getContactPhoto(getApplicationContext()))
.circleCrop()
.submit(getResources().getDimensionPixelSize(android.R.dimen.notification_large_icon_width),
getResources().getDimensionPixelSize(android.R.dimen.notification_large_icon_width))
@@ -56,9 +56,7 @@ public class CommunicationActions {
@Override
protected void onPostExecute(Long threadId) {
Intent intent = new Intent(context, ConversationActivity.class);
intent.putExtra(ConversationActivity.ADDRESS_EXTRA, recipient.getAddress());
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId);
intent.putExtra(ConversationActivity.TIMING_EXTRA, System.currentTimeMillis());
if (!TextUtils.isEmpty(text)) {
intent.putExtra(ConversationActivity.TEXT_EXTRA, text);
@@ -1,5 +1,8 @@
package org.thoughtcrime.securesms.util;
import android.annotation.SuppressLint;
import android.content.Context;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
@@ -9,6 +12,8 @@ import java.security.NoSuchAlgorithmException;
public class FileUtils {
public static final String AVATAR_PATH_TEMPLATE = "/group_chat_avatar_%s_%o.jpg";
public static native int getFileDescriptorOwner(FileDescriptor fileDescriptor);
public static byte[] getFileDigest(FileInputStream fin) throws IOException {
@@ -50,4 +55,10 @@ public class FileUtils {
directory.delete();
}
@SuppressLint("DefaultLocale")
public static String getFilePathForChatAvatar(Context context, int chatId, long timestamp) {
return context.getFilesDir().getAbsolutePath() + String.format(AVATAR_PATH_TEMPLATE, chatId, timestamp);
}
}
@@ -67,6 +67,10 @@ public class MediaUtil {
return slide;
}
/**
* @deprecated use getSlideForMsg instead.
*/
@Deprecated
public static Slide getSlideForAttachment(Context context, Attachment attachment) {
Slide slide = null;
if (isGif(attachment.getContentType())) {