Compare commits
51 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a2d6a00cd1 | |||
| ffe69f7f4b | |||
| 887cc3a1bb | |||
| c192b719f5 | |||
| ed0de74406 | |||
| ff1878511d | |||
| a77cbee5f2 | |||
| e92fda0ca3 | |||
| a4c9e1699b | |||
| df87b64f66 | |||
| b79e8e812d | |||
| ec2deebda4 | |||
| befbf84c59 | |||
| 7b205db200 | |||
| e43c85b12b | |||
| 758c175b3d | |||
| fdceb0e080 | |||
| 3863ef91e1 | |||
| 6e07258fc7 | |||
| c07a0dedd6 | |||
| e5b906184d | |||
| 2dbd12403f | |||
| 9906608956 | |||
| 5870f0cd02 | |||
| 3dc6ea1c85 | |||
| e66ae137fe | |||
| 4d32f5746c | |||
| 6e5c6e3d4d | |||
| c459aa4476 | |||
| bf4bcba78c | |||
| ccb5e3285f | |||
| 5424d56801 | |||
| 5c15784f66 | |||
| 0abecd74c9 | |||
| aa3df42f71 | |||
| cfe6e023cb | |||
| 6140b2aab4 | |||
| cc95e1fa1f | |||
| 643e8d80f7 | |||
| 84cc1c124a | |||
| 22d8f3177f | |||
| 471f4480ac | |||
| bc387096f5 | |||
| 93b5a345ea | |||
| 50b18b7d65 | |||
| 004319db64 | |||
| 90b52cebf1 | |||
| f4a01a2bae | |||
| 77ed15125d | |||
| 15ef0271fc | |||
| 1dd2ad04ba |
@@ -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"
|
||||
|
||||
@@ -255,8 +255,8 @@ android {
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
versionCode 396
|
||||
versionName "0.0.6"
|
||||
versionCode 398
|
||||
versionName "0.0.8"
|
||||
applicationId "chat.delta.androidii"
|
||||
|
||||
minSdkVersion 14
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
Before Width: | Height: | Size: 129 B After Width: | Height: | Size: 344 B |
|
After Width: | Height: | Size: 727 B |
|
Before Width: | Height: | Size: 92 B After Width: | Height: | Size: 351 B |
|
After Width: | Height: | Size: 508 B |
|
Before Width: | Height: | Size: 97 B After Width: | Height: | Size: 368 B |
|
After Width: | Height: | Size: 822 B |
|
Before Width: | Height: | Size: 97 B After Width: | Height: | Size: 387 B |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
@@ -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"/>
|
||||
@@ -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"/>
|
||||
@@ -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"
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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())); }
|
||||
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
@@ -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())) {
|
||||
|
||||