Compare commits

..

19 Commits

Author SHA1 Message Date
B. Petersen a2d6a00cd1 Update messenger-backend submodule. 2018-10-29 23:40:38 +01:00
Angelo Fuchs ffe69f7f4b Using DcEventDelegate instead of Object. 2018-10-29 23:01:57 +02:00
björn petersen 887cc3a1bb [WIP] Media gallery (#109) closes #78
* Update messenger-backend submodule.
* display a swipe able media gallery when one taps an image
* display a tiled gallery from chat-menu and from preview gallery
* changed some utility classes to handle the new situations
2018-10-29 20:55:49 +01:00
daniel.boehrs c192b719f5 show avatars for groups #55 2018-10-29 15:44:55 +01:00
daniel.boehrs ed0de74406 Adjust keyboard show / hide #92 2018-10-29 10:29:43 +01:00
B. Petersen ff1878511d show contact-avatar in deaddrop, tackles #95 2018-10-23 23:16:03 +02:00
B. Petersen a77cbee5f2 remove unused semi-transparency 2018-10-23 23:07:01 +02:00
B. Petersen e92fda0ca3 use plus-icon instead of pencil in chatlist, closes #107 2018-10-23 22:53:31 +02:00
B. Petersen a4c9e1699b improve 'no messages' hint on empty chats. 2018-10-23 21:51:14 +02:00
B. Petersen df87b64f66 upper-right align date in chatlist 2018-10-23 16:36:44 +02:00
B. Petersen b79e8e812d make status icons mutual exclusive 2018-10-23 16:24:19 +02:00
B. Petersen ec2deebda4 add space between columns in chatlist, closes #106 2018-10-23 16:09:09 +02:00
B. Petersen befbf84c59 remove unused thumbnail from chatlist 2018-10-23 14:30:08 +02:00
B. Petersen 7b205db200 do not show the date in bold on unread, too noisy. only show subject in bold and green unread counter. 2018-10-23 13:57:58 +02:00
B. Petersen e43c85b12b unify ripple color 2018-10-23 13:52:29 +02:00
B. Petersen 758c175b3d add horizontal border between chats in chatlist, tackles #106 2018-10-23 13:11:21 +02:00
B. Petersen fdceb0e080 draw verified-icon directly right of chat-title, closes #105 2018-10-23 12:00:56 +02:00
B. Petersen 3863ef91e1 comment 2018-10-23 09:51:35 +02:00
B. Petersen 6e07258fc7 make deaddrop-background compatible to selection-background 2018-10-23 07:04:15 +02:00
43 changed files with 559 additions and 547 deletions
-1
View File
@@ -192,7 +192,6 @@
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize" />
<activity android:name=".GroupCreateActivity"
android:windowSoftInputMode="stateVisible"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".DatabaseUpgradeActivity"
+2 -2
View File
@@ -255,8 +255,8 @@ android {
}
defaultConfig {
versionCode 397
versionName "0.0.7"
versionCode 398
versionName "0.0.8"
applicationId "chat.delta.androidii"
minSdkVersion 14
Binary file not shown.

Before

Width:  |  Height:  |  Size: 129 B

After

Width:  |  Height:  |  Size: 344 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 512 B

After

Width:  |  Height:  |  Size: 727 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 B

After

Width:  |  Height:  |  Size: 351 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 331 B

After

Width:  |  Height:  |  Size: 508 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 B

After

Width:  |  Height:  |  Size: 368 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 634 B

After

Width:  |  Height:  |  Size: 822 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 B

After

Width:  |  Height:  |  Size: 387 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1017 B

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

+1 -1
View File
@@ -73,7 +73,7 @@
android:layout_gravity="bottom|end"
android:layout_margin="16dp"
app:backgroundTint="@color/signal_primary"
android:src="@drawable/ic_create_white_24dp"
android:src="@drawable/ic_add_white_24dp"
android:focusable="true"
android:contentDescription="@string/conversation_list_fragment__fab_content_description"/>
+95 -124
View File
@@ -8,9 +8,7 @@
android:focusable="true"
android:nextFocusRight="@+id/fab"
android:nextFocusLeft="@+id/container"
android:layout_height="72dp"
android:paddingLeft="16dp"
android:paddingRight="16dp">
android:layout_height="72dp">
<org.thoughtcrime.securesms.components.AvatarImageView
android:id="@+id/contact_photo_image"
@@ -23,150 +21,123 @@
android:cropToPadding="true"
tools:src="@drawable/ic_contact_picture"
android:contentDescription="@string/conversation_list_item_view__contact_photo_image"
android:layout_marginRight="12dp"
android:layout_marginEnd="12dp" />
android:layout_marginLeft="12dp"
android:layout_marginRight="12dp" />
<RelativeLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:layout_marginStart="4dp"
android:layout_centerVertical="true"
android:layout_toRightOf="@id/contact_photo_image"
android:layout_toEndOf="@id/contact_photo_image"
android:weightSum="1"
android:orientation="horizontal">
<LinearLayout
android:orientation="horizontal"
android:gravity="center_vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_toRightOf="@id/contact_photo_image"
android:layout_toEndOf="@id/contact_photo_image">
<RelativeLayout
android:id="@+id/from"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_toLeftOf="@+id/thumbnail"
android:layout_toStartOf="@+id/thumbnail">
<LinearLayout
android:orientation="vertical"
android:layout_weight="2"
android:layout_width="0dp"
android:layout_height="wrap_content">
<org.thoughtcrime.securesms.components.FromTextView
android:id="@+id/from_text"
style="@style/Signal.Text.Body"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
<LinearLayout
android:id="@+id/from"
android:orientation="horizontal"
android:layout_marginBottom="2dp"
android:layout_toLeftOf="@id/verified_indicator"
android:layout_toStartOf="@id/verified_indicator"
android:drawablePadding="5dp"
android:ellipsize="end"
android:fontFamily="sans-serif-medium"
android:maxLines="1"
android:textColor="?attr/conversation_list_item_contact_color"
tools:text="Jules Bonnot" />
<ImageView
android:id="@+id/verified_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/ic_verified"
android:visibility="gone"
android:layout_marginLeft="5dp"
android:layout_marginStart="5dp"
android:layout_marginTop="4dp"
android:layout_marginRight="5dp"
android:layout_marginEnd="5dp"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true" />
android:layout_height="wrap_content">
</RelativeLayout>
<org.thoughtcrime.securesms.components.FromTextView
android:id="@+id/from_text"
style="@style/Signal.Text.Body"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawablePadding="5dp"
android:ellipsize="end"
android:fontFamily="sans-serif-medium"
android:maxLines="1"
android:textColor="?attr/conversation_list_item_contact_color"
tools:text="Jules Bonnot" />
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
<ImageView
android:id="@+id/verified_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/ic_verified"
android:visibility="visible"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp" />
</LinearLayout>
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
android:id="@+id/subject"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/from"
android:layout_toLeftOf="@+id/status"
android:layout_toStartOf="@+id/status"
android:paddingRight="1dp"
style="@style/Signal.Text.Preview"
android:textColor="?attr/conversation_list_item_subject_color"
android:maxLines="1"
tools:text="Wheels arrive at 3pm flat. This is a somewhat longer message."
android:ellipsize="end" />
<org.thoughtcrime.securesms.components.ThumbnailView
android:id="@+id/thumbnail"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_gravity="center_horizontal"
android:layout_marginRight="5dp"
android:layout_marginLeft="5dp"
android:layout_toLeftOf="@+id/date"
android:layout_toStartOf="@+id/date"
android:layout_alignParentTop="true"
android:layout_marginTop="5dp"
android:contentDescription="@string/conversation_activity__attachment_thumbnail"
android:visibility="gone"
tools:src="@drawable/ic_video_light"
tools:visibility="visible" />
</LinearLayout>
<TextView android:id="@id/date"
android:layout_marginLeft="3dp"
android:layout_marginStart="3dp"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_alignBaseline="@+id/from"
android:layout_above="@id/subject"
style="@style/Signal.Text.Caption"
android:textColor="?attr/conversation_list_item_date_color"
android:textAllCaps="true"
tools:text="30 mins"
android:singleLine="true"/>
<LinearLayout
android:orientation="vertical"
android:gravity="end"
android:layout_marginLeft="24dp"
android:layout_marginStart="24dp"
android:layout_marginRight="12dp"
android:layout_marginEnd="12dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView android:id="@+id/archived"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/date"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_alignBaseline="@id/subject"
android:layout_marginLeft="5dp"
android:layout_marginStart="5dp"
android:paddingLeft="6dp"
android:paddingRight="6dp"
android:paddingTop="2dp"
android:paddingBottom="2dp"
style="@style/Signal.Text.Caption"
android:text="@string/conversation_list_item_view__archived"
android:textColor="@color/core_white"
android:textAllCaps="true"
android:background="@drawable/archived_indicator_background" />
<TextView android:id="@id/date"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
style="@style/Signal.Text.Caption"
android:textColor="?attr/conversation_list_item_date_color"
android:textAllCaps="true"
tools:text="30 mins"
android:singleLine="true"/>
<LinearLayout
android:orientation="horizontal"
android:minHeight="20dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<FrameLayout android:id="@+id/status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:layout_below="@id/date"
android:layout_toLeftOf="@id/archived"
android:layout_toStartOf="@id/archived"
android:layout_alignWithParentIfMissing="true">
<TextView android:id="@+id/archived"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="6dp"
android:paddingRight="6dp"
android:paddingTop="2dp"
android:paddingBottom="2dp"
style="@style/Signal.Text.Caption"
android:text="@string/conversation_list_item_view__archived"
android:textColor="@color/core_white"
android:textAllCaps="true"
android:background="@drawable/archived_indicator_background" />
<org.thoughtcrime.securesms.components.DeliveryStatusView
android:id="@+id/delivery_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
app:iconColor="?attr/conversation_list_item_delivery_icon_color"/>
<org.thoughtcrime.securesms.components.DeliveryStatusView
android:id="@+id/delivery_status"
android:layout_marginTop="6dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:iconColor="?attr/conversation_list_item_delivery_icon_color"/>
<ImageView android:id="@+id/unread_indicator"
android:layout_width="24dp"
android:layout_height="24dp"/>
<ImageView android:id="@+id/unread_indicator"
android:layout_width="24dp"
android:layout_height="24dp"/>
</LinearLayout>
</FrameLayout>
</LinearLayout>
</RelativeLayout>
</LinearLayout>
<View android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_alignParentBottom="true"
android:background="@drawable/attachment_selector_shadow"/>
</org.thoughtcrime.securesms.ConversationListItem>
+28 -30
View File
@@ -39,7 +39,8 @@
tools:src="@drawable/ic_contact_picture"
android:contentDescription="@string/conversation_list_item_view__contact_photo_image"/>
<RelativeLayout
<LinearLayout
android:orientation="vertical"
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -47,35 +48,33 @@
android:layout_toEndOf="@id/contact_photo_image"
android:layout_toRightOf="@id/contact_photo_image">
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
android:id="@+id/title"
style="@style/TextSecure.TitleTextStyle"
<LinearLayout
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_toLeftOf="@id/verified_indicator"
android:layout_toStartOf="@id/verified_indicator"
android:drawablePadding="5dp"
android:ellipsize="end"
android:maxLines="1"
android:textSize="18dp"
android:transitionName="recipient_name"
tools:text="Jules Bonnot" />
android:layout_height="wrap_content">
<ImageView
android:id="@+id/verified_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_marginStart="5dp"
android:layout_marginTop="5dp"
android:layout_marginRight="5dp"
android:layout_marginEnd="5dp"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:src="@drawable/ic_verified"
android:visibility="gone" />
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
android:id="@+id/title"
style="@style/TextSecure.TitleTextStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawablePadding="5dp"
android:ellipsize="end"
android:maxLines="1"
android:textSize="18dp"
android:transitionName="recipient_name"
tools:text="Jules Bonnot" />
<ImageView
android:id="@+id/verified_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:src="@drawable/ic_verified"
android:visibility="gone" />
</LinearLayout>
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
android:id="@+id/subtitle"
@@ -86,9 +85,8 @@
android:maxLines="1"
android:textDirection="ltr"
android:textSize="13dp"
android:layout_below="@id/title"
tools:text="(123) 123-1234" />
</RelativeLayout>
</LinearLayout>
</org.thoughtcrime.securesms.ConversationTitleView>
+1
View File
@@ -1532,5 +1532,6 @@
<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>
+9 -9
View File
@@ -28,16 +28,16 @@ import java.util.Enumeration;
import java.util.Hashtable;
public class DcEventCenter {
private Hashtable<Integer, ArrayList<Object>> allObservers = new Hashtable<>();
private Hashtable<Integer, ArrayList<DcEventDelegate>> allObservers = new Hashtable<>();
private final Object LOCK = new Object();
public interface DcEventDelegate {
void handleEvent(int eventId, Object data1, Object data2);
}
public void addObserver(Object observer, int eventId) {
public void addObserver(DcEventDelegate observer, int eventId) {
synchronized (LOCK) {
ArrayList<Object> idObservers = allObservers.get(eventId);
ArrayList<DcEventDelegate> idObservers = allObservers.get(eventId);
if (idObservers == null) {
allObservers.put(eventId, (idObservers = new ArrayList<>()));
}
@@ -45,9 +45,9 @@ public class DcEventCenter {
}
}
public void removeObserver(Object observer, int eventId) {
public void removeObserver(DcEventDelegate observer, int eventId) {
synchronized (LOCK) {
ArrayList<Object> idObservers = allObservers.get(eventId);
ArrayList<DcEventDelegate> idObservers = allObservers.get(eventId);
if (idObservers != null) {
idObservers.remove(observer);
}
@@ -59,7 +59,7 @@ public class DcEventCenter {
Enumeration<Integer> enumKey = allObservers.keys();
while(enumKey.hasMoreElements()) {
Integer eventId = enumKey.nextElement();
ArrayList<Object> idObservers = allObservers.get(eventId);
ArrayList<DcEventDelegate> idObservers = allObservers.get(eventId);
if (idObservers != null) {
idObservers.remove(observer);
}
@@ -69,11 +69,11 @@ public class DcEventCenter {
public void sendToObservers(int eventId, Object data1, Object data2) {
synchronized (LOCK) {
ArrayList<Object> idObservers = allObservers.get(eventId);
ArrayList<DcEventDelegate> idObservers = allObservers.get(eventId);
if (idObservers != null) {
for (int i = 0; i < idObservers.size(); i++) {
Object observer = idObservers.get(i);
((DcEventDelegate) observer).handleEvent(eventId, data1, data2);
DcEventDelegate observer = idObservers.get(i);
observer.handleEvent(eventId, data1, data2);
}
}
}
@@ -0,0 +1,44 @@
package com.b44t.messenger;
/**
* Contains a list of media entries, their respective positions and ability to move through it.
*/
public class DcMediaGalleryElement {
final int[] mediaMsgs;
int position;
final DcContext context;
public DcMediaGalleryElement(int[] mediaMsgs, int position, DcContext context, boolean leftIsRecent) {
this.mediaMsgs = mediaMsgs;
this.position = position;
this.context = context;
// normal state is left is recent. If the ui needs right to be recent we reverse the order here.
if (!leftIsRecent) {
for (int ii = 0; ii < mediaMsgs.length / 2; ii++) {
int tmp = mediaMsgs[ii];
int opposite = mediaMsgs.length - 1 - ii;
mediaMsgs[ii] = mediaMsgs[opposite];
mediaMsgs[opposite] = tmp;
}
}
}
public int getCount() {
return mediaMsgs.length;
}
public int getPosition() {
return position;
}
public void moveToPosition(int newPosition) {
if(newPosition < 0 || newPosition >= mediaMsgs.length)
throw new IllegalArgumentException("can't move outside of known area.");
position = newPosition;
}
public DcMsg getMessage() {
return context.getMsg(mediaMsgs[position]);
}
}
+8 -2
View File
@@ -31,8 +31,7 @@ import org.thoughtcrime.securesms.database.model.Quote;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientProvider;
import java.util.Collections;
import java.util.List;
import java.io.File;
import java.util.Set;
public class DcMsg {
@@ -54,6 +53,7 @@ public class DcMsg {
public final static int DC_STATE_OUT_DELIVERED = 26;
public final static int DC_STATE_OUT_MDN_RCVD = 28;
public static final int DC_MSG_NO_ID = 0;
public final static int DC_MSG_ID_MARKER1 = 1;
public final static int DC_MSG_ID_DAYMARKER = 9;
@@ -111,6 +111,12 @@ public class DcMsg {
public native void setDimension (int width, int height);
public native void setDuration (int duration);
public File getFileAsFile() {
if(getFile()==null)
throw new AssertionError("expected a file to be present.");
return new File(getFile());
}
// aliases and higher-level tools
public static int[] msgSetToIds(final Set<DcMsg> dcMsgs) {
int cnt = dcMsgs==null? 0 : dcMsgs.size();
@@ -170,10 +170,15 @@ public class ConversationFragment extends Fragment
private void setNoMessageText() {
if(threadId==DcChat.DC_CHAT_ID_DEADDROP) {
noMessageTextView.setText(R.string.menu_deaddrop);
noMessageTextView.setText(R.string.conversation__no_messages);
}
else if(getListAdapter().isGroupChat()){
noMessageTextView.setText(R.string.ConversationActivity_MsgNewGroupDraftHint);
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);
@@ -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,19 +26,15 @@ 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.b44t.messenger.DcContact;
import com.google.zxing.integration.android.IntentIntegrator;
import com.google.zxing.integration.android.IntentResult;
import org.thoughtcrime.securesms.components.SearchToolbar;
import org.thoughtcrime.securesms.database.Address;
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;
@@ -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;
@@ -138,7 +139,10 @@ class ConversationListAdapter extends RecyclerView.Adapter {
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;
@@ -17,11 +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;
@@ -40,6 +38,7 @@ 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;
@@ -47,7 +46,7 @@ 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;
@@ -88,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);
@@ -108,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());
@@ -146,7 +140,6 @@ public class ConversationListItem extends RelativeLayout
this.msgId = msgId;
this.glideRequests = glideRequests;
this.unreadCount = thread.getUnreadCount();
this.distributionType = thread.getDistributionType();
this.lastSeen = thread.getLastSeen();
if (highlightSubstring != null) {
@@ -163,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);
}
@@ -198,10 +189,9 @@ public class ConversationListItem extends RelativeLayout
archivedView.setVisibility(GONE);
unreadIndicator.setVisibility(GONE);
deliveryStatusIndicator.setNone();
thumbnailView.setVisibility(GONE);
setBatchState(false);
setRippleColor(contact);
setBgColor();
contactPhotoImage.setAvatar(glideRequests, recipient, true);
}
@@ -220,10 +210,9 @@ public class ConversationListItem extends RelativeLayout
archivedView.setVisibility(GONE);
unreadIndicator.setVisibility(GONE);
deliveryStatusIndicator.setNone();
thumbnailView.setVisibility(GONE);
setBatchState(false);
setRippleColor(recipient);
setBgColor();
contactPhotoImage.setAvatar(glideRequests, recipient, true);
}
@@ -256,88 +245,63 @@ public class ConversationListItem extends RelativeLayout
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) {
// TODO: a different backgroupd for the deaddrop would be nice, however the following code collades with the selected state and should be fixex
// 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();
// }
// else {
// TypedArray ta = getContext().obtainStyledAttributes(new int[] { R.attr.conversation_list_item_background});
// setBackground(ta.getDrawable(0));
// ta.recycle();
((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,
@@ -377,41 +341,4 @@ public class ConversationListItem extends RelativeLayout
return spanned;
}
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);
}
}
}
@@ -25,12 +25,12 @@ import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
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;
@@ -53,6 +53,7 @@ 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;
@@ -114,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
@@ -204,7 +205,25 @@ 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));
}
@@ -212,6 +231,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
final Address groupAddress = getIntent().getParcelableExtra(GROUP_ADDRESS_EXTRA);
if (groupAddress != null) {
editGroupChatId = groupAddress.getDcChatId();
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
new FillExistingGroupInfoAsyncTask(this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, editGroupChatId);
}
}
@@ -270,28 +290,30 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
finish();
}
@SuppressWarnings("ResultOfMethodCallIgnored")
private void setGroupAvatar(int chatId) {
if(avatarBmp == null) {
return;
}
String avatarPath = getFilesDir().getAbsolutePath() + "/chatAvatar_" + chatId + ".jpg";
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();
}
FileOutputStream outStream;
try {
outStream = new FileOutputStream(avatarPath);
avatarBmp.compress(Bitmap.CompressFormat.JPEG, 85, outStream);
dcContext.setChatProfileImage(chatId, avatarPath);
Log.d("dboehrs", "setGroupAvatar: " + avatarPath);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
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();
}
}
private boolean showGroupNameEmptyToast(String groupName) {
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;
@@ -383,22 +405,27 @@ 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) {
@@ -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;
}
}
@@ -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));
@@ -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)
@@ -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);
}
@@ -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) {
@@ -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;
@@ -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))
@@ -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())) {