Add sticker support.

No sticker packs are available for use yet, but we now have the
latent ability to send and receive.
This commit is contained in:
Greyson Parrelli
2019-04-17 10:21:30 -04:00
parent d5fffb0132
commit 2a644437fb
447 changed files with 8782 additions and 1132 deletions

View File

@@ -105,8 +105,10 @@ import org.thoughtcrime.securesms.components.InputAwareLayout;
import org.thoughtcrime.securesms.components.InputPanel;
import org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout.OnKeyboardShownListener;
import org.thoughtcrime.securesms.components.SendButton;
import org.thoughtcrime.securesms.components.emoji.EmojiDrawer;
import org.thoughtcrime.securesms.components.TooltipPopup;
import org.thoughtcrime.securesms.components.emoji.EmojiKeyboardProvider;
import org.thoughtcrime.securesms.components.emoji.EmojiStrings;
import org.thoughtcrime.securesms.components.emoji.MediaKeyboard;
import org.thoughtcrime.securesms.components.identity.UntrustedSendDialog;
import org.thoughtcrime.securesms.components.identity.UnverifiedBannerView;
import org.thoughtcrime.securesms.components.identity.UnverifiedSendDialog;
@@ -141,17 +143,18 @@ import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.database.identity.IdentityRecordList;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
import org.thoughtcrime.securesms.database.model.StickerRecord;
import org.thoughtcrime.securesms.events.ReminderUpdateEvent;
import org.thoughtcrime.securesms.giph.ui.GiphyActivity;
import org.thoughtcrime.securesms.linkpreview.LinkPreview;
import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository;
import org.thoughtcrime.securesms.linkpreview.LinkPreviewViewModel;
import org.thoughtcrime.securesms.mediasend.MediaSendActivity;
import org.thoughtcrime.securesms.mediasend.Media;
import org.thoughtcrime.securesms.jobs.MultiDeviceBlockedUpdateJob;
import org.thoughtcrime.securesms.jobs.RetrieveProfileJob;
import org.thoughtcrime.securesms.jobs.ServiceOutageDetectionJob;
import org.thoughtcrime.securesms.linkpreview.LinkPreview;
import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository;
import org.thoughtcrime.securesms.linkpreview.LinkPreviewViewModel;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.mediasend.Media;
import org.thoughtcrime.securesms.mediasend.MediaSendActivity;
import org.thoughtcrime.securesms.mms.AttachmentManager;
import org.thoughtcrime.securesms.mms.AttachmentManager.MediaType;
import org.thoughtcrime.securesms.mms.AudioSlide;
@@ -169,6 +172,7 @@ import org.thoughtcrime.securesms.mms.QuoteId;
import org.thoughtcrime.securesms.mms.QuoteModel;
import org.thoughtcrime.securesms.mms.Slide;
import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.mms.StickerSlide;
import org.thoughtcrime.securesms.mms.TextSlide;
import org.thoughtcrime.securesms.mms.VideoSlide;
import org.thoughtcrime.securesms.notifications.MarkReadReceiver;
@@ -178,15 +182,20 @@ import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.profiles.GroupShareProfileView;
import org.thoughtcrime.securesms.providers.BlobProvider;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientExporter;
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
import org.thoughtcrime.securesms.recipients.RecipientExporter;
import org.thoughtcrime.securesms.search.model.MessageResult;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.sms.OutgoingEncryptedMessage;
import org.thoughtcrime.securesms.sms.OutgoingEndSessionMessage;
import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
import org.thoughtcrime.securesms.stickers.StickerKeyboardProvider;
import org.thoughtcrime.securesms.stickers.StickerLocator;
import org.thoughtcrime.securesms.stickers.StickerManagementActivity;
import org.thoughtcrime.securesms.stickers.StickerPackInstallEvent;
import org.thoughtcrime.securesms.stickers.StickerSearchRepository;
import org.thoughtcrime.securesms.util.BitmapUtil;
import org.thoughtcrime.securesms.util.CharacterCalculator.CharacterState;
import org.thoughtcrime.securesms.util.CommunicationActions;
@@ -200,6 +209,7 @@ import org.thoughtcrime.securesms.util.IdentityUtil;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.ServiceUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.TextSecurePreferences.MediaKeyboardMode;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.concurrent.AssertedSuccessListener;
@@ -240,7 +250,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
InputPanel.Listener,
InputPanel.MediaListener,
ComposeText.CursorPositionChangedListener,
ConversationSearchBottomBar.EventListener
ConversationSearchBottomBar.EventListener,
StickerKeyboardProvider.StickerEventListener
{
private static final String TAG = ConversationActivity.class.getSimpleName();
@@ -249,6 +260,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
public static final String IS_ARCHIVED_EXTRA = "is_archived";
public static final String TEXT_EXTRA = "draft_text";
public static final String MEDIA_EXTRA = "media_list";
public static final String STICKER_EXTRA = "media_list";
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";
@@ -291,13 +303,14 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private AttachmentManager attachmentManager;
private AudioRecorder audioRecorder;
private BroadcastReceiver securityUpdateReceiver;
private Stub<EmojiDrawer> emojiDrawerStub;
private Stub<MediaKeyboard> emojiDrawerStub;
protected HidingLinearLayout quickAttachmentToggle;
protected HidingLinearLayout inlineAttachmentToggle;
private InputPanel inputPanel;
private LinkPreviewViewModel linkPreviewViewModel;
private ConversationSearchViewModel searchViewModel;
private LinkPreviewViewModel linkPreviewViewModel;
private ConversationSearchViewModel searchViewModel;
private ConversationStickerViewModel stickerViewModel;
private Recipient recipient;
private long threadId;
@@ -338,6 +351,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
initializeResources();
initializeLinkPreviewObserver();
initializeSearchObserver();
initializeStickerObserver();
initializeSecurity(false, isDefaultSms).addListener(new AssertedSuccessListener<Boolean>() {
@Override
public void onSuccess(Boolean result) {
@@ -397,18 +411,13 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
searchNav.setVisibility(View.GONE);
}
@Override
protected void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@Override
protected void onResume() {
super.onResume();
dynamicTheme.onResume(this);
dynamicLanguage.onResume(this);
EventBus.getDefault().register(this);
initializeEnabledCheck();
initializeMmsEnabledCheck();
initializeIdentityRecords();
@@ -554,7 +563,16 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
final Context context = ConversationActivity.this.getApplicationContext();
sendMediaMessage(transport.isSms(), message, slideDeck, Collections.emptyList(), Collections.emptyList(), expiresIn, subscriptionId, initiating).addListener(new AssertedSuccessListener<Void>() {
sendMediaMessage(transport.isSms(),
message,
slideDeck,
inputPanel.getQuote().orNull(),
Collections.emptyList(),
Collections.emptyList(),
expiresIn,
subscriptionId,
initiating,
true).addListener(new AssertedSuccessListener<Void>() {
@Override
public void onSuccess(Void result) {
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
@@ -1191,10 +1209,16 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private ListenableFuture<Boolean> initializeDraft() {
final SettableFuture<Boolean> result = new SettableFuture<>();
final String draftText = getIntent().getStringExtra(TEXT_EXTRA);
final Uri draftMedia = getIntent().getData();
final MediaType draftMediaType = MediaType.from(getIntent().getType());
final List<Media> mediaList = getIntent().getParcelableArrayListExtra(MEDIA_EXTRA);
final String draftText = getIntent().getStringExtra(TEXT_EXTRA);
final Uri draftMedia = getIntent().getData();
final MediaType draftMediaType = MediaType.from(getIntent().getType());
final List<Media> mediaList = getIntent().getParcelableArrayListExtra(MEDIA_EXTRA);
final StickerLocator stickerLocator = getIntent().getParcelableExtra(STICKER_EXTRA);
if (stickerLocator != null && draftMedia != null) {
sendSticker(stickerLocator, draftMedia, 0, true);
return new SettableFuture<>(false);
}
if (!Util.isEmpty(mediaList)) {
Intent sendIntent = MediaSendActivity.buildEditorIntent(this, mediaList, recipient, draftText, sendButton.getSelectedTransport());
@@ -1203,7 +1227,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
if (draftText != null) {
composeText.setText(draftText);
composeText.setText("");
composeText.append(draftText);
result.set(true);
}
@@ -1565,7 +1590,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private void initializeLinkPreviewObserver() {
linkPreviewViewModel = ViewModelProviders.of(this, new LinkPreviewViewModel.Factory(new LinkPreviewRepository())).get(LinkPreviewViewModel.class);
linkPreviewViewModel = ViewModelProviders.of(this, new LinkPreviewViewModel.Factory(new LinkPreviewRepository(this))).get(LinkPreviewViewModel.class);
if (!TextSecurePreferences.isLinkPreviewsEnabled(this)) {
linkPreviewViewModel.onUserCancel();
@@ -1602,6 +1627,52 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
});
}
private void initializeStickerObserver() {
StickerSearchRepository repository = new StickerSearchRepository(this);
stickerViewModel = ViewModelProviders.of(this, new ConversationStickerViewModel.Factory(getApplication(), repository))
.get(ConversationStickerViewModel.class);
stickerViewModel.getStickerResults().observe(this, stickers -> {
if (stickers == null) return;
inputPanel.setStickerSuggestions(stickers);
});
stickerViewModel.getStickersAvailability().observe(this, stickersAvailable -> {
if (stickersAvailable == null) return;
boolean isSystemEmojiPreferred = TextSecurePreferences.isSystemEmojiPreferred(this);
MediaKeyboardMode keyboardMode = TextSecurePreferences.getMediaKeyboardMode(this);
boolean stickerIntro = !TextSecurePreferences.hasSeenStickerIntroTooltip(this);
if (stickersAvailable) {
inputPanel.showMediaKeyboardToggle(true);
inputPanel.setMediaKeyboardToggleMode(isSystemEmojiPreferred || keyboardMode == MediaKeyboardMode.STICKER);
if (stickerIntro) showStickerIntroductionTooltip();
}
if (emojiDrawerStub.resolved()) {
initializeMediaKeyboardProviders(emojiDrawerStub.get(), stickersAvailable);
}
});
}
private void showStickerIntroductionTooltip() {
TextSecurePreferences.setMediaKeyboardMode(this, MediaKeyboardMode.STICKER);
inputPanel.setMediaKeyboardToggleMode(true);
TooltipPopup.forTarget(inputPanel.getMediaKeyboardToggleAnchorView())
.setBackgroundTint(getResources().getColor(R.color.core_blue))
.setTextColor(getResources().getColor(R.color.core_white))
.setText(R.string.ConversationActivity_new_say_it_with_stickers)
.setOnDismissListener(() -> {
TextSecurePreferences.setHasSeenStickerIntroTooltip(this, true);
EventBus.getDefault().removeStickyEvent(StickerPackInstallEvent.class);
})
.show(TooltipPopup.POSITION_ABOVE);
}
@Override
public void onSearchMoveUpPressed() {
searchViewModel.onMoveUp();
@@ -1648,6 +1719,17 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
initializeIdentityRecords();
}
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
public void onStickerPackInstalled(final StickerPackInstallEvent event) {
if (!TextSecurePreferences.hasSeenStickerIntroTooltip(this)) return;
EventBus.getDefault().removeStickyEvent(event);
TooltipPopup.forTarget(inputPanel.getMediaKeyboardToggleAnchorView())
.setText(R.string.ConversationActivity_sticker_pack_installed)
.setIconGlideModel(event.getIconGlideModel())
.show(TooltipPopup.POSITION_ABOVE);
}
private void initializeReceivers() {
securityUpdateReceiver = new BroadcastReceiver() {
@Override
@@ -1725,7 +1807,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
long expiresIn = recipient.getExpireMessages() * 1000L;
boolean initiating = threadId == -1;
sendMediaMessage(isSmsForced(), "", attachmentManager.buildSlideDeck(), contacts, Collections.emptyList(), expiresIn, subscriptionId, initiating);
sendMediaMessage(isSmsForced(), "", attachmentManager.buildSlideDeck(), null, contacts, Collections.emptyList(), expiresIn, subscriptionId, initiating, false);
}
private void selectContactInfo(ContactData contactData) {
@@ -1868,6 +1950,26 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
}
private void initializeMediaKeyboardProviders(@NonNull MediaKeyboard mediaKeyboard, boolean stickersAvailable) {
boolean isSystemEmojiPreferred = TextSecurePreferences.isSystemEmojiPreferred(this);
if (stickersAvailable) {
if (isSystemEmojiPreferred) {
mediaKeyboard.setProviders(0, new StickerKeyboardProvider(this, this));
} else {
MediaKeyboardMode keyboardMode = TextSecurePreferences.getMediaKeyboardMode(this);
int index = keyboardMode == MediaKeyboardMode.STICKER ? 1 : 0;
mediaKeyboard.setProviders(index,
new EmojiKeyboardProvider(this, inputPanel),
new StickerKeyboardProvider(this, this));
}
} else if (!isSystemEmojiPreferred) {
mediaKeyboard.setProviders(0, new EmojiKeyboardProvider(this, inputPanel));
}
}
private boolean isSingleConversation() {
return getRecipient() != null && !getRecipient().isGroupRecipient();
}
@@ -2047,17 +2149,19 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
throws InvalidMessageException
{
Log.i(TAG, "Sending media message...");
sendMediaMessage(forceSms, getMessage(), attachmentManager.buildSlideDeck(), Collections.emptyList(), linkPreviewViewModel.getActiveLinkPreviews(), expiresIn, subscriptionId, initiating);
sendMediaMessage(forceSms, getMessage(), attachmentManager.buildSlideDeck(), inputPanel.getQuote().orNull(), Collections.emptyList(), linkPreviewViewModel.getActiveLinkPreviews(), expiresIn, subscriptionId, initiating, true);
}
private ListenableFuture<Void> sendMediaMessage(final boolean forceSms,
String body,
SlideDeck slideDeck,
QuoteModel quote,
List<Contact> contacts,
List<LinkPreview> previews,
final long expiresIn,
final int subscriptionId,
final boolean initiating)
final boolean initiating,
final boolean clearComposeBox)
{
if (!isDefaultSms && (!isSecureText || forceSms)) {
showDefaultSmsPrompt();
@@ -2073,7 +2177,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
}
OutgoingMediaMessage outgoingMessageCandidate = new OutgoingMediaMessage(recipient, slideDeck, body, System.currentTimeMillis(), subscriptionId, expiresIn, distributionType, inputPanel.getQuote().orNull(), contacts, previews);
OutgoingMediaMessage outgoingMessageCandidate = new OutgoingMediaMessage(recipient, slideDeck, body, System.currentTimeMillis(), subscriptionId, expiresIn, distributionType, quote, contacts, previews);
final SettableFuture<Void> future = new SettableFuture<>();
final Context context = getApplicationContext();
@@ -2092,9 +2196,12 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
.ifNecessary(!isSecureText || forceSms)
.withPermanentDenialDialog(getString(R.string.ConversationActivity_signal_needs_sms_permission_in_order_to_send_an_sms))
.onAllGranted(() -> {
inputPanel.clearQuote();
attachmentManager.clear(glideRequests, false);
silentlySetComposeText("");
if (clearComposeBox) {
inputPanel.clearQuote();
attachmentManager.clear(glideRequests, false);
silentlySetComposeText("");
}
final long id = fragment.stageOutgoingMessage(outgoingMessage);
new AsyncTask<Void, Void, Long>() {
@@ -2271,7 +2378,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
SlideDeck slideDeck = new SlideDeck();
slideDeck.addSlide(audioSlide);
sendMediaMessage(forceSms, "", slideDeck, Collections.emptyList(), Collections.emptyList(), expiresIn, subscriptionId, initiating).addListener(new AssertedSuccessListener<Void>() {
sendMediaMessage(forceSms, "", slideDeck, inputPanel.getQuote().orNull(), Collections.emptyList(), Collections.emptyList(), expiresIn, subscriptionId, initiating, true).addListener(new AssertedSuccessListener<Void>() {
@Override
public void onSuccess(Void nothing) {
new AsyncTask<Void, Void, Void>() {
@@ -2321,8 +2428,11 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Override
public void onEmojiToggle() {
if (!emojiDrawerStub.resolved()) {
inputPanel.setEmojiDrawer(emojiDrawerStub.get());
emojiDrawerStub.get().setEmojiEventListener(inputPanel);
Boolean stickersAvailable = stickerViewModel.getStickersAvailability().getValue();
initializeMediaKeyboardProviders(emojiDrawerStub.get(), stickersAvailable == null ? false : stickersAvailable);
inputPanel.setMediaKeyboard(emojiDrawerStub.get());
}
if (container.getCurrentInput() == emojiDrawerStub.get()) {
@@ -2337,6 +2447,11 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
linkPreviewViewModel.onUserCancel();
}
@Override
public void onStickerSuggestionSelected(@NonNull StickerRecord sticker) {
sendSticker(sticker, true);
}
@Override
public void onMediaSelected(@NonNull Uri uri, String contentType) {
if (!TextUtils.isEmpty(contentType) && contentType.trim().equals("image/gif")) {
@@ -2355,6 +2470,46 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
linkPreviewViewModel.onTextChanged(this, composeText.getTextTrimmed(), start, end);
}
@Override
public void onStickerSelected(@NonNull StickerRecord stickerRecord) {
sendSticker(stickerRecord, false);
}
@Override
public void onStickerManagementClicked() {
startActivity(StickerManagementActivity.getIntent(this));
container.hideAttachedInput(true);
}
private void sendSticker(@NonNull StickerRecord stickerRecord, boolean clearCompose) {
sendSticker(new StickerLocator(stickerRecord.getPackId(), stickerRecord.getPackKey(), stickerRecord.getStickerId()), stickerRecord.getUri(), stickerRecord.getSize(), clearCompose);
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
DatabaseFactory.getStickerDatabase(this).updateStickerLastUsedTime(stickerRecord.getRowId(), System.currentTimeMillis());
});
}
private void sendSticker(@NonNull StickerLocator stickerLocator, @NonNull Uri uri, long size, boolean clearCompose) {
if (sendButton.getSelectedTransport().isSms()) {
Media media = new Media(uri, MediaUtil.IMAGE_WEBP, System.currentTimeMillis(), StickerSlide.WIDTH, StickerSlide.HEIGHT, size, Optional.absent(), Optional.absent());
Intent intent = MediaSendActivity.buildEditorIntent(this, Collections.singletonList(media), recipient, composeText.getTextTrimmed(), sendButton.getSelectedTransport());
startActivityForResult(intent, MEDIA_SENDER);
return;
}
long expiresIn = recipient.getExpireMessages() * 1000L;
int subscriptionId = sendButton.getSelectedTransport().getSimSubscriptionId().or(-1);
boolean initiating = threadId == -1;
TransportOption transport = sendButton.getSelectedTransport();
SlideDeck slideDeck = new SlideDeck();
Slide stickerSlide = new StickerSlide(this, uri, size, stickerLocator);
slideDeck.addSlide(stickerSlide);
sendMediaMessage(transport.isSms(), "", slideDeck, null, Collections.emptyList(), Collections.emptyList(), expiresIn, subscriptionId, initiating, clearCompose);
}
private void silentlySetComposeText(String text) {
typingTextWatcher.setEnabled(false);
composeText.setText(text);
@@ -2460,6 +2615,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
if (composeText.getTextTrimmed().length() == 0 || beforeLength == 0) {
composeText.postDelayed(ConversationActivity.this::updateToggleButtonState, 50);
}
stickerViewModel.onInputTextUpdated(s.toString());
}
@Override
@@ -2529,7 +2686,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
author,
messageRecord.getBody(),
slideDeck);
} else {
inputPanel.setQuote(GlideApp.with(this),
messageRecord.getDateSent(),

View File

@@ -23,10 +23,6 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
import android.support.v7.widget.RecyclerView;
import org.thoughtcrime.securesms.BindableConversationItem;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.logging.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -34,14 +30,17 @@ import android.widget.TextView;
import com.annimon.stream.Stream;
import org.thoughtcrime.securesms.conversation.ConversationAdapter.HeaderViewHolder;
import org.thoughtcrime.securesms.BindableConversationItem;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
import org.thoughtcrime.securesms.conversation.ConversationAdapter.HeaderViewHolder;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.FastCursorRecyclerViewAdapter;
import org.thoughtcrime.securesms.database.MmsSmsColumns;
import org.thoughtcrime.securesms.database.MmsSmsDatabase;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.recipients.Recipient;
@@ -296,11 +295,16 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
@Override
protected long getItemId(@NonNull MessageRecord record) {
if (record.isOutgoing() && record.isMms()) {
SlideDeck slideDeck = ((MmsMessageRecord)record).getSlideDeck();
MmsMessageRecord mmsRecord = (MmsMessageRecord) record;
SlideDeck slideDeck = mmsRecord.getSlideDeck();
if (slideDeck.getThumbnailSlide() != null && slideDeck.getThumbnailSlide().getFastPreflightId() != null) {
return Long.valueOf(slideDeck.getThumbnailSlide().getFastPreflightId());
}
if (slideDeck.getStickerSlide() != null && slideDeck.getStickerSlide().getFastPreflightId() != null) {
return Long.valueOf(slideDeck.getStickerSlide().getFastPreflightId());
}
}
return record.getId();

View File

@@ -41,19 +41,6 @@ import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.OnScrollListener;
import android.text.ClipboardManager;
import android.text.TextUtils;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.MessageDetailsActivity;
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.ShareActivity;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.components.ConversationTypingView;
import org.thoughtcrime.securesms.components.recyclerview.SmoothScrollingLinearLayoutManager;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.linkpreview.LinkPreview;
import org.thoughtcrime.securesms.logging.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@@ -69,11 +56,20 @@ import android.widget.ViewSwitcher;
import com.annimon.stream.Stream;
import org.thoughtcrime.securesms.conversation.ConversationAdapter.HeaderViewHolder;
import org.thoughtcrime.securesms.conversation.ConversationAdapter.ItemClickListener;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.MessageDetailsActivity;
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.ShareActivity;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.components.ConversationTypingView;
import org.thoughtcrime.securesms.components.recyclerview.SmoothScrollingLinearLayoutManager;
import org.thoughtcrime.securesms.contactshare.Contact;
import org.thoughtcrime.securesms.contactshare.ContactUtil;
import org.thoughtcrime.securesms.contactshare.SharedContactDetailsActivity;
import org.thoughtcrime.securesms.contactshare.Contact;
import org.thoughtcrime.securesms.conversation.ConversationAdapter.HeaderViewHolder;
import org.thoughtcrime.securesms.conversation.ConversationAdapter.ItemClickListener;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MmsSmsDatabase;
import org.thoughtcrime.securesms.database.RecipientDatabase;
@@ -82,6 +78,8 @@ import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob;
import org.thoughtcrime.securesms.linkpreview.LinkPreview;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.longmessage.LongMessageActivity;
import org.thoughtcrime.securesms.mediasend.Media;
import org.thoughtcrime.securesms.mms.GlideApp;
@@ -92,6 +90,8 @@ import org.thoughtcrime.securesms.profiles.UnknownSenderView;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
import org.thoughtcrime.securesms.stickers.StickerLocator;
import org.thoughtcrime.securesms.stickers.StickerPackPreviewActivity;
import org.thoughtcrime.securesms.util.CommunicationActions;
import org.thoughtcrime.securesms.util.SaveAttachmentTask;
import org.thoughtcrime.securesms.util.StickyHeaderDecoration;
@@ -379,10 +379,11 @@ public class ConversationFragment extends Fragment
MessageRecord messageRecord = messageRecords.iterator().next();
menu.findItem(R.id.menu_context_resend).setVisible(messageRecord.isFailed());
menu.findItem(R.id.menu_context_save_attachment).setVisible(!actionMessage &&
messageRecord.isMms() &&
!messageRecord.isMmsNotification() &&
((MediaMmsMessageRecord)messageRecord).containsMediaSlide());
menu.findItem(R.id.menu_context_save_attachment).setVisible(!actionMessage &&
messageRecord.isMms() &&
!messageRecord.isMmsNotification() &&
((MediaMmsMessageRecord)messageRecord).containsMediaSlide() &&
((MediaMmsMessageRecord)messageRecord).getSlideDeck().getStickerSlide() == null);
menu.findItem(R.id.menu_context_forward).setVisible(!actionMessage && !sharedContact);
menu.findItem(R.id.menu_context_details).setVisible(!actionMessage);
@@ -528,10 +529,11 @@ public class ConversationFragment extends Fragment
if (message.isMms()) {
MmsMessageRecord mediaMessage = (MmsMessageRecord) message;
boolean isAlbum = mediaMessage.containsMediaSlide() &&
mediaMessage.getSlideDeck().getSlides().size() > 1 &&
mediaMessage.getSlideDeck().getAudioSlide() == null &&
mediaMessage.getSlideDeck().getDocumentSlide() == null;
boolean isAlbum = mediaMessage.containsMediaSlide() &&
mediaMessage.getSlideDeck().getSlides().size() > 1 &&
mediaMessage.getSlideDeck().getAudioSlide() == null &&
mediaMessage.getSlideDeck().getDocumentSlide() == null &&
mediaMessage.getSlideDeck().getStickerSlide() == null;
if (isAlbum) {
ArrayList<Media> mediaList = new ArrayList<>(mediaMessage.getSlideDeck().getSlides().size());
@@ -562,6 +564,10 @@ public class ConversationFragment extends Fragment
Slide slide = mediaMessage.getSlideDeck().getSlides().get(0);
composeIntent.putExtra(Intent.EXTRA_STREAM, slide.getUri());
composeIntent.setType(slide.getContentType());
if (slide.hasSticker()) {
composeIntent.putExtra(ConversationActivity.STICKER_EXTRA, slide.asAttachment().getSticker());
}
}
if (mediaMessage.getSlideDeck().getTextSlide() != null && mediaMessage.getSlideDeck().getTextSlide().getUri() != null) {
@@ -941,6 +947,13 @@ public class ConversationFragment extends Fragment
}
}
@Override
public void onStickerClicked(@NonNull StickerLocator sticker) {
if (getContext() != null && getActivity() != null) {
startActivity(StickerPackPreviewActivity.getIntent(sticker.getPackId(), sticker.getPackKey()));
}
}
@Override
public void onSharedContactDetailsClicked(@NonNull Contact contact, @NonNull View avatarTransitionView) {
if (getContext() != null && getActivity() != null) {

View File

@@ -42,20 +42,6 @@ import android.text.style.ForegroundColorSpan;
import android.text.style.URLSpan;
import android.text.util.Linkify;
import android.util.AttributeSet;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.BindableConversationItem;
import org.thoughtcrime.securesms.ConfirmIdentityDialog;
import org.thoughtcrime.securesms.MediaPreviewActivity;
import org.thoughtcrime.securesms.MessageDetailsActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.components.LinkPreviewView;
import org.thoughtcrime.securesms.components.emoji.EmojiTextView;
import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.linkpreview.LinkPreview;
import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil;
import org.thoughtcrime.securesms.logging.Log;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
@@ -65,6 +51,13 @@ import android.widget.Toast;
import com.annimon.stream.Stream;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.BindableConversationItem;
import org.thoughtcrime.securesms.ConfirmIdentityDialog;
import org.thoughtcrime.securesms.MediaPreviewActivity;
import org.thoughtcrime.securesms.MessageDetailsActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
import org.thoughtcrime.securesms.components.AlertView;
import org.thoughtcrime.securesms.components.AudioView;
@@ -72,9 +65,13 @@ import org.thoughtcrime.securesms.components.AvatarImageView;
import org.thoughtcrime.securesms.components.ConversationItemFooter;
import org.thoughtcrime.securesms.components.ConversationItemThumbnail;
import org.thoughtcrime.securesms.components.DocumentView;
import org.thoughtcrime.securesms.components.LinkPreviewView;
import org.thoughtcrime.securesms.components.QuoteView;
import org.thoughtcrime.securesms.components.SharedContactView;
import org.thoughtcrime.securesms.components.StickerView;
import org.thoughtcrime.securesms.components.emoji.EmojiTextView;
import org.thoughtcrime.securesms.contactshare.Contact;
import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.MmsSmsDatabase;
@@ -88,6 +85,9 @@ import org.thoughtcrime.securesms.jobs.AttachmentDownloadJob;
import org.thoughtcrime.securesms.jobs.MmsDownloadJob;
import org.thoughtcrime.securesms.jobs.MmsSendJob;
import org.thoughtcrime.securesms.jobs.SmsSendJob;
import org.thoughtcrime.securesms.linkpreview.LinkPreview;
import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.mms.ImageSlide;
import org.thoughtcrime.securesms.mms.PartAuthority;
@@ -97,12 +97,14 @@ import org.thoughtcrime.securesms.mms.SlidesClickedListener;
import org.thoughtcrime.securesms.mms.TextSlide;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
import org.thoughtcrime.securesms.stickers.StickerUrl;
import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.LongClickCopySpan;
import org.thoughtcrime.securesms.util.LongClickMovementMethod;
import org.thoughtcrime.securesms.util.SearchUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.ThemeUtil;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.views.Stub;
@@ -140,6 +142,7 @@ public class ConversationItem extends LinearLayout
private QuoteView quoteView;
private EmojiTextView bodyText;
private ConversationItemFooter footer;
private ConversationItemFooter stickerFooter;
private TextView groupSender;
private TextView groupSenderProfileName;
private View groupSenderHolder;
@@ -155,6 +158,7 @@ public class ConversationItem extends LinearLayout
private Stub<DocumentView> documentViewStub;
private Stub<SharedContactView> sharedContactStub;
private Stub<LinkPreviewView> linkPreviewStub;
private Stub<StickerView> stickerStub;
private @Nullable EventListener eventListener;
private int defaultBubbleColor;
@@ -191,6 +195,7 @@ public class ConversationItem extends LinearLayout
this.bodyText = findViewById(R.id.conversation_item_body);
this.footer = findViewById(R.id.conversation_item_footer);
this.stickerFooter = findViewById(R.id.conversation_item_sticker_footer);
this.groupSender = findViewById(R.id.group_message_sender);
this.groupSenderProfileName = findViewById(R.id.group_message_sender_profile);
this.alertView = findViewById(R.id.indicators_parent);
@@ -202,6 +207,7 @@ public class ConversationItem extends LinearLayout
this.documentViewStub = new Stub<>(findViewById(R.id.document_view_stub));
this.sharedContactStub = new Stub<>(findViewById(R.id.shared_contact_view_stub));
this.linkPreviewStub = new Stub<>(findViewById(R.id.link_preview_stub));
this.stickerStub = new Stub<>(findViewById(R.id.sticker_view_stub));
this.groupSenderHolder = findViewById(R.id.group_sender_holder);
this.quoteView = findViewById(R.id.quote_view);
this.container = findViewById(R.id.container);
@@ -245,6 +251,7 @@ public class ConversationItem extends LinearLayout
setStatusIcons(messageRecord);
setContactPhoto(recipient);
setGroupMessageStatus(messageRecord, recipient);
setGroupAuthorColor(messageRecord);
setAuthor(messageRecord, previousMessageRecord, nextMessageRecord, groupThread);
setQuote(messageRecord, previousMessageRecord, nextMessageRecord, groupThread);
setMessageSpacing(context, messageRecord, previousMessageRecord, nextMessageRecord, groupThread);
@@ -398,8 +405,16 @@ public class ConversationItem extends LinearLayout
return messageRecord.isMms() && ((MmsMessageRecord)messageRecord).getSlideDeck().getThumbnailSlide() != null;
}
private boolean hasSticker(MessageRecord messageRecord) {
return messageRecord.isMms() && ((MmsMessageRecord)messageRecord).getSlideDeck().getStickerSlide() != null;
}
private boolean hasOnlyThumbnail(MessageRecord messageRecord) {
return hasThumbnail(messageRecord) && !hasAudio(messageRecord) && !hasDocument(messageRecord) && !hasSharedContact(messageRecord);
return hasThumbnail(messageRecord) &&
!hasAudio(messageRecord) &&
!hasDocument(messageRecord) &&
!hasSharedContact(messageRecord) &&
!hasSticker(messageRecord);
}
private boolean hasDocument(MessageRecord messageRecord) {
@@ -462,6 +477,7 @@ public class ConversationItem extends LinearLayout
if (mediaThumbnailStub.resolved()) mediaThumbnailStub.get().setVisibility(View.GONE);
if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE);
if (linkPreviewStub.resolved()) linkPreviewStub.get().setVisibility(GONE);
if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE);
sharedContactStub.get().setContact(((MediaMmsMessageRecord) messageRecord).getSharedContacts().get(0), glideRequests, locale);
sharedContactStub.get().setEventListener(sharedContactEventListener);
@@ -479,11 +495,12 @@ public class ConversationItem extends LinearLayout
if (mediaThumbnailStub.resolved()) mediaThumbnailStub.get().setVisibility(View.GONE);
if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE);
if (sharedContactStub.resolved()) sharedContactStub.get().setVisibility(GONE);
if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE);
//noinspection ConstantConditions
LinkPreview linkPreview = ((MmsMessageRecord) messageRecord).getLinkPreviews().get(0);
if (linkPreview.getThumbnail().isPresent() && shouldPromotePreviewImage(linkPreview.getThumbnail().get())) {
if (linkPreview.getThumbnail().isPresent() && shouldPromotePreviewImage(linkPreview.getThumbnail().get(), linkPreview.getUrl())) {
mediaThumbnailStub.get().setVisibility(VISIBLE);
mediaThumbnailStub.get().setImageResource(glideRequests, Collections.singletonList(new ImageSlide(context, linkPreview.getThumbnail().get())), showControls, false);
mediaThumbnailStub.get().setThumbnailClickListener(new LinkPreviewThumbnailClickListener());
@@ -516,6 +533,7 @@ public class ConversationItem extends LinearLayout
if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE);
if (sharedContactStub.resolved()) sharedContactStub.get().setVisibility(GONE);
if (linkPreviewStub.resolved()) linkPreviewStub.get().setVisibility(GONE);
if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE);
//noinspection ConstantConditions
audioViewStub.get().setAudio(((MediaMmsMessageRecord) messageRecord).getSlideDeck().getAudioSlide(), showControls);
@@ -524,6 +542,7 @@ public class ConversationItem extends LinearLayout
ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
footer.setVisibility(VISIBLE);
} else if (hasDocument(messageRecord)) {
documentViewStub.get().setVisibility(View.VISIBLE);
@@ -531,15 +550,38 @@ public class ConversationItem extends LinearLayout
if (audioViewStub.resolved()) audioViewStub.get().setVisibility(View.GONE);
if (sharedContactStub.resolved()) sharedContactStub.get().setVisibility(GONE);
if (linkPreviewStub.resolved()) linkPreviewStub.get().setVisibility(GONE);
if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE);
//noinspection ConstantConditions
documentViewStub.get().setDocument(((MediaMmsMessageRecord)messageRecord).getSlideDeck().getDocumentSlide(), showControls);
documentViewStub.get().setDocument(((MediaMmsMessageRecord) messageRecord).getSlideDeck().getDocumentSlide(), showControls);
documentViewStub.get().setDocumentClickListener(new ThumbnailClickListener());
documentViewStub.get().setDownloadClickListener(singleDownloadClickListener);
documentViewStub.get().setOnLongClickListener(passthroughClickListener);
ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
footer.setVisibility(VISIBLE);
} else if (hasSticker(messageRecord) && isCaptionlessMms(messageRecord)) {
bodyBubble.setBackgroundColor(Color.TRANSPARENT);
stickerStub.get().setVisibility(View.VISIBLE);
if (mediaThumbnailStub.resolved()) mediaThumbnailStub.get().setVisibility(View.GONE);
if (audioViewStub.resolved()) audioViewStub.get().setVisibility(View.GONE);
if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE);
if (sharedContactStub.resolved()) sharedContactStub.get().setVisibility(GONE);
if (linkPreviewStub.resolved()) linkPreviewStub.get().setVisibility(GONE);
//noinspection ConstantConditions
stickerStub.get().setSticker(glideRequests, ((MmsMessageRecord) messageRecord).getSlideDeck().getStickerSlide());
stickerStub.get().setThumbnailClickListener(new StickerClickListener());
stickerStub.get().setDownloadClickListener(downloadClickListener);
stickerStub.get().setOnLongClickListener(passthroughClickListener);
stickerStub.get().setOnClickListener(passthroughClickListener);
ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
footer.setVisibility(VISIBLE);
} else if (hasThumbnail(messageRecord)) {
mediaThumbnailStub.get().setVisibility(View.VISIBLE);
@@ -547,6 +589,7 @@ public class ConversationItem extends LinearLayout
if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE);
if (sharedContactStub.resolved()) sharedContactStub.get().setVisibility(GONE);
if (linkPreviewStub.resolved()) linkPreviewStub.get().setVisibility(GONE);
if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE);
//noinspection ConstantConditions
List<Slide> thumbnailSlides = ((MmsMessageRecord) messageRecord).getSlideDeck().getThumbnailSlides();
@@ -561,11 +604,13 @@ public class ConversationItem extends LinearLayout
mediaThumbnailStub.get().showShade(TextUtils.isEmpty(messageRecord.getDisplayBody(getContext())) && !hasExtraText(messageRecord));
mediaThumbnailStub.get().setConversationColor(messageRecord.isOutgoing() ? defaultBubbleColor
: messageRecord.getRecipient().getColor().toConversationColor(context));
mediaThumbnailStub.get().setBorderless(false);
setThumbnailCorners(messageRecord, previousRecord, nextRecord, isGroupThread);
ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
footer.setVisibility(VISIBLE);
} else {
if (mediaThumbnailStub.resolved()) mediaThumbnailStub.get().setVisibility(View.GONE);
@@ -573,9 +618,11 @@ public class ConversationItem extends LinearLayout
if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE);
if (sharedContactStub.resolved()) sharedContactStub.get().setVisibility(GONE);
if (linkPreviewStub.resolved()) linkPreviewStub.get().setVisibility(GONE);
if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE);
ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
footer.setVisibility(VISIBLE);
}
}
@@ -675,9 +722,9 @@ public class ConversationItem extends LinearLayout
contactPhoto.setAvatar(glideRequests, recipient, true);
}
private boolean shouldPromotePreviewImage(@NonNull Attachment attachment) {
private boolean shouldPromotePreviewImage(@NonNull Attachment attachment, @NonNull String url) {
int minWidth = getResources().getDimensionPixelSize(R.dimen.media_bubble_min_width);
return attachment.getWidth() >= minWidth;
return attachment.getWidth() >= minWidth && !StickerUrl.isValidShareLink(url);
}
private SpannableString linkifyMessageBody(SpannableString messageBody, boolean shouldLinkifyAllLinks) {
@@ -715,7 +762,7 @@ public class ConversationItem extends LinearLayout
private void setQuote(@NonNull MessageRecord current, @NonNull Optional<MessageRecord> previous, @NonNull Optional<MessageRecord> next, boolean isGroupThread) {
if (current.isMms() && !current.isMmsNotification() && ((MediaMmsMessageRecord)current).getQuote() != null) {
Quote quote = ((MediaMmsMessageRecord)current).getQuote();
assert quote != null;
//noinspection ConstantConditions
quoteView.setQuote(glideRequests, quote.getId(), Recipient.from(context, quote.getAuthor(), true), quote.getText(), quote.isOriginalMissing(), quote.getAttachment());
quoteView.setVisibility(View.VISIBLE);
quoteView.getLayoutParams().width = ViewGroup.LayoutParams.WRAP_CONTENT;
@@ -770,6 +817,7 @@ public class ConversationItem extends LinearLayout
ViewUtil.updateLayoutParams(footer, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
footer.setVisibility(GONE);
stickerFooter.setVisibility(GONE);
if (sharedContactStub.resolved()) sharedContactStub.get().getFooter().setVisibility(GONE);
if (mediaThumbnailStub.resolved()) mediaThumbnailStub.get().getFooter().setVisibility(GONE);
@@ -785,7 +833,9 @@ public class ConversationItem extends LinearLayout
}
private ConversationItemFooter getActiveFooter(@NonNull MessageRecord messageRecord) {
if (hasSharedContact(messageRecord)) {
if (hasSticker(messageRecord)) {
return stickerFooter;
} else if (hasSharedContact(messageRecord)) {
return sharedContactStub.get().getFooter();
} else if (hasOnlyThumbnail(messageRecord) && TextUtils.isEmpty(messageRecord.getDisplayBody(getContext()))) {
return mediaThumbnailStub.get().getFooter();
@@ -820,6 +870,16 @@ public class ConversationItem extends LinearLayout
}
}
private void setGroupAuthorColor(@NonNull MessageRecord messageRecord) {
if (hasSticker(messageRecord)) {
groupSender.setTextColor(ThemeUtil.getThemedColor(context, R.attr.conversation_sticker_author_color));
groupSenderProfileName.setTextColor(ThemeUtil.getThemedColor(context, R.attr.conversation_sticker_author_color));
} else {
groupSender.setTextColor(ThemeUtil.getThemedColor(context, R.attr.conversation_item_received_text_primary_color));
groupSenderProfileName.setTextColor(ThemeUtil.getThemedColor(context, R.attr.conversation_item_received_text_primary_color));
}
}
private void setAuthor(@NonNull MessageRecord current, @NonNull Optional<MessageRecord> previous, @NonNull Optional<MessageRecord> next, boolean isGroupThread) {
if (isGroupThread && !current.isOutgoing()) {
contactPhotoHolder.setVisibility(VISIBLE);
@@ -1072,6 +1132,18 @@ public class ConversationItem extends LinearLayout
}
}
private class StickerClickListener implements SlideClickListener {
@Override
public void onClick(View v, Slide slide) {
if (shouldInterceptClicks(messageRecord) || !batchSelected.isEmpty()) {
performClick();
} else if (eventListener != null && hasSticker(messageRecord)){
//noinspection ConstantConditions
eventListener.onStickerClicked(((MmsMessageRecord) messageRecord).getSlideDeck().getStickerSlide().asAttachment().getSticker());
}
}
}
private class ThumbnailClickListener implements SlideClickListener {
public void onClick(final View v, final Slide slide) {
if (shouldInterceptClicks(messageRecord) || !batchSelected.isEmpty()) {

View File

@@ -4,7 +4,6 @@ import android.app.Application;
import android.arch.lifecycle.AndroidViewModel;
import android.arch.lifecycle.LiveData;
import android.content.Context;
import android.os.AsyncTask;
import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.contacts.ContactAccessor;
@@ -15,6 +14,7 @@ import org.thoughtcrime.securesms.search.model.MessageResult;
import org.thoughtcrime.securesms.util.CloseableLiveData;
import org.thoughtcrime.securesms.util.Debouncer;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
import java.io.Closeable;
import java.util.List;
@@ -40,7 +40,7 @@ public class ConversationSearchViewModel extends AndroidViewModel {
DatabaseFactory.getContactsDatabase(context),
DatabaseFactory.getThreadDatabase(context),
ContactAccessor.getInstance(),
AsyncTask.THREAD_POOL_EXECUTOR);
SignalExecutors.SERIAL);
}
LiveData<SearchResult> getSearchResults() {

View File

@@ -0,0 +1,85 @@
package org.thoughtcrime.securesms.conversation;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.model.StickerRecord;
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri;
import org.thoughtcrime.securesms.mms.GlideRequests;
import java.util.ArrayList;
import java.util.List;
public class ConversationStickerSuggestionAdapter extends RecyclerView.Adapter<ConversationStickerSuggestionAdapter.StickerSuggestionViewHolder> {
private final GlideRequests glideRequests;
private final EventListener eventListener;
private final List<StickerRecord> stickers;
public ConversationStickerSuggestionAdapter(@NonNull GlideRequests glideRequests, @NonNull EventListener eventListener) {
this.glideRequests = glideRequests;
this.eventListener = eventListener;
this.stickers = new ArrayList<>();
}
@Override
public @NonNull StickerSuggestionViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
return new StickerSuggestionViewHolder(LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.sticker_suggestion_list_item, viewGroup, false));
}
@Override
public void onBindViewHolder(@NonNull StickerSuggestionViewHolder viewHolder, int i) {
viewHolder.bind(glideRequests, eventListener, stickers.get(i));
}
@Override
public void onViewRecycled(@NonNull StickerSuggestionViewHolder holder) {
holder.recycle();
}
@Override
public int getItemCount() {
return stickers.size();
}
public void setStickers(@NonNull List<StickerRecord> stickers) {
this.stickers.clear();
this.stickers.addAll(stickers);
notifyDataSetChanged();
}
static class StickerSuggestionViewHolder extends RecyclerView.ViewHolder {
private final ImageView image;
StickerSuggestionViewHolder(@NonNull View itemView) {
super(itemView);
this.image = itemView.findViewById(R.id.sticker_suggestion_item_image);
}
void bind(@NonNull GlideRequests glideRequests, @NonNull EventListener eventListener, @NonNull StickerRecord sticker) {
glideRequests.load(new DecryptableUri(sticker.getUri()))
.transition(DrawableTransitionOptions.withCrossFade())
.into(image);
itemView.setOnClickListener(v -> {
eventListener.onStickerSuggestionClicked(sticker);
});
}
void recycle() {
itemView.setOnClickListener(null);
}
}
public interface EventListener {
void onStickerSuggestionClicked(@NonNull StickerRecord sticker);
}
}

View File

@@ -0,0 +1,85 @@
package org.thoughtcrime.securesms.conversation;
import android.app.Application;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.MutableLiveData;
import android.arch.lifecycle.ViewModel;
import android.arch.lifecycle.ViewModelProvider;
import android.database.ContentObserver;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import org.thoughtcrime.securesms.database.CursorList;
import org.thoughtcrime.securesms.database.DatabaseContentProviders;
import org.thoughtcrime.securesms.database.model.StickerRecord;
import org.thoughtcrime.securesms.stickers.StickerSearchRepository;
import org.thoughtcrime.securesms.util.CloseableLiveData;
import org.thoughtcrime.securesms.util.Throttler;
class ConversationStickerViewModel extends ViewModel {
private static final int SEARCH_LIMIT = 10;
private final Application application;
private final StickerSearchRepository repository;
private final CloseableLiveData<CursorList<StickerRecord>> stickers;
private final MutableLiveData<Boolean> stickersAvailable;
private final Throttler availabilityThrottler;
private final ContentObserver packObserver;
private ConversationStickerViewModel(@NonNull Application application, @NonNull StickerSearchRepository repository) {
this.application = application;
this.repository = repository;
this.stickers = new CloseableLiveData<>();
this.stickersAvailable = new MutableLiveData<>();
this.availabilityThrottler = new Throttler(500);
this.packObserver = new ContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange) {
availabilityThrottler.publish(() -> repository.getStickerFeatureAvailability(stickersAvailable::postValue));
}
};
application.getContentResolver().registerContentObserver(DatabaseContentProviders.StickerPack.CONTENT_URI, true, packObserver);
}
@NonNull LiveData<CursorList<StickerRecord>> getStickerResults() {
return stickers;
}
@NonNull LiveData<Boolean> getStickersAvailability() {
repository.getStickerFeatureAvailability(stickersAvailable::postValue);
return stickersAvailable;
}
void onInputTextUpdated(@NonNull String text) {
if (TextUtils.isEmpty(text) || text.length() > SEARCH_LIMIT) {
stickers.setValue(CursorList.emptyList());
} else {
repository.searchByEmoji(text, stickers::postValue);
}
}
@Override
protected void onCleared() {
stickers.close();
application.getContentResolver().unregisterContentObserver(packObserver);
}
static class Factory extends ViewModelProvider.NewInstanceFactory {
private final Application application;
private final StickerSearchRepository repository;
public Factory(@NonNull Application application, @NonNull StickerSearchRepository repository) {
this.application = application;
this.repository = repository;
}
@Override
public @NonNull <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
//noinspection ConstantConditions
return modelClass.cast(new ConversationStickerViewModel(application, repository));
}
}
}