Add support for sending borderless keyboard stickers.
This commit is contained in:
parent
a9e30eefdc
commit
c9d2cef58d
8 changed files with 121 additions and 16 deletions
|
@ -7,6 +7,9 @@ import android.util.AttributeSet;
|
|||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import com.bumptech.glide.load.resource.bitmap.CenterCrop;
|
||||
import com.bumptech.glide.load.resource.bitmap.CenterInside;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import org.thoughtcrime.securesms.mms.Slide;
|
||||
|
@ -53,7 +56,14 @@ public class BorderlessImageView extends FrameLayout {
|
|||
public void setSlide(@NonNull GlideRequests glideRequests, @NonNull Slide slide) {
|
||||
boolean showControls = slide.asAttachment().getDataUri() == null;
|
||||
|
||||
image.setImageResource(glideRequests, slide, showControls, false);
|
||||
if (slide.hasSticker()) {
|
||||
image.setFit(new CenterInside());
|
||||
image.setImageResource(glideRequests, slide, showControls, false);
|
||||
} else {
|
||||
image.setFit(new CenterCrop());
|
||||
image.setImageResource(glideRequests, slide, showControls, false, slide.asAttachment().getWidth(), slide.asAttachment().getHeight());
|
||||
}
|
||||
|
||||
missingShade.setVisibility(showControls ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
|
|
|
@ -398,6 +398,10 @@ public class ThumbnailView extends FrameLayout {
|
|||
getTransferControls().showProgressSpinner();
|
||||
}
|
||||
|
||||
public void setFit(@NonNull BitmapTransformation fit) {
|
||||
this.fit = fit;
|
||||
}
|
||||
|
||||
protected void setRadius(int radius) {
|
||||
this.radius = radius;
|
||||
}
|
||||
|
|
|
@ -41,7 +41,6 @@ import android.provider.Browser;
|
|||
import android.provider.ContactsContract;
|
||||
import android.provider.Telephony;
|
||||
import android.text.Editable;
|
||||
import android.text.TextUtils;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.Gravity;
|
||||
import android.view.KeyEvent;
|
||||
|
@ -63,6 +62,7 @@ import android.widget.Toast;
|
|||
import androidx.annotation.IdRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.WorkerThread;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.widget.SearchView;
|
||||
|
@ -73,6 +73,7 @@ import androidx.core.graphics.drawable.IconCompat;
|
|||
import androidx.lifecycle.ViewModelProviders;
|
||||
|
||||
import com.annimon.stream.Stream;
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||
import com.bumptech.glide.request.target.CustomTarget;
|
||||
import com.bumptech.glide.request.transition.Transition;
|
||||
|
||||
|
@ -246,7 +247,10 @@ import java.io.IOException;
|
|||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
|
@ -1997,7 +2001,12 @@ public class ConversationActivity extends PassphraseRequiredActivity
|
|||
openContactShareEditor(uri);
|
||||
return new SettableFuture<>(false);
|
||||
} else if (MediaType.IMAGE.equals(mediaType) || MediaType.GIF.equals(mediaType) || MediaType.VIDEO.equals(mediaType)) {
|
||||
Media media = new Media(uri, MediaUtil.getMimeType(this, uri), 0, width, height, 0, 0, borderless, Optional.absent(), Optional.absent(), Optional.absent());
|
||||
String mimeType = MediaUtil.getMimeType(this, uri);
|
||||
if (mimeType == null) {
|
||||
mimeType = mediaType.toFallbackMimeType();
|
||||
}
|
||||
|
||||
Media media = new Media(uri, mimeType, 0, width, height, 0, 0, borderless, Optional.absent(), Optional.absent(), Optional.absent());
|
||||
startActivityForResult(MediaSendActivity.buildEditorIntent(ConversationActivity.this, Collections.singletonList(media), recipient.get(), composeText.getTextTrimmed(), sendButton.getSelectedTransport()), MEDIA_SENDER);
|
||||
return new SettableFuture<>(false);
|
||||
} else {
|
||||
|
@ -2363,7 +2372,7 @@ public class ConversationActivity extends PassphraseRequiredActivity
|
|||
}
|
||||
|
||||
private ListenableFuture<Void> sendMediaMessage(final boolean forceSms,
|
||||
String body,
|
||||
@NonNull String body,
|
||||
SlideDeck slideDeck,
|
||||
QuoteModel quote,
|
||||
List<Contact> contacts,
|
||||
|
@ -2651,10 +2660,10 @@ public class ConversationActivity extends PassphraseRequiredActivity
|
|||
|
||||
@Override
|
||||
public void onMediaSelected(@NonNull Uri uri, String contentType) {
|
||||
if (!TextUtils.isEmpty(contentType) && contentType.trim().equals("image/gif")) {
|
||||
setMedia(uri, MediaType.GIF);
|
||||
} else if (MediaUtil.isImageType(contentType)) {
|
||||
setMedia(uri, MediaType.IMAGE);
|
||||
if (MediaUtil.isGif(contentType) || MediaUtil.isImageType(contentType)) {
|
||||
SimpleTask.run(getLifecycle(),
|
||||
() -> getKeyboardImageDetails(uri),
|
||||
details -> sendKeyboardImage(uri, contentType, details));
|
||||
} else if (MediaUtil.isVideoType(contentType)) {
|
||||
setMedia(uri, MediaType.VIDEO);
|
||||
} else if (MediaUtil.isAudioType(contentType)) {
|
||||
|
@ -3066,6 +3075,55 @@ public class ConversationActivity extends PassphraseRequiredActivity
|
|||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private @Nullable KeyboardImageDetails getKeyboardImageDetails(@NonNull Uri uri) {
|
||||
try {
|
||||
Bitmap bitmap = glideRequests.asBitmap()
|
||||
.load(uri)
|
||||
.skipMemoryCache(true)
|
||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||
.submit()
|
||||
.get(1000, TimeUnit.MILLISECONDS);
|
||||
int topLeft = bitmap.getPixel(0, 0);
|
||||
return new KeyboardImageDetails(bitmap.getWidth(), bitmap.getHeight(), Color.alpha(topLeft) < 255);
|
||||
} catch (InterruptedException | ExecutionException | TimeoutException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void sendKeyboardImage(@NonNull Uri uri, @NonNull String contentType, @Nullable KeyboardImageDetails details) {
|
||||
if (details == null || !details.hasTransparency) {
|
||||
setMedia(uri, Objects.requireNonNull(MediaType.from(contentType)));
|
||||
return;
|
||||
}
|
||||
|
||||
long expiresIn = recipient.get().getExpireMessages() * 1000L;
|
||||
int subscriptionId = sendButton.getSelectedTransport().getSimSubscriptionId().or(-1);
|
||||
boolean initiating = threadId == -1;
|
||||
QuoteModel quote = inputPanel.getQuote().orNull();
|
||||
SlideDeck slideDeck = new SlideDeck();
|
||||
|
||||
if (MediaUtil.isGif(contentType)) {
|
||||
slideDeck.addSlide(new GifSlide(this, uri, 0, details.width, details.height, details.hasTransparency, null));
|
||||
} else if (MediaUtil.isImageType(contentType)) {
|
||||
slideDeck.addSlide(new ImageSlide(this, uri, contentType, 0, details.width, details.height, details.hasTransparency, null, null));
|
||||
} else {
|
||||
throw new AssertionError("Only images are supported!");
|
||||
}
|
||||
|
||||
sendMediaMessage(isSmsForced(),
|
||||
"",
|
||||
slideDeck,
|
||||
quote,
|
||||
Collections.emptyList(),
|
||||
Collections.emptyList(),
|
||||
expiresIn,
|
||||
false,
|
||||
subscriptionId,
|
||||
initiating,
|
||||
true);
|
||||
}
|
||||
|
||||
private class UnverifiedDismissedListener implements UnverifiedBannerView.DismissListener {
|
||||
@Override
|
||||
public void onDismissed(final List<IdentityRecord> unverifiedIdentities) {
|
||||
|
@ -3155,4 +3213,16 @@ public class ConversationActivity extends PassphraseRequiredActivity
|
|||
|
||||
messageRequestBottomView.setRecipient(recipient);
|
||||
}
|
||||
|
||||
private static class KeyboardImageDetails {
|
||||
private final int width;
|
||||
private final int height;
|
||||
private final boolean hasTransparency;
|
||||
|
||||
private KeyboardImageDetails(int width, int height, boolean hasTransparency) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.hasTransparency = hasTransparency;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -700,6 +700,7 @@ public class ConversationItem extends LinearLayout implements BindableConversati
|
|||
} else {
|
||||
//noinspection ConstantConditions
|
||||
stickerStub.get().setSlide(glideRequests, ((MmsMessageRecord) messageRecord).getSlideDeck().getThumbnailSlide());
|
||||
stickerStub.get().setThumbnailClickListener((v, slide) -> performClick());
|
||||
}
|
||||
|
||||
stickerStub.get().setDownloadClickListener(downloadClickListener);
|
||||
|
|
|
@ -522,7 +522,19 @@ public class AttachmentManager {
|
|||
}
|
||||
|
||||
public enum MediaType {
|
||||
IMAGE, GIF, AUDIO, VIDEO, DOCUMENT, VCARD;
|
||||
IMAGE(MediaUtil.IMAGE_JPEG),
|
||||
GIF(MediaUtil.IMAGE_GIF),
|
||||
AUDIO(MediaUtil.AUDIO_AAC),
|
||||
VIDEO(MediaUtil.VIDEO_MP4),
|
||||
DOCUMENT(MediaUtil.UNKNOWN),
|
||||
VCARD(MediaUtil.VCARD);
|
||||
|
||||
private final String fallbackMimeType;
|
||||
|
||||
MediaType(String fallbackMimeType) {
|
||||
this.fallbackMimeType = fallbackMimeType;
|
||||
}
|
||||
|
||||
|
||||
public @NonNull Slide createSlide(@NonNull Context context,
|
||||
@NonNull Uri uri,
|
||||
|
@ -559,5 +571,8 @@ public class AttachmentManager {
|
|||
return DOCUMENT;
|
||||
}
|
||||
|
||||
public String toFallbackMimeType() {
|
||||
return fallbackMimeType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -135,8 +135,8 @@
|
|||
|
||||
<ViewStub
|
||||
android:id="@+id/sticker_view_stub"
|
||||
android:layout_width="@dimen/media_bubble_sticker_dimens"
|
||||
android:layout_height="@dimen/media_bubble_sticker_dimens"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout="@layout/conversation_item_received_sticker" />
|
||||
|
||||
<ViewStub
|
||||
|
|
|
@ -73,8 +73,8 @@
|
|||
|
||||
<ViewStub
|
||||
android:id="@+id/sticker_view_stub"
|
||||
android:layout_width="@dimen/media_bubble_sticker_dimens"
|
||||
android:layout_height="@dimen/media_bubble_sticker_dimens"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout="@layout/conversation_item_sent_sticker" />
|
||||
|
||||
<ViewStub
|
||||
|
@ -150,6 +150,7 @@
|
|||
android:layout_marginStart="@dimen/message_bubble_horizontal_padding"
|
||||
android:layout_marginTop="6dp"
|
||||
android:layout_marginEnd="@dimen/message_bubble_horizontal_padding"
|
||||
android:layout_gravity="end"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
android:gravity="end"
|
||||
|
|
|
@ -15,9 +15,13 @@
|
|||
|
||||
<org.thoughtcrime.securesms.components.ThumbnailView
|
||||
android:id="@+id/sticker_thumbnail"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="@dimen/media_bubble_sticker_dimens"
|
||||
android:layout_height="@dimen/media_bubble_sticker_dimens"
|
||||
app:thumbnail_radius="0dp"
|
||||
app:thumbnail_fit="fit_center"/>
|
||||
app:thumbnail_fit="fit_center"
|
||||
app:minWidth="@dimen/media_bubble_min_width"
|
||||
app:maxWidth="@dimen/media_bubble_max_width"
|
||||
app:minHeight="@dimen/media_bubble_min_height"
|
||||
app:maxHeight="@dimen/media_bubble_max_height" />
|
||||
|
||||
</merge>
|
||||
|
|
Loading…
Add table
Reference in a new issue