diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 75cd3b706f..527fc7b339 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -429,6 +429,10 @@
android:theme="@style/TextSecure.DarkTheme"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
+
+
diff --git a/res/drawable-hdpi/ic_sticker_32.webp b/res/drawable-hdpi/ic_sticker_32.webp
new file mode 100644
index 0000000000..09d338c31e
Binary files /dev/null and b/res/drawable-hdpi/ic_sticker_32.webp differ
diff --git a/res/drawable-mdpi/sticker_32.webp b/res/drawable-mdpi/sticker_32.webp
new file mode 100644
index 0000000000..020d81c54c
Binary files /dev/null and b/res/drawable-mdpi/sticker_32.webp differ
diff --git a/res/drawable-xhdpi/sticker_32.webp b/res/drawable-xhdpi/sticker_32.webp
new file mode 100644
index 0000000000..5924355998
Binary files /dev/null and b/res/drawable-xhdpi/sticker_32.webp differ
diff --git a/res/drawable-xxhdpi/sticker_32.webp b/res/drawable-xxhdpi/sticker_32.webp
new file mode 100644
index 0000000000..f97e9d2d66
Binary files /dev/null and b/res/drawable-xxhdpi/sticker_32.webp differ
diff --git a/res/drawable-xxxhdpi/sticker_32.webp b/res/drawable-xxxhdpi/sticker_32.webp
new file mode 100644
index 0000000000..7bda7018b7
Binary files /dev/null and b/res/drawable-xxxhdpi/sticker_32.webp differ
diff --git a/res/layout/image_editor_hud.xml b/res/layout/image_editor_hud.xml
index 4ebafacaca..8601d9919a 100644
--- a/res/layout/image_editor_hud.xml
+++ b/res/layout/image_editor_hud.xml
@@ -75,13 +75,21 @@
android:src="@drawable/ic_brush_highlight_32" />
+
+
+ app:layout_constraintTop_toBottomOf="@id/media_keyboard_tabs_top" />
+
+
+ app:layout_constraintStart_toStartOf="parent"
+ tools:layout_height="40dp"
+ tools:visibility="visible" />
-
+ android:background="?emoji_tab_indicator"
+ android:visibility="gone"
+ tools:visibility="visible" />
+ android:padding="6dp" />
+
+
diff --git a/res/layout/scribble_select_new_sticker_activity.xml b/res/layout/scribble_select_new_sticker_activity.xml
new file mode 100644
index 0000000000..a99e01fd22
--- /dev/null
+++ b/res/layout/scribble_select_new_sticker_activity.xml
@@ -0,0 +1,8 @@
+
+
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 92d2670708..c2ed44653b 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -355,4 +355,11 @@
+
+
+
+
+
+
+
diff --git a/src/org/thoughtcrime/securesms/components/emoji/MediaKeyboard.java b/src/org/thoughtcrime/securesms/components/emoji/MediaKeyboard.java
index 29e8b47671..3443725dd3 100644
--- a/src/org/thoughtcrime/securesms/components/emoji/MediaKeyboard.java
+++ b/src/org/thoughtcrime/securesms/components/emoji/MediaKeyboard.java
@@ -1,18 +1,20 @@
package org.thoughtcrime.securesms.components.emoji;
import android.content.Context;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.viewpager.widget.PagerAdapter;
-import androidx.viewpager.widget.ViewPager;
-import androidx.recyclerview.widget.LinearLayoutManager;
-import androidx.recyclerview.widget.RecyclerView;
+import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.viewpager.widget.PagerAdapter;
+import androidx.viewpager.widget.ViewPager;
+
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.InputAwareLayout.InputView;
import org.thoughtcrime.securesms.components.RepeatableImageKey;
@@ -29,16 +31,18 @@ public class MediaKeyboard extends FrameLayout implements InputView,
private static final String TAG = Log.tag(MediaKeyboard.class);
- private RecyclerView categoryTabs;
- private ViewPager categoryPager;
- private ViewGroup providerTabs;
- private RepeatableImageKey backspaceButton;
- private RepeatableImageKey backspaceButtonBackup;
- private View searchButton;
- private View addButton;
- private MediaKeyboardListener keyboardListener;
- private MediaKeyboardProvider[] providers;
- private int providerIndex;
+ private RecyclerView categoryTabs;
+ private ViewPager categoryPager;
+ private ViewGroup providerTabs;
+ private RepeatableImageKey backspaceButton;
+ private RepeatableImageKey backspaceButtonBackup;
+ private View searchButton;
+ private View addButton;
+ @Nullable private MediaKeyboardListener keyboardListener;
+ private MediaKeyboardProvider[] providers;
+ private int providerIndex;
+
+ private final boolean tabsAtBottom;
private MediaKeyboardBottomTabAdapter categoryTabAdapter;
@@ -48,6 +52,14 @@ public class MediaKeyboard extends FrameLayout implements InputView,
public MediaKeyboard(Context context, AttributeSet attrs) {
super(context, attrs);
+
+ TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.MediaKeyboard, 0, 0);
+
+ try {
+ tabsAtBottom = typedArray.getInt(R.styleable.MediaKeyboard_tabs_gravity, 0) == 0;
+ } finally {
+ typedArray.recycle();
+ }
}
public void setProviders(int startIndex, MediaKeyboardProvider... providers) {
@@ -59,7 +71,7 @@ public class MediaKeyboard extends FrameLayout implements InputView,
}
}
- public void setKeyboardListener(MediaKeyboardListener listener) {
+ public void setKeyboardListener(@Nullable MediaKeyboardListener listener) {
this.keyboardListener = listener;
}
@@ -76,8 +88,14 @@ public class MediaKeyboard extends FrameLayout implements InputView,
params.height = height;
Log.i(TAG, "showing emoji drawer with height " + params.height);
setLayoutParams(params);
- setVisibility(VISIBLE);
+ show();
+ }
+
+ public void show() {
+ if (this.categoryPager == null) initView();
+
+ setVisibility(VISIBLE);
if (keyboardListener != null) keyboardListener.onShown();
requestPresent(providers, providerIndex);
@@ -122,7 +140,7 @@ public class MediaKeyboard extends FrameLayout implements InputView,
public void requestDismissal() {
hide(true);
providerIndex = 0;
- keyboardListener.onKeyboardProviderChanged(providers[providerIndex]);
+ if (keyboardListener != null) keyboardListener.onKeyboardProviderChanged(providers[providerIndex]);
}
@Override
@@ -148,7 +166,10 @@ public class MediaKeyboard extends FrameLayout implements InputView,
private void initView() {
final View view = LayoutInflater.from(getContext()).inflate(R.layout.media_keyboard, this, true);
- this.categoryTabs = view.findViewById(R.id.media_keyboard_tabs);
+ RecyclerView categoryTabsTop = view.findViewById(R.id.media_keyboard_tabs_top);
+ RecyclerView categoryTabsBottom = view.findViewById(R.id.media_keyboard_tabs);
+
+ this.categoryTabs = tabsAtBottom ? categoryTabsBottom : categoryTabsTop;
this.categoryPager = view.findViewById(R.id.media_keyboard_pager);
this.providerTabs = view.findViewById(R.id.media_keyboard_provider_tabs);
this.backspaceButton = view.findViewById(R.id.media_keyboard_backspace);
@@ -156,10 +177,11 @@ public class MediaKeyboard extends FrameLayout implements InputView,
this.searchButton = view.findViewById(R.id.media_keyboard_search);
this.addButton = view.findViewById(R.id.media_keyboard_add);
- this.categoryTabAdapter = new MediaKeyboardBottomTabAdapter(GlideApp.with(this), this);
+ this.categoryTabAdapter = new MediaKeyboardBottomTabAdapter(GlideApp.with(this), this, tabsAtBottom);
categoryTabs.setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false));
categoryTabs.setAdapter(categoryTabAdapter);
+ categoryTabs.setVisibility(VISIBLE);
}
private void requestPresent(@NonNull MediaKeyboardProvider[] providers, int newIndex) {
diff --git a/src/org/thoughtcrime/securesms/components/emoji/MediaKeyboardBottomTabAdapter.java b/src/org/thoughtcrime/securesms/components/emoji/MediaKeyboardBottomTabAdapter.java
index 440ce88e1e..80fd336813 100644
--- a/src/org/thoughtcrime/securesms/components/emoji/MediaKeyboardBottomTabAdapter.java
+++ b/src/org/thoughtcrime/securesms/components/emoji/MediaKeyboardBottomTabAdapter.java
@@ -15,19 +15,22 @@ public class MediaKeyboardBottomTabAdapter extends RecyclerView.Adapter {
- DatabaseFactory.getStickerDatabase(this).updateStickerLastUsedTime(stickerRecord.getRowId(), System.currentTimeMillis());
- });
+ SignalExecutors.BOUNDED.execute(() ->
+ DatabaseFactory.getStickerDatabase(getApplicationContext())
+ .updateStickerLastUsedTime(stickerRecord.getRowId(), System.currentTimeMillis())
+ );
}
private void sendSticker(@NonNull StickerLocator stickerLocator, @NonNull Uri uri, long size, boolean clearCompose) {
diff --git a/src/org/thoughtcrime/securesms/database/model/StickerRecord.java b/src/org/thoughtcrime/securesms/database/model/StickerRecord.java
index 76ad74fbdf..8db2525d3d 100644
--- a/src/org/thoughtcrime/securesms/database/model/StickerRecord.java
+++ b/src/org/thoughtcrime/securesms/database/model/StickerRecord.java
@@ -1,6 +1,7 @@
package org.thoughtcrime.securesms.database.model;
import android.net.Uri;
+
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.mms.PartAuthority;
diff --git a/src/org/thoughtcrime/securesms/mediasend/MediaSendActivity.java b/src/org/thoughtcrime/securesms/mediasend/MediaSendActivity.java
index 1329e5330c..481c01ac80 100644
--- a/src/org/thoughtcrime/securesms/mediasend/MediaSendActivity.java
+++ b/src/org/thoughtcrime/securesms/mediasend/MediaSendActivity.java
@@ -1,10 +1,5 @@
package org.thoughtcrime.securesms.mediasend;
-import androidx.annotation.Nullable;
-import androidx.appcompat.app.AlertDialog;
-import androidx.appcompat.view.ContextThemeWrapper;
-import androidx.lifecycle.ViewModelProviders;
-
import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Context;
@@ -15,12 +10,6 @@ import android.graphics.Rect;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
-import androidx.annotation.NonNull;
-import androidx.fragment.app.Fragment;
-import androidx.fragment.app.FragmentManager;
-import androidx.recyclerview.widget.LinearLayoutManager;
-import androidx.recyclerview.widget.RecyclerView;
-
import android.text.Editable;
import android.text.TextWatcher;
import android.view.KeyEvent;
@@ -32,6 +21,16 @@ import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.view.ContextThemeWrapper;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+import androidx.lifecycle.ViewModelProviders;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.TransportOption;
@@ -438,10 +437,13 @@ public class MediaSendActivity extends PassphraseRequiredActionBarActivity imple
}
@Override
- public void onRequestFullScreen(boolean fullScreen) {
+ public void onRequestFullScreen(boolean fullScreen, boolean hideKeyboard) {
if (captionAndRail != null) {
captionAndRail.setVisibility(fullScreen ? View.GONE : View.VISIBLE);
}
+ if (hideKeyboard && hud.isKeyboardOpen()) {
+ hud.hideSoftkey(composeText, null);
+ }
}
@Override
diff --git a/src/org/thoughtcrime/securesms/scribbles/ImageEditorFragment.java b/src/org/thoughtcrime/securesms/scribbles/ImageEditorFragment.java
index 50ab3041ad..93703a83fe 100644
--- a/src/org/thoughtcrime/securesms/scribbles/ImageEditorFragment.java
+++ b/src/org/thoughtcrime/securesms/scribbles/ImageEditorFragment.java
@@ -6,14 +6,16 @@ import android.graphics.Bitmap;
import android.graphics.Paint;
import android.net.Uri;
import android.os.Bundle;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+import androidx.lifecycle.ViewModelProviders;
+
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.imageeditor.ColorableRenderer;
import org.thoughtcrime.securesms.imageeditor.ImageEditorView;
@@ -28,6 +30,7 @@ import org.thoughtcrime.securesms.mms.PushMediaConstraints;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.providers.BlobProvider;
import org.thoughtcrime.securesms.scribbles.widget.VerticalSlideColorPicker;
+import org.thoughtcrime.securesms.stickers.StickerSearchRepository;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.ParcelUtil;
import org.thoughtcrime.securesms.util.SaveAttachmentTask;
@@ -36,7 +39,6 @@ import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
import java.io.ByteArrayOutputStream;
-import java.io.IOException;
import static android.app.Activity.RESULT_OK;
@@ -48,14 +50,15 @@ public final class ImageEditorFragment extends Fragment implements ImageEditorHu
private static final String KEY_IMAGE_URI = "image_uri";
- public static final int SELECT_STICKER_REQUEST_CODE = 123;
+ private static final int SELECT_OLD_STICKER_REQUEST_CODE = 123;
+ private static final int SELECT_NEW_STICKER_REQUEST_CODE = 124;
private EditorModel restoredModel;
- @Nullable
- private EditorElement currentSelection;
- private int imageMaxHeight;
- private int imageMaxWidth;
+ @Nullable private EditorElement currentSelection;
+ private int imageMaxHeight;
+ private int imageMaxWidth;
+ private ImageEditorFragmentViewModel viewModel;
public static class Data {
private final Bundle bundle;
@@ -118,6 +121,13 @@ public final class ImageEditorFragment extends Fragment implements ImageEditorHu
imageMaxWidth = mediaConstraints.getImageMaxWidth(requireContext());
imageMaxHeight = mediaConstraints.getImageMaxHeight(requireContext());
+
+ StickerSearchRepository repository = new StickerSearchRepository(requireContext());
+
+ viewModel = ViewModelProviders.of(this, new ImageEditorFragmentViewModel.Factory(requireActivity().getApplication(), repository))
+ .get(ImageEditorFragmentViewModel.class);
+
+ viewModel.getStickersAvailability().observe(this, isAvailable -> imageEditorHud.setStickersAvailable(isAvailable));
}
@Nullable
@@ -233,15 +243,26 @@ public final class ImageEditorFragment extends Fragment implements ImageEditorHu
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
- if (resultCode == RESULT_OK && requestCode == SELECT_STICKER_REQUEST_CODE && data != null) {
- final String stickerFile = data.getStringExtra(StickerSelectActivity.EXTRA_STICKER_FILE);
-
- UriGlideRenderer renderer = new UriGlideRenderer(Uri.parse("file:///android_asset/" + stickerFile), false, imageMaxWidth, imageMaxHeight);
- EditorElement element = new EditorElement(renderer);
- imageEditorView.getModel().addElementCentered(element, 0.2f);
- currentSelection = element;
+ if (resultCode == RESULT_OK && requestCode == SELECT_NEW_STICKER_REQUEST_CODE && data != null) {
+ final Uri uri = data.getData();
+ if (uri != null) {
+ UriGlideRenderer renderer = new UriGlideRenderer(uri, true, imageMaxWidth, imageMaxHeight);
+ EditorElement element = new EditorElement(renderer);
+ imageEditorView.getModel().addElementCentered(element, 0.2f);
+ currentSelection = element;
+ imageEditorHud.setMode(ImageEditorHud.Mode.MOVE_DELETE);
+ }
+ } else if (resultCode == RESULT_OK && requestCode == SELECT_OLD_STICKER_REQUEST_CODE && data != null) {
+ final Uri uri = data.getData();
+ if (uri != null) {
+ UriGlideRenderer renderer = new UriGlideRenderer(uri, false, imageMaxWidth, imageMaxHeight);
+ EditorElement element = new EditorElement(renderer);
+ imageEditorView.getModel().addElementCentered(element, 0.2f);
+ currentSelection = element;
+ imageEditorHud.setMode(ImageEditorHud.Mode.MOVE_DELETE);
+ }
} else {
- imageEditorHud.enterMode(ImageEditorHud.Mode.NONE);
+ imageEditorHud.setMode(ImageEditorHud.Mode.NONE);
}
}
@@ -253,31 +274,46 @@ public final class ImageEditorFragment extends Fragment implements ImageEditorHu
controller.onTouchEventsNeeded(mode != ImageEditorHud.Mode.NONE);
switch (mode) {
- case CROP:
+ case CROP: {
imageEditorView.getModel().startCrop();
- break;
+ break;
+ }
- case DRAW:
+ case DRAW: {
imageEditorView.startDrawing(0.01f, Paint.Cap.ROUND);
break;
+ }
- case HIGHLIGHT:
+ case HIGHLIGHT: {
imageEditorView.startDrawing(0.03f, Paint.Cap.SQUARE);
break;
+ }
- case TEXT:
+ case TEXT: {
addText();
break;
+ }
+
+ case INSERT_ASSET_STICKER: {
+ Intent intent = new Intent(getContext(), StickerSelectActivity.class);
+ startActivityForResult(intent, SELECT_OLD_STICKER_REQUEST_CODE);
+ break;
+ }
+
+ case INSERT_STICKER: {
+ Intent intent = new Intent(getContext(), NewStickerSelectActivity.class);
+ startActivityForResult(intent, SELECT_NEW_STICKER_REQUEST_CODE);
+ break;
+ }
case MOVE_DELETE:
- Intent intent = new Intent(getContext(), StickerSelectActivity.class);
- startActivityForResult(intent, SELECT_STICKER_REQUEST_CODE);
break;
- case NONE:
+ case NONE: {
imageEditorView.getModel().doneCrop();
currentSelection = null;
break;
+ }
}
}
@@ -350,8 +386,8 @@ public final class ImageEditorFragment extends Fragment implements ImageEditorHu
}
@Override
- public void onRequestFullScreen(boolean fullScreen) {
- controller.onRequestFullScreen(fullScreen);
+ public void onRequestFullScreen(boolean fullScreen, boolean hideKeyboard) {
+ controller.onRequestFullScreen(fullScreen, hideKeyboard);
}
private void refreshUniqueColors() {
@@ -371,8 +407,7 @@ public final class ImageEditorFragment extends Fragment implements ImageEditorHu
} else {
currentSelection = null;
controller.onTouchEventsNeeded(false);
- imageEditorHud.enterMode(ImageEditorHud.Mode.NONE);
- imageEditorView.doneTextEditing();
+ imageEditorHud.setMode(ImageEditorHud.Mode.NONE);
}
}
@@ -383,7 +418,7 @@ public final class ImageEditorFragment extends Fragment implements ImageEditorHu
if (editorElement.getRenderer() instanceof MultiLineTextRenderer) {
setTextElement(editorElement, (ColorableRenderer) editorElement.getRenderer(), imageEditorView.isTextEditing());
} else {
- imageEditorHud.enterMode(ImageEditorHud.Mode.MOVE_DELETE);
+ imageEditorHud.setMode(ImageEditorHud.Mode.MOVE_DELETE);
}
}
}
@@ -412,6 +447,6 @@ public final class ImageEditorFragment extends Fragment implements ImageEditorHu
public interface Controller {
void onTouchEventsNeeded(boolean needed);
- void onRequestFullScreen(boolean fullScreen);
+ void onRequestFullScreen(boolean fullScreen, boolean hideKeyboard);
}
}
diff --git a/src/org/thoughtcrime/securesms/scribbles/ImageEditorFragmentViewModel.java b/src/org/thoughtcrime/securesms/scribbles/ImageEditorFragmentViewModel.java
new file mode 100644
index 0000000000..8f843d8d44
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/scribbles/ImageEditorFragmentViewModel.java
@@ -0,0 +1,65 @@
+package org.thoughtcrime.securesms.scribbles;
+
+import android.app.Application;
+import android.database.ContentObserver;
+import android.os.Handler;
+
+import androidx.annotation.NonNull;
+import androidx.lifecycle.LiveData;
+import androidx.lifecycle.MutableLiveData;
+import androidx.lifecycle.ViewModel;
+import androidx.lifecycle.ViewModelProvider;
+
+import org.thoughtcrime.securesms.database.DatabaseContentProviders;
+import org.thoughtcrime.securesms.stickers.StickerSearchRepository;
+import org.thoughtcrime.securesms.util.Throttler;
+
+public final class ImageEditorFragmentViewModel extends ViewModel {
+
+ private final Application application;
+ private final StickerSearchRepository repository;
+ private final MutableLiveData stickersAvailable;
+ private final Throttler availabilityThrottler;
+ private final ContentObserver packObserver;
+
+ private ImageEditorFragmentViewModel(@NonNull Application application, @NonNull StickerSearchRepository repository) {
+ this.application = application;
+ this.repository = repository;
+ 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 getStickersAvailability() {
+ repository.getStickerFeatureAvailability(stickersAvailable::postValue);
+ return stickersAvailable;
+ }
+
+ @Override
+ protected void onCleared() {
+ 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 create(@NonNull Class modelClass) {
+ //noinspection ConstantConditions
+ return modelClass.cast(new ImageEditorFragmentViewModel(application, repository));
+ }
+ }
+}
diff --git a/src/org/thoughtcrime/securesms/scribbles/ImageEditorHud.java b/src/org/thoughtcrime/securesms/scribbles/ImageEditorHud.java
index efc062b14d..81f37d5f9d 100644
--- a/src/org/thoughtcrime/securesms/scribbles/ImageEditorHud.java
+++ b/src/org/thoughtcrime/securesms/scribbles/ImageEditorHud.java
@@ -2,15 +2,17 @@ package org.thoughtcrime.securesms.scribbles;
import android.content.Context;
import android.graphics.Color;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.recyclerview.widget.LinearLayoutManager;
-import androidx.recyclerview.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
+import androidx.annotation.MainThread;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.scribbles.widget.ColorPaletteAdapter;
import org.thoughtcrime.securesms.scribbles.widget.VerticalSlideColorPicker;
@@ -34,7 +36,8 @@ public final class ImageEditorHud extends LinearLayout {
private View drawButton;
private View highlightButton;
private View textButton;
- private View stickerButton;
+ private View oldStickerButton;
+ private View newStickerButton;
private View undoButton;
private View saveButton;
private View deleteButton;
@@ -80,7 +83,8 @@ public final class ImageEditorHud extends LinearLayout {
drawButton = findViewById(R.id.scribble_draw_button);
highlightButton = findViewById(R.id.scribble_highlight_button);
textButton = findViewById(R.id.scribble_text_button);
- stickerButton = findViewById(R.id.scribble_sticker_button);
+ oldStickerButton = findViewById(R.id.old_scribble_sticker_button);
+ newStickerButton = findViewById(R.id.scribble_sticker_button);
undoButton = findViewById(R.id.scribble_undo_button);
saveButton = findViewById(R.id.scribble_save_button);
deleteButton = findViewById(R.id.scribble_delete_button);
@@ -102,7 +106,7 @@ public final class ImageEditorHud extends LinearLayout {
}
private void initializeVisibilityMap() {
- setVisibleViewsWhenInMode(Mode.NONE, drawButton, highlightButton, textButton, stickerButton, cropButton, undoButton, saveButton);
+ setStickersAvailable(false);
setVisibleViewsWhenInMode(Mode.DRAW, confirmButton, undoButton, colorPicker, colorPalette);
@@ -112,17 +116,34 @@ public final class ImageEditorHud extends LinearLayout {
setVisibleViewsWhenInMode(Mode.MOVE_DELETE, confirmButton, deleteButton);
+ setVisibleViewsWhenInMode(Mode.INSERT_STICKER, confirmButton);
+
+ setVisibleViewsWhenInMode(Mode.INSERT_ASSET_STICKER, confirmButton);
+
setVisibleViewsWhenInMode(Mode.CROP, confirmButton, cropFlipButton, cropRotateButton, cropAspectLock, undoButton);
for (Set views : visibilityModeMap.values()) {
allViews.addAll(views);
}
+
+ allViews.add(newStickerButton);
+ allViews.add(oldStickerButton);
}
private void setVisibleViewsWhenInMode(Mode mode, View... views) {
visibilityModeMap.put(mode, new HashSet<>(Arrays.asList(views)));
}
+ @MainThread
+ public void setStickersAvailable(boolean stickersAvailable) {
+ if (stickersAvailable) {
+ setVisibleViewsWhenInMode(Mode.NONE, drawButton, highlightButton, textButton, newStickerButton, cropButton, undoButton, saveButton);
+ } else {
+ setVisibleViewsWhenInMode(Mode.NONE, drawButton, highlightButton, textButton, oldStickerButton, cropButton, undoButton, saveButton);
+ }
+ updateButtonVisibility(currentMode);
+ }
+
private void initializeViews() {
undoButton.setOnClickListener(v -> eventListener.onUndo());
@@ -146,7 +167,8 @@ public final class ImageEditorHud extends LinearLayout {
drawButton.setOnClickListener(v -> setMode(Mode.DRAW));
highlightButton.setOnClickListener(v -> setMode(Mode.HIGHLIGHT));
textButton.setOnClickListener(v -> setMode(Mode.TEXT));
- stickerButton.setOnClickListener(v -> setMode(Mode.MOVE_DELETE));
+ oldStickerButton.setOnClickListener(v -> setMode(Mode.INSERT_ASSET_STICKER));
+ newStickerButton.setOnClickListener(v -> setMode(Mode.INSERT_STICKER));
saveButton.setOnClickListener(v -> eventListener.onSave());
}
@@ -172,16 +194,13 @@ public final class ImageEditorHud extends LinearLayout {
setMode(mode, false);
}
- private void setMode(@NonNull Mode mode) {
+ public void setMode(@NonNull Mode mode) {
setMode(mode, true);
}
private void setMode(@NonNull Mode mode, boolean notify) {
this.currentMode = mode;
- Set visibleButtons = visibilityModeMap.get(mode);
- for (View button : allViews) {
- button.setVisibility(buttonIsVisible(visibleButtons, button) ? VISIBLE : GONE);
- }
+ updateButtonVisibility(mode);
switch (mode) {
case CROP: presentModeCrop(); break;
@@ -193,7 +212,14 @@ public final class ImageEditorHud extends LinearLayout {
if (notify) {
eventListener.onModeStarted(mode);
}
- eventListener.onRequestFullScreen(mode != Mode.NONE);
+ eventListener.onRequestFullScreen(mode != Mode.NONE, mode != Mode.TEXT);
+ }
+
+ private void updateButtonVisibility(@NonNull Mode mode) {
+ Set visibleButtons = visibilityModeMap.get(mode);
+ for (View button : allViews) {
+ button.setVisibility(buttonIsVisible(visibleButtons, button) ? VISIBLE : GONE);
+ }
}
private boolean buttonIsVisible(@Nullable Set visibleButtons, @NonNull View button) {
@@ -236,7 +262,14 @@ public final class ImageEditorHud extends LinearLayout {
}
public enum Mode {
- NONE, DRAW, HIGHLIGHT, TEXT, MOVE_DELETE, CROP
+ NONE,
+ CROP,
+ TEXT,
+ DRAW,
+ HIGHLIGHT,
+ MOVE_DELETE,
+ INSERT_STICKER,
+ INSERT_ASSET_STICKER
}
public interface EventListener {
@@ -249,7 +282,7 @@ public final class ImageEditorHud extends LinearLayout {
void onRotate90AntiClockwise();
void onCropAspectLock(boolean locked);
boolean isCropAspectLocked();
- void onRequestFullScreen(boolean fullScreen);
+ void onRequestFullScreen(boolean fullScreen, boolean hideKeyboard);
}
private static final EventListener NULL_EVENT_LISTENER = new EventListener() {
@@ -292,7 +325,7 @@ public final class ImageEditorHud extends LinearLayout {
}
@Override
- public void onRequestFullScreen(boolean fullScreen) {
+ public void onRequestFullScreen(boolean fullScreen, boolean hideKeyboard) {
}
};
}
diff --git a/src/org/thoughtcrime/securesms/scribbles/NewStickerSelectActivity.java b/src/org/thoughtcrime/securesms/scribbles/NewStickerSelectActivity.java
new file mode 100644
index 0000000000..ae6c81d773
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/scribbles/NewStickerSelectActivity.java
@@ -0,0 +1,82 @@
+package org.thoughtcrime.securesms.scribbles;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.MenuItem;
+import android.view.WindowManager;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.FragmentActivity;
+
+import org.thoughtcrime.securesms.R;
+import org.thoughtcrime.securesms.components.emoji.MediaKeyboard;
+import org.thoughtcrime.securesms.components.emoji.MediaKeyboardProvider;
+import org.thoughtcrime.securesms.database.DatabaseFactory;
+import org.thoughtcrime.securesms.database.model.StickerRecord;
+import org.thoughtcrime.securesms.stickers.StickerKeyboardProvider;
+import org.thoughtcrime.securesms.stickers.StickerManagementActivity;
+import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
+
+public final class NewStickerSelectActivity extends FragmentActivity {
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
+ WindowManager.LayoutParams.FLAG_FULLSCREEN);
+
+ setContentView(R.layout.scribble_select_new_sticker_activity);
+
+ MediaKeyboard mediaKeyboard = findViewById(R.id.emoji_drawer);
+
+ mediaKeyboard.setProviders(0, new StickerKeyboardProvider(this, new StickerKeyboardProvider.StickerEventListener() {
+ @Override
+ public void onStickerSelected(@NonNull StickerRecord sticker) {
+ Intent intent = new Intent();
+ intent.setData(sticker.getUri());
+ setResult(RESULT_OK, intent);
+
+ SignalExecutors.BOUNDED.execute(() ->
+ DatabaseFactory.getStickerDatabase(getApplicationContext())
+ .updateStickerLastUsedTime(sticker.getRowId(), System.currentTimeMillis())
+ );
+
+ finish();
+ }
+
+ @Override
+ public void onStickerManagementClicked() {
+ startActivity(StickerManagementActivity.getIntent(NewStickerSelectActivity.this));
+ }
+ }
+ ));
+
+ mediaKeyboard.setKeyboardListener(new MediaKeyboard.MediaKeyboardListener() {
+ @Override
+ public void onShown() {
+ }
+
+ @Override
+ public void onHidden() {
+ finish();
+ }
+
+ @Override
+ public void onKeyboardProviderChanged(@NonNull MediaKeyboardProvider provider) {
+ }
+ });
+
+ mediaKeyboard.show();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ onBackPressed();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+}
diff --git a/src/org/thoughtcrime/securesms/scribbles/UriGlideRenderer.java b/src/org/thoughtcrime/securesms/scribbles/UriGlideRenderer.java
index 429136917f..37f1a206c7 100644
--- a/src/org/thoughtcrime/securesms/scribbles/UriGlideRenderer.java
+++ b/src/org/thoughtcrime/securesms/scribbles/UriGlideRenderer.java
@@ -63,9 +63,7 @@ final class UriGlideRenderer implements Renderer {
try {
Bitmap bitmap = getBitmapGlideRequest(rendererContext.context, false).submit().get();
setBitmap(rendererContext, bitmap);
- } catch (ExecutionException e) {
- throw new RuntimeException(e);
- } catch (InterruptedException e) {
+ } catch (ExecutionException | InterruptedException e) {
throw new RuntimeException(e);
}
} else {
@@ -73,6 +71,8 @@ final class UriGlideRenderer implements Renderer {
@Override
public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition super Bitmap> transition) {
setBitmap(rendererContext, resource);
+
+ rendererContext.invalidate.onInvalidate(UriGlideRenderer.this);
}
@Override
@@ -99,7 +99,7 @@ final class UriGlideRenderer implements Renderer {
paint.setAlpha(alpha);
rendererContext.restore();
- } else {
+ } else if (rendererContext.isBlockingLoad()) {
// If failed to load, we draw a black out, in case image was sticker positioned to cover private info.
rendererContext.canvas.drawRect(Bounds.FULL_BOUNDS, paint);
}
diff --git a/src/org/thoughtcrime/securesms/stickers/StickerKeyboardProvider.java b/src/org/thoughtcrime/securesms/stickers/StickerKeyboardProvider.java
index df9ab953bc..5b47fc8ade 100644
--- a/src/org/thoughtcrime/securesms/stickers/StickerKeyboardProvider.java
+++ b/src/org/thoughtcrime/securesms/stickers/StickerKeyboardProvider.java
@@ -1,16 +1,17 @@
package org.thoughtcrime.securesms.stickers;
-import androidx.lifecycle.ViewModelProviders;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.net.Uri;
+import android.widget.ImageView;
+
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentStatePagerAdapter;
-import androidx.appcompat.app.AppCompatActivity;
-import android.widget.ImageView;
+import androidx.lifecycle.ViewModelProviders;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.emoji.MediaKeyboardProvider;
@@ -48,7 +49,7 @@ public final class StickerKeyboardProvider implements MediaKeyboardProvider,
private boolean isSoloProvider;
private StickerKeyboardViewModel viewModel;
- public StickerKeyboardProvider(@NonNull AppCompatActivity activity,
+ public StickerKeyboardProvider(@NonNull FragmentActivity activity,
@NonNull StickerEventListener eventListener)
{
this.context = activity;
@@ -109,7 +110,7 @@ public final class StickerKeyboardProvider implements MediaKeyboardProvider,
}
}
- private void initViewModel(@NonNull AppCompatActivity activity) {
+ private void initViewModel(@NonNull FragmentActivity activity) {
StickerKeyboardRepository repository = new StickerKeyboardRepository(DatabaseFactory.getStickerDatabase(activity));
viewModel = ViewModelProviders.of(activity, new StickerKeyboardViewModel.Factory(activity.getApplication(), repository)).get(StickerKeyboardViewModel.class);