Use vanniktech emoji library.
@@ -1,6 +1,7 @@
|
||||
package org.briarproject.bramble.api.settings;
|
||||
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.db.Transaction;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
@NotNullByDefault
|
||||
@@ -11,6 +12,11 @@ public interface SettingsManager {
|
||||
*/
|
||||
Settings getSettings(String namespace) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns all settings in the given namespace.
|
||||
*/
|
||||
Settings getSettings(Transaction txn, String namespace) throws DbException;
|
||||
|
||||
/**
|
||||
* Merges the given settings with any existing settings in the given
|
||||
* namespace.
|
||||
|
||||
@@ -34,6 +34,12 @@ class SettingsManagerImpl implements SettingsManager {
|
||||
return s;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Settings getSettings(Transaction txn, String namespace)
|
||||
throws DbException {
|
||||
return db.getSettings(txn, namespace);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mergeSettings(Settings s, String namespace) throws DbException {
|
||||
Transaction txn = db.startTransaction(false);
|
||||
|
||||
@@ -31,9 +31,8 @@ dependencies {
|
||||
implementation 'info.guardianproject.trustedintents:trustedintents:0.2'
|
||||
implementation 'de.hdodenhof:circleimageview:2.2.0'
|
||||
implementation 'com.google.zxing:core:3.3.0'
|
||||
implementation 'com.jpardogo.materialtabstrip:library:1.1.0'
|
||||
implementation 'com.github.bumptech.glide:glide:3.8.0'
|
||||
implementation 'uk.co.samuelwall:material-tap-target-prompt:2.8.0'
|
||||
implementation 'com.vanniktech:emoji-google:0.5.1'
|
||||
|
||||
annotationProcessor 'com.google.dagger:dagger-compiler:2.0.2'
|
||||
|
||||
@@ -80,7 +79,7 @@ android {
|
||||
buildToolsVersion '27.0.3'
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 14
|
||||
minSdkVersion 15
|
||||
targetSdkVersion 26
|
||||
versionCode 10013
|
||||
versionName "1.0.13"
|
||||
|
||||
@@ -27,9 +27,4 @@
|
||||
-dontnote org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout
|
||||
|
||||
# Emoji
|
||||
-keep class org.thoughtcrime.securesms.**
|
||||
-keep class com.astuetz.PagerSlidingTabStrip$OnTabReselectedListener
|
||||
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
|
||||
**[] $VALUES;
|
||||
public *;
|
||||
}
|
||||
-keep class com.vanniktech.emoji.**
|
||||
|
||||
|
Before Width: | Height: | Size: 244 KiB |
|
Before Width: | Height: | Size: 348 KiB |
|
Before Width: | Height: | Size: 901 KiB |
|
Before Width: | Height: | Size: 258 KiB |
|
Before Width: | Height: | Size: 478 KiB |
|
Before Width: | Height: | Size: 650 KiB |
|
Before Width: | Height: | Size: 412 KiB |
|
Before Width: | Height: | Size: 547 KiB |
@@ -28,6 +28,7 @@ import org.briarproject.briar.BriarCoreEagerSingletons;
|
||||
import org.briarproject.briar.BriarCoreModule;
|
||||
import org.briarproject.briar.android.login.SignInReminderReceiver;
|
||||
import org.briarproject.briar.android.reporting.BriarReportSender;
|
||||
import org.briarproject.briar.android.view.TextInputView;
|
||||
import org.briarproject.briar.api.android.AndroidNotificationManager;
|
||||
import org.briarproject.briar.api.android.DozeWatchdog;
|
||||
import org.briarproject.briar.api.android.LockManager;
|
||||
@@ -49,8 +50,6 @@ import org.briarproject.briar.api.privategroup.PrivateGroupManager;
|
||||
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationFactory;
|
||||
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager;
|
||||
import org.briarproject.briar.api.test.TestDataCreator;
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiProvider;
|
||||
import org.thoughtcrime.securesms.components.emoji.RecentEmojiPageModel;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
@@ -161,12 +160,10 @@ public interface AndroidComponent
|
||||
|
||||
void inject(BriarReportSender briarReportSender);
|
||||
|
||||
void inject(EmojiProvider emojiProvider);
|
||||
|
||||
void inject(RecentEmojiPageModel recentEmojiPageModel);
|
||||
|
||||
void inject(NotificationCleanupService notificationCleanupService);
|
||||
|
||||
void inject(TextInputView textInputView);
|
||||
|
||||
// Eager singleton load
|
||||
void inject(AppModule.EagerSingletons init);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.StrictMode;
|
||||
|
||||
import com.vanniktech.emoji.RecentEmoji;
|
||||
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||
@@ -65,6 +67,8 @@ public class AppModule {
|
||||
NetworkUsageLogger networkUsageLogger;
|
||||
@Inject
|
||||
DozeWatchdog dozeWatchdog;
|
||||
@Inject
|
||||
RecentEmoji recentEmoji;
|
||||
}
|
||||
|
||||
private final Application application;
|
||||
@@ -211,4 +215,11 @@ public class AppModule {
|
||||
return lockManager;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
RecentEmoji provideRecentEmoji(LifecycleManager lifecycleManager,
|
||||
RecentEmojiImpl recentEmoji) {
|
||||
lifecycleManager.registerClient(recentEmoji);
|
||||
return recentEmoji;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,9 @@ import android.os.StrictMode.ThreadPolicy;
|
||||
import android.os.StrictMode.VmPolicy;
|
||||
import android.preference.PreferenceManager;
|
||||
|
||||
import com.vanniktech.emoji.EmojiManager;
|
||||
import com.vanniktech.emoji.google.GoogleEmojiProvider;
|
||||
|
||||
import org.acra.ACRA;
|
||||
import org.acra.ReportingInteractionMode;
|
||||
import org.acra.annotation.ReportsCrashes;
|
||||
@@ -89,6 +92,7 @@ public class BriarApplicationImpl extends Application
|
||||
Localizer.getInstance().setLocale(base));
|
||||
setTheme(base, prefs);
|
||||
ACRA.init(this);
|
||||
EmojiManager.install(new GoogleEmojiProvider());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
package org.briarproject.briar.android;
|
||||
|
||||
import com.vanniktech.emoji.EmojiRange;
|
||||
import com.vanniktech.emoji.EmojiUtils;
|
||||
import com.vanniktech.emoji.RecentEmoji;
|
||||
import com.vanniktech.emoji.emoji.Emoji;
|
||||
|
||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.db.Transaction;
|
||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||
import org.briarproject.bramble.api.settings.Settings;
|
||||
import org.briarproject.bramble.api.settings.SettingsManager;
|
||||
import org.briarproject.bramble.api.sync.Client;
|
||||
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||
import org.briarproject.bramble.util.StringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
import static org.briarproject.briar.android.settings.SettingsFragment.SETTINGS_NAMESPACE;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
class RecentEmojiImpl implements RecentEmoji, Client {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(RecentEmojiImpl.class.getName());
|
||||
|
||||
private static final String EMOJI_LRU_PREFERENCE = "pref_emoji_recent2";
|
||||
private static final int EMOJI_LRU_SIZE = 50;
|
||||
|
||||
// UI thread
|
||||
private final LinkedHashSet<Emoji> recentlyUsed = new LinkedHashSet<>();
|
||||
|
||||
private final Executor dbExecutor;
|
||||
private final AndroidExecutor androidExecutor;
|
||||
private final SettingsManager settingsManager;
|
||||
|
||||
@Inject
|
||||
RecentEmojiImpl(@DatabaseExecutor Executor dbExecutor,
|
||||
AndroidExecutor androidExecutor, SettingsManager settingsManager) {
|
||||
this.dbExecutor = dbExecutor;
|
||||
this.androidExecutor = androidExecutor;
|
||||
this.settingsManager = settingsManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Emoji> getRecentEmojis() {
|
||||
return new ArrayList<>(recentlyUsed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addEmoji(Emoji emoji) {
|
||||
recentlyUsed.remove(emoji);
|
||||
recentlyUsed.add(emoji);
|
||||
|
||||
if (recentlyUsed.size() > EMOJI_LRU_SIZE) {
|
||||
Iterator<Emoji> iterator = recentlyUsed.iterator();
|
||||
iterator.next();
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void persist() {
|
||||
if (!recentlyUsed.isEmpty()) save(serialize(recentlyUsed));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createLocalState(Transaction txn) throws DbException {
|
||||
Settings settings =
|
||||
settingsManager.getSettings(txn, SETTINGS_NAMESPACE);
|
||||
String serialized = settings.get(EMOJI_LRU_PREFERENCE);
|
||||
if (serialized != null) {
|
||||
androidExecutor.runOnUiThread(() ->
|
||||
recentlyUsed.addAll(deserialize(serialized)));
|
||||
}
|
||||
}
|
||||
|
||||
private String serialize(Collection<Emoji> emojis) {
|
||||
Collection<String> strings = new ArrayList<>(emojis.size());
|
||||
for (Emoji emoji : emojis) strings.add(emoji.getUnicode());
|
||||
return StringUtils.join(strings, "\t");
|
||||
}
|
||||
|
||||
private Collection<Emoji> deserialize(String serialized) {
|
||||
Collection<EmojiRange> ranges = EmojiUtils.emojis(serialized);
|
||||
Collection<Emoji> result = new ArrayList<>(ranges.size());
|
||||
for (EmojiRange range : ranges) result.add(range.emoji);
|
||||
return result;
|
||||
}
|
||||
|
||||
private void save(String serialized) {
|
||||
dbExecutor.execute(() -> {
|
||||
Settings settings = new Settings();
|
||||
settings.put(EMOJI_LRU_PREFERENCE, serialized);
|
||||
try {
|
||||
settingsManager.mergeSettings(settings, SETTINGS_NAMESPACE);
|
||||
} catch (DbException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -202,7 +202,6 @@ public class GroupActivity extends
|
||||
if (!enabled) {
|
||||
textInput.setVisibility(GONE);
|
||||
if (textInput.isKeyboardOpen()) textInput.hideSoftKeyboard();
|
||||
if (textInput.isEmojiDrawerOpen()) textInput.hideEmojiDrawer();
|
||||
} else {
|
||||
textInput.setVisibility(VISIBLE);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ import android.view.LayoutInflater;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||
import org.briarproject.briar.R;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
@@ -15,6 +17,8 @@ import javax.annotation.Nullable;
|
||||
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
|
||||
@UiThread
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
public class LargeTextInputView extends TextInputView {
|
||||
|
||||
public LargeTextInputView(Context context) {
|
||||
@@ -46,30 +50,28 @@ public class LargeTextInputView extends TextInputView {
|
||||
R.styleable.LargeTextInputView);
|
||||
String buttonText =
|
||||
attributes.getString(R.styleable.LargeTextInputView_buttonText);
|
||||
int maxLines =
|
||||
attributes
|
||||
.getInteger(R.styleable.LargeTextInputView_maxLines, 0);
|
||||
int maxLines = attributes
|
||||
.getInteger(R.styleable.LargeTextInputView_maxLines, 0);
|
||||
boolean fillHeight = attributes
|
||||
.getBoolean(R.styleable.LargeTextInputView_fillHeight,
|
||||
false);
|
||||
.getBoolean(R.styleable.LargeTextInputView_fillHeight, false);
|
||||
attributes.recycle();
|
||||
|
||||
if (buttonText != null) setButtonText(buttonText);
|
||||
if (maxLines > 0) ui.editText.setMaxLines(maxLines);
|
||||
if (maxLines > 0) editText.setMaxLines(maxLines);
|
||||
if (fillHeight) {
|
||||
ViewGroup layout = findViewById(R.id.input_layout);
|
||||
LayoutParams params = (LayoutParams) layout.getLayoutParams();
|
||||
params.height = 0;
|
||||
params.weight = 1;
|
||||
layout.setLayoutParams(params);
|
||||
ViewGroup.LayoutParams editParams = ui.editText.getLayoutParams();
|
||||
ViewGroup.LayoutParams editParams = editText.getLayoutParams();
|
||||
editParams.height = MATCH_PARENT;
|
||||
ui.editText.setLayoutParams(editParams);
|
||||
editText.setLayoutParams(editParams);
|
||||
}
|
||||
}
|
||||
|
||||
public void setButtonText(String text) {
|
||||
((Button) ui.sendButton).setText(text);
|
||||
((Button) sendButton).setText(text);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,35 +8,47 @@ import android.os.IBinder;
|
||||
import android.support.annotation.CallSuper;
|
||||
import android.support.annotation.StringRes;
|
||||
import android.support.annotation.UiThread;
|
||||
import android.support.v7.widget.AppCompatImageButton;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
|
||||
import com.vanniktech.emoji.EmojiEditText;
|
||||
import com.vanniktech.emoji.EmojiPopup;
|
||||
import com.vanniktech.emoji.RecentEmoji;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.BriarApplication;
|
||||
import org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout;
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiDrawer;
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiDrawer.EmojiEventListener;
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiEditText;
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiToggle;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static android.content.Context.INPUT_METHOD_SERVICE;
|
||||
import static android.content.Context.LAYOUT_INFLATER_SERVICE;
|
||||
import static android.view.KeyEvent.KEYCODE_BACK;
|
||||
import static android.view.KeyEvent.KEYCODE_ENTER;
|
||||
import static android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT;
|
||||
|
||||
@UiThread
|
||||
public class TextInputView extends KeyboardAwareLinearLayout
|
||||
implements EmojiEventListener {
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
public class TextInputView extends KeyboardAwareLinearLayout {
|
||||
|
||||
protected final ViewHolder ui;
|
||||
protected TextInputListener listener;
|
||||
@Inject
|
||||
RecentEmoji recentEmoji;
|
||||
|
||||
@Nullable
|
||||
TextInputListener listener;
|
||||
|
||||
AppCompatImageButton emojiToggle;
|
||||
EmojiEditText editText;
|
||||
EmojiPopup emojiPopup;
|
||||
View sendButton;
|
||||
|
||||
public TextInputView(Context context) {
|
||||
this(context, null);
|
||||
@@ -49,11 +61,12 @@ public class TextInputView extends KeyboardAwareLinearLayout
|
||||
public TextInputView(Context context, @Nullable AttributeSet attrs,
|
||||
int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
BriarApplication app =
|
||||
(BriarApplication) context.getApplicationContext();
|
||||
app.getApplicationComponent().inject(this);
|
||||
setOrientation(VERTICAL);
|
||||
setLayoutTransition(new LayoutTransition());
|
||||
|
||||
inflateLayout(context);
|
||||
ui = new ViewHolder();
|
||||
setUpViews(context, attrs);
|
||||
}
|
||||
|
||||
@@ -65,89 +78,79 @@ public class TextInputView extends KeyboardAwareLinearLayout
|
||||
|
||||
@CallSuper
|
||||
protected void setUpViews(Context context, @Nullable AttributeSet attrs) {
|
||||
emojiToggle = findViewById(R.id.emoji_toggle);
|
||||
editText = findViewById(R.id.input_text);
|
||||
emojiPopup = EmojiPopup.Builder
|
||||
.fromRootView(this)
|
||||
.setRecentEmoji(recentEmoji)
|
||||
.setOnEmojiPopupShownListener(this::showKeyboardIcon)
|
||||
.setOnEmojiPopupDismissListener(this::showEmojiIcon)
|
||||
.build(editText);
|
||||
sendButton = findViewById(R.id.btn_send);
|
||||
|
||||
// get attributes
|
||||
TypedArray attributes = context.obtainStyledAttributes(attrs,
|
||||
R.styleable.TextInputView);
|
||||
String hint = attributes.getString(R.styleable.TextInputView_hint);
|
||||
attributes.recycle();
|
||||
|
||||
if (hint != null) {
|
||||
ui.editText.setHint(hint);
|
||||
}
|
||||
if (hint != null) editText.setHint(hint);
|
||||
|
||||
ui.emojiToggle.attach(ui.emojiDrawer);
|
||||
ui.emojiToggle.setOnClickListener(v -> onEmojiToggleClicked());
|
||||
ui.editText.setOnClickListener(v -> showSoftKeyboard());
|
||||
ui.editText.setOnKeyListener((v, keyCode, event) -> {
|
||||
if (keyCode == KEYCODE_BACK && isEmojiDrawerOpen()) {
|
||||
hideEmojiDrawer();
|
||||
return true;
|
||||
}
|
||||
emojiToggle.setOnClickListener(v -> emojiPopup.toggle());
|
||||
editText.setOnClickListener(v -> showSoftKeyboard());
|
||||
editText.setOnKeyListener((v, keyCode, event) -> {
|
||||
if (keyCode == KEYCODE_ENTER && event.isCtrlPressed()) {
|
||||
trySendMessage();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
ui.sendButton.setOnClickListener(v -> trySendMessage());
|
||||
ui.emojiDrawer.setEmojiEventListener(this);
|
||||
sendButton.setOnClickListener(v -> trySendMessage());
|
||||
}
|
||||
|
||||
private void showEmojiIcon() {
|
||||
emojiToggle.setImageResource(R.drawable.ic_emoji_toggle);
|
||||
}
|
||||
|
||||
private void showKeyboardIcon() {
|
||||
emojiToggle.setImageResource(R.drawable.ic_keyboard);
|
||||
}
|
||||
|
||||
private void trySendMessage() {
|
||||
if (listener != null) {
|
||||
listener.onSendClick(ui.editText.getText().toString());
|
||||
listener.onSendClick(editText.getText().toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVisibility(int visibility) {
|
||||
if (visibility == GONE && isKeyboardOpen()) {
|
||||
onKeyboardClose();
|
||||
}
|
||||
super.setVisibility(visibility);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onKeyEvent(KeyEvent keyEvent) {
|
||||
ui.editText.dispatchKeyEvent(keyEvent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEmojiSelected(String emoji) {
|
||||
ui.editText.insertEmoji(emoji);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
|
||||
return ui.editText.requestFocus(direction, previouslyFocusedRect);
|
||||
return editText.requestFocus(direction, previouslyFocusedRect);
|
||||
}
|
||||
|
||||
private void onEmojiToggleClicked() {
|
||||
if (isEmojiDrawerOpen()) {
|
||||
showSoftKeyboard();
|
||||
} else {
|
||||
showEmojiDrawer();
|
||||
}
|
||||
@Override
|
||||
public void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
if (emojiPopup.isShowing()) emojiPopup.dismiss();
|
||||
}
|
||||
|
||||
public void setText(String text) {
|
||||
ui.editText.setText(text);
|
||||
editText.setText(text);
|
||||
}
|
||||
|
||||
public Editable getText() {
|
||||
return ui.editText.getText();
|
||||
return editText.getText();
|
||||
}
|
||||
|
||||
public void setHint(@StringRes int res) {
|
||||
ui.editText.setHint(res);
|
||||
editText.setHint(res);
|
||||
}
|
||||
|
||||
public void setSendButtonEnabled(boolean enabled) {
|
||||
ui.sendButton.setEnabled(enabled);
|
||||
sendButton.setEnabled(enabled);
|
||||
}
|
||||
|
||||
public void addTextChangedListener(TextWatcher watcher) {
|
||||
ui.editText.addTextChangedListener(watcher);
|
||||
editText.addTextChangedListener(watcher);
|
||||
}
|
||||
|
||||
public void setListener(TextInputListener listener) {
|
||||
@@ -155,58 +158,16 @@ public class TextInputView extends KeyboardAwareLinearLayout
|
||||
}
|
||||
|
||||
public void showSoftKeyboard() {
|
||||
if (isKeyboardOpen()) return;
|
||||
|
||||
if (ui.emojiDrawer.isShowing()) {
|
||||
postOnKeyboardOpen(this::hideEmojiDrawer);
|
||||
}
|
||||
ui.editText.post(() -> {
|
||||
ui.editText.requestFocus();
|
||||
InputMethodManager imm = (InputMethodManager)
|
||||
getContext().getSystemService(INPUT_METHOD_SERVICE);
|
||||
imm.showSoftInput(ui.editText, SHOW_IMPLICIT);
|
||||
});
|
||||
Object o = getContext().getSystemService(INPUT_METHOD_SERVICE);
|
||||
((InputMethodManager) o).showSoftInput(editText, SHOW_IMPLICIT);
|
||||
}
|
||||
|
||||
public void hideSoftKeyboard() {
|
||||
IBinder token = ui.editText.getWindowToken();
|
||||
IBinder token = editText.getWindowToken();
|
||||
Object o = getContext().getSystemService(INPUT_METHOD_SERVICE);
|
||||
((InputMethodManager) o).hideSoftInputFromWindow(token, 0);
|
||||
}
|
||||
|
||||
public void showEmojiDrawer() {
|
||||
if (isKeyboardOpen()) {
|
||||
postOnKeyboardClose(() -> ui.emojiDrawer.show(getKeyboardHeight()));
|
||||
hideSoftKeyboard();
|
||||
} else {
|
||||
ui.emojiDrawer.show(getKeyboardHeight());
|
||||
ui.editText.requestFocus();
|
||||
}
|
||||
}
|
||||
|
||||
public void hideEmojiDrawer() {
|
||||
ui.emojiDrawer.hide();
|
||||
}
|
||||
|
||||
public boolean isEmojiDrawerOpen() {
|
||||
return ui.emojiDrawer.isShowing();
|
||||
}
|
||||
|
||||
protected class ViewHolder {
|
||||
|
||||
private final EmojiToggle emojiToggle;
|
||||
final EmojiEditText editText;
|
||||
final View sendButton;
|
||||
final EmojiDrawer emojiDrawer;
|
||||
|
||||
private ViewHolder() {
|
||||
emojiToggle = findViewById(R.id.emoji_toggle);
|
||||
editText = findViewById(R.id.input_text);
|
||||
emojiDrawer = findViewById(R.id.emoji_drawer);
|
||||
sendButton = findViewById(R.id.btn_send);
|
||||
}
|
||||
}
|
||||
|
||||
public interface TextInputListener {
|
||||
void onSendClick(String text);
|
||||
}
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
package org.thoughtcrime.securesms.components;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.UiThread;
|
||||
import android.support.v7.widget.AppCompatImageButton;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewConfiguration;
|
||||
|
||||
import static android.view.HapticFeedbackConstants.KEYBOARD_TAP;
|
||||
import static android.view.MotionEvent.ACTION_CANCEL;
|
||||
import static android.view.MotionEvent.ACTION_DOWN;
|
||||
import static android.view.MotionEvent.ACTION_UP;
|
||||
|
||||
@UiThread
|
||||
public class RepeatableImageKey extends AppCompatImageButton {
|
||||
|
||||
private KeyEventListener listener;
|
||||
|
||||
public RepeatableImageKey(Context context) {
|
||||
super(context);
|
||||
init();
|
||||
}
|
||||
|
||||
public RepeatableImageKey(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init();
|
||||
}
|
||||
|
||||
public RepeatableImageKey(Context context, AttributeSet attrs,
|
||||
int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
setOnClickListener(new RepeaterClickListener());
|
||||
setOnTouchListener(new RepeaterTouchListener());
|
||||
}
|
||||
|
||||
public void setOnKeyEventListener(KeyEventListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
private void notifyListener() {
|
||||
if (listener != null) listener.onKeyEvent();
|
||||
}
|
||||
|
||||
private class RepeaterClickListener implements OnClickListener {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
notifyListener();
|
||||
}
|
||||
}
|
||||
|
||||
private class Repeater implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
notifyListener();
|
||||
postDelayed(this, ViewConfiguration.getKeyRepeatDelay());
|
||||
}
|
||||
}
|
||||
|
||||
private class RepeaterTouchListener implements OnTouchListener {
|
||||
|
||||
private final Repeater repeater;
|
||||
|
||||
private RepeaterTouchListener() {
|
||||
repeater = new Repeater();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouch(View view, MotionEvent motionEvent) {
|
||||
switch (motionEvent.getAction()) {
|
||||
case ACTION_DOWN:
|
||||
view.postDelayed(repeater,
|
||||
ViewConfiguration.getKeyRepeatTimeout());
|
||||
performHapticFeedback(KEYBOARD_TAP);
|
||||
return false;
|
||||
case ACTION_CANCEL:
|
||||
case ACTION_UP:
|
||||
view.removeCallbacks(repeater);
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface KeyEventListener {
|
||||
void onKeyEvent();
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package org.thoughtcrime.securesms.components.emoji;
|
||||
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.Drawable.Callback;
|
||||
import android.support.annotation.UiThread;
|
||||
import android.text.style.ImageSpan;
|
||||
|
||||
@UiThread
|
||||
class AnimatingImageSpan extends ImageSpan {
|
||||
|
||||
AnimatingImageSpan(Drawable drawable, Callback callback) {
|
||||
super(drawable, ALIGN_BOTTOM);
|
||||
drawable.setCallback(callback);
|
||||
}
|
||||
}
|
||||
@@ -1,202 +0,0 @@
|
||||
package org.thoughtcrime.securesms.components.emoji;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.UiThread;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v4.view.PagerAdapter;
|
||||
import android.support.v4.view.ViewPager;
|
||||
import android.support.v7.widget.AppCompatImageView;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import com.astuetz.PagerSlidingTabStrip;
|
||||
import com.astuetz.PagerSlidingTabStrip.CustomTabProvider;
|
||||
|
||||
import org.briarproject.briar.R;
|
||||
import org.thoughtcrime.securesms.components.RepeatableImageKey;
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiPageView.EmojiSelectionListener;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static android.support.v4.widget.ImageViewCompat.setImageTintList;
|
||||
import static android.view.KeyEvent.ACTION_DOWN;
|
||||
import static android.view.KeyEvent.KEYCODE_DEL;
|
||||
import static android.widget.ImageView.ScaleType.CENTER_INSIDE;
|
||||
import static java.util.logging.Level.INFO;
|
||||
|
||||
@UiThread
|
||||
public class EmojiDrawer extends LinearLayout {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(EmojiDrawer.class.getName());
|
||||
private static final KeyEvent DELETE_KEY_EVENT =
|
||||
new KeyEvent(ACTION_DOWN, KEYCODE_DEL);
|
||||
|
||||
private ViewPager pager;
|
||||
private List<EmojiPageModel> models;
|
||||
private PagerSlidingTabStrip strip;
|
||||
private RecentEmojiPageModel recentModel;
|
||||
private EmojiEventListener listener;
|
||||
private EmojiDrawerListener drawerListener;
|
||||
|
||||
public EmojiDrawer(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public EmojiDrawer(Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
setOrientation(VERTICAL);
|
||||
}
|
||||
|
||||
private void initView() {
|
||||
View v = LayoutInflater.from(getContext())
|
||||
.inflate(R.layout.emoji_drawer, this, true);
|
||||
initializeResources(v);
|
||||
initializePageModels();
|
||||
initializeEmojiGrid();
|
||||
}
|
||||
|
||||
public void setEmojiEventListener(EmojiEventListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public void setDrawerListener(EmojiDrawerListener listener) {
|
||||
this.drawerListener = listener;
|
||||
}
|
||||
|
||||
private void initializeResources(View v) {
|
||||
this.pager = v.findViewById(R.id.emoji_pager);
|
||||
this.strip = v.findViewById(R.id.tabs);
|
||||
|
||||
RepeatableImageKey backspace = v.findViewById(R.id.backspace);
|
||||
backspace.setOnKeyEventListener(() -> {
|
||||
if (listener != null) listener.onKeyEvent(DELETE_KEY_EVENT);
|
||||
});
|
||||
}
|
||||
|
||||
public boolean isShowing() {
|
||||
return getVisibility() == VISIBLE;
|
||||
}
|
||||
|
||||
public void show(int height) {
|
||||
if (this.pager == null) initView();
|
||||
ViewGroup.LayoutParams params = getLayoutParams();
|
||||
params.height = height;
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Showing emoji drawer with height " + params.height);
|
||||
setLayoutParams(params);
|
||||
setVisibility(VISIBLE);
|
||||
if (drawerListener != null) drawerListener.onShown();
|
||||
}
|
||||
|
||||
public void hide() {
|
||||
setVisibility(GONE);
|
||||
if (drawerListener != null) drawerListener.onHidden();
|
||||
}
|
||||
|
||||
private void initializeEmojiGrid() {
|
||||
pager.setAdapter(new EmojiPagerAdapter(getContext(), models,
|
||||
emoji -> {
|
||||
recentModel.onCodePointSelected(emoji);
|
||||
if (listener != null) listener.onEmojiSelected(emoji);
|
||||
}));
|
||||
|
||||
if (recentModel.getEmoji().length == 0) {
|
||||
pager.setCurrentItem(1);
|
||||
}
|
||||
strip.setViewPager(pager);
|
||||
}
|
||||
|
||||
private void initializePageModels() {
|
||||
this.models = new LinkedList<>();
|
||||
this.recentModel = new RecentEmojiPageModel(getContext());
|
||||
this.models.add(recentModel);
|
||||
this.models.addAll(EmojiProvider.getInstance(getContext())
|
||||
.getStaticPages());
|
||||
}
|
||||
|
||||
public static class EmojiPagerAdapter extends PagerAdapter
|
||||
implements CustomTabProvider {
|
||||
private Context context;
|
||||
private List<EmojiPageModel> pages;
|
||||
private EmojiSelectionListener listener;
|
||||
|
||||
private EmojiPagerAdapter(@NonNull Context context,
|
||||
@NonNull List<EmojiPageModel> pages,
|
||||
@Nullable EmojiSelectionListener listener) {
|
||||
super();
|
||||
this.context = context;
|
||||
this.pages = pages;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return pages.size();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Object instantiateItem(@NonNull ViewGroup container,
|
||||
int position) {
|
||||
EmojiPageView page = new EmojiPageView(context);
|
||||
page.setModel(pages.get(position));
|
||||
page.setEmojiSelectedListener(listener);
|
||||
container.addView(page);
|
||||
return page;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroyItem(@NonNull ViewGroup container, int position,
|
||||
@NonNull Object object) {
|
||||
container.removeView((View) object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isViewFromObject(@NonNull View view,
|
||||
@NonNull Object object) {
|
||||
return view == object;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getCustomTabView(ViewGroup viewGroup, int i) {
|
||||
ImageView image = new AppCompatImageView(context);
|
||||
image.setScaleType(CENTER_INSIDE);
|
||||
image.setImageResource(pages.get(i).getIcon());
|
||||
setImageTintList(image, ColorStateList.valueOf(
|
||||
ContextCompat.getColor(context, R.color.color_primary)));
|
||||
return image;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tabSelected(View view) {
|
||||
view.animate().setDuration(300).alpha(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tabUnselected(View view) {
|
||||
view.animate().setDuration(400).alpha(0.4f);
|
||||
}
|
||||
}
|
||||
|
||||
public interface EmojiEventListener extends EmojiSelectionListener {
|
||||
void onKeyEvent(KeyEvent keyEvent);
|
||||
}
|
||||
|
||||
public interface EmojiDrawerListener {
|
||||
void onShown();
|
||||
|
||||
void onHidden();
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package org.thoughtcrime.securesms.components.emoji;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.UiThread;
|
||||
import android.support.v7.widget.AppCompatEditText;
|
||||
import android.text.InputFilter;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import org.briarproject.briar.R;
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiProvider.EmojiDrawable;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@UiThread
|
||||
public class EmojiEditText extends AppCompatEditText {
|
||||
|
||||
public EmojiEditText(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public EmojiEditText(Context context, @Nullable AttributeSet attrs) {
|
||||
this(context, attrs, R.attr.editTextStyle);
|
||||
}
|
||||
|
||||
public EmojiEditText(Context context, @Nullable AttributeSet attrs,
|
||||
int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
// this ensures the view is redrawn when invalidated
|
||||
setLayerType(LAYER_TYPE_SOFTWARE, null);
|
||||
setFilters(new InputFilter[] {new EmojiFilter(this)});
|
||||
}
|
||||
|
||||
public void insertEmoji(String emoji) {
|
||||
int start = getSelectionStart();
|
||||
int end = getSelectionEnd();
|
||||
|
||||
getText().replace(Math.min(start, end), Math.max(start, end), emoji);
|
||||
setSelection(start + emoji.length());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateDrawable(@NonNull Drawable drawable) {
|
||||
if (drawable instanceof EmojiDrawable) invalidate();
|
||||
else super.invalidateDrawable(drawable);
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package org.thoughtcrime.securesms.components.emoji;
|
||||
|
||||
import android.support.annotation.UiThread;
|
||||
import android.text.InputFilter;
|
||||
import android.text.Spannable;
|
||||
import android.text.Spanned;
|
||||
import android.text.TextUtils;
|
||||
import android.widget.TextView;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@UiThread
|
||||
class EmojiFilter implements InputFilter {
|
||||
|
||||
private final TextView view;
|
||||
|
||||
EmojiFilter(TextView view) {
|
||||
this.view = view;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public CharSequence filter(CharSequence source, int start, int end,
|
||||
Spanned dest, int dstart, int dend) {
|
||||
|
||||
char[] v = new char[end - start];
|
||||
TextUtils.getChars(source, start, end, v, 0);
|
||||
Spannable emojified = EmojiProvider.getInstance(view.getContext())
|
||||
.emojify(new String(v), view);
|
||||
if (source instanceof Spanned && emojified != null) {
|
||||
TextUtils.copySpansFrom((Spanned) source, start, end, null,
|
||||
emojified, 0);
|
||||
}
|
||||
return emojified;
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package org.thoughtcrime.securesms.components.emoji;
|
||||
|
||||
import android.support.annotation.DrawableRes;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
interface EmojiPageModel {
|
||||
|
||||
@DrawableRes
|
||||
int getIcon();
|
||||
|
||||
@NonNull
|
||||
String[] getEmoji();
|
||||
|
||||
boolean hasSpriteMap();
|
||||
|
||||
@Nullable
|
||||
String getSprite();
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
package org.thoughtcrime.securesms.components.emoji;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.UiThread;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AbsListView;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.GridView;
|
||||
|
||||
import org.briarproject.briar.R;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@UiThread
|
||||
public class EmojiPageView extends FrameLayout {
|
||||
|
||||
private final GridView grid;
|
||||
|
||||
private EmojiSelectionListener listener;
|
||||
|
||||
public EmojiPageView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public EmojiPageView(Context context, @Nullable AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public EmojiPageView(Context context, @Nullable AttributeSet attrs,
|
||||
int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
View view = LayoutInflater.from(getContext())
|
||||
.inflate(R.layout.emoji_grid_layout, this, true);
|
||||
grid = view.findViewById(R.id.emoji);
|
||||
grid.setColumnWidth(getResources()
|
||||
.getDimensionPixelSize(R.dimen.emoji_drawer_size) + 2 *
|
||||
getResources().getDimensionPixelSize(
|
||||
R.dimen.emoji_drawer_item_padding));
|
||||
grid.setOnItemClickListener((parent, view1, position, id) -> {
|
||||
if (listener != null)
|
||||
listener.onEmojiSelected(((EmojiView) view1).getEmoji());
|
||||
});
|
||||
}
|
||||
|
||||
public void setModel(EmojiPageModel model) {
|
||||
grid.setAdapter(new EmojiGridAdapter(getContext(), model));
|
||||
}
|
||||
|
||||
public void setEmojiSelectedListener(EmojiSelectionListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
private static class EmojiGridAdapter extends BaseAdapter {
|
||||
|
||||
private final Context context;
|
||||
private final EmojiPageModel model;
|
||||
private final int emojiSize;
|
||||
|
||||
private EmojiGridAdapter(Context context, EmojiPageModel model) {
|
||||
this.context = context;
|
||||
this.model = model;
|
||||
emojiSize = (int) context.getResources()
|
||||
.getDimension(R.dimen.emoji_drawer_size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return model.getEmoji().length;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object getItem(int position) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
EmojiView view;
|
||||
int pad = context.getResources()
|
||||
.getDimensionPixelSize(R.dimen.emoji_drawer_item_padding);
|
||||
if (convertView != null && convertView instanceof EmojiView) {
|
||||
view = (EmojiView) convertView;
|
||||
} else {
|
||||
EmojiView emojiView = new EmojiView(context);
|
||||
emojiView.setPadding(pad, pad, pad, pad);
|
||||
emojiView.setLayoutParams(
|
||||
new AbsListView.LayoutParams(emojiSize + 2 * pad,
|
||||
emojiSize + 2 * pad));
|
||||
view = emojiView;
|
||||
}
|
||||
String emoji = model.getEmoji()[position];
|
||||
view.setEmoji(emoji);
|
||||
|
||||
return view;
|
||||
}
|
||||
}
|
||||
|
||||
interface EmojiSelectionListener {
|
||||
void onEmojiSelected(String emoji);
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
package org.thoughtcrime.securesms.components.emoji;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import org.briarproject.briar.R;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
class EmojiPages {
|
||||
|
||||
static List<EmojiPageModel> getPages(Context ctx) {
|
||||
return Arrays.asList(
|
||||
new StaticEmojiPageModel(ctx, R.drawable.ic_emoji_smiley_people,
|
||||
R.array.emoji_smiley_people,
|
||||
"emoji_smiley_people.png"),
|
||||
new StaticEmojiPageModel(ctx,
|
||||
R.drawable.ic_emoji_animals_nature,
|
||||
R.array.emoji_animals_nature,
|
||||
"emoji_animals_nature.png"),
|
||||
new StaticEmojiPageModel(ctx, R.drawable.ic_emoji_food_drink,
|
||||
R.array.emoji_food_drink,
|
||||
"emoji_food_drink.png"),
|
||||
new StaticEmojiPageModel(ctx, R.drawable.ic_emoji_travel_places,
|
||||
R.array.emoji_travel_places,
|
||||
"emoji_travel_places.png"),
|
||||
new StaticEmojiPageModel(ctx, R.drawable.ic_emoji_activity,
|
||||
R.array.emoji_activity,
|
||||
"emoji_activity.png"),
|
||||
new StaticEmojiPageModel(ctx, R.drawable.ic_emoji_objects,
|
||||
R.array.emoji_objects,
|
||||
"emoji_objects.png"),
|
||||
new StaticEmojiPageModel(ctx, R.drawable.ic_emoji_symbols,
|
||||
R.array.emoji_symbols,
|
||||
"emoji_symbols.png"),
|
||||
new StaticEmojiPageModel(ctx, R.drawable.ic_emoji_flags,
|
||||
R.array.emoji_flags,
|
||||
"emoji_flags.png"),
|
||||
|
||||
new StaticEmojiPageModel(R.drawable.ic_emoji_emoticons,
|
||||
new String[] {
|
||||
":-)", ";-)", "(-:", ":->", ":-D", "\\o/",
|
||||
":-P", "B-)", ":-$", ":-*", "O:-)", "=-O",
|
||||
"O_O", "O_o", "o_O", ":O", ":-!", ":-x",
|
||||
":-|", ":-\\", ":-(", ":'(", ":-[", ">:-(",
|
||||
"^.^", "^_^", "\\(\u02c6\u02da\u02c6)/",
|
||||
"\u30fd(\u00b0\u25c7\u00b0 )\u30ce",
|
||||
"\u00af\\(\u00b0_o)/\u00af",
|
||||
"\u00af\\_(\u30c4)_/\u00af", "(\u00ac_\u00ac)",
|
||||
"(>_<)", "(\u2565\ufe4f\u2565)",
|
||||
"(\u261e\uff9f\u30ee\uff9f)\u261e",
|
||||
"\u261c(\uff9f\u30ee\uff9f\u261c)",
|
||||
"\u261c(\u2312\u25bd\u2312)\u261e",
|
||||
"(\u256f\u00b0\u25a1\u00b0)\u256f\ufe35",
|
||||
"\u253b\u2501\u253b",
|
||||
"\u252c\u2500\u252c",
|
||||
"\u30ce(\u00b0\u2013\u00b0\u30ce)",
|
||||
"(^._.^)\uff89",
|
||||
"\u0e05^\u2022\ufecc\u2022^\u0e05",
|
||||
"(\u2022_\u2022)",
|
||||
" \u25a0-\u25a0\u00ac <(\u2022_\u2022) ",
|
||||
"(\u25a0_\u25a0\u00ac)"
|
||||
}, null));
|
||||
}
|
||||
}
|
||||
@@ -1,306 +0,0 @@
|
||||
package org.thoughtcrime.securesms.components.emoji;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.ColorFilter;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.AsyncTask;
|
||||
import android.support.annotation.UiThread;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.util.SparseArray;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.BriarApplication;
|
||||
import org.thoughtcrime.securesms.components.util.FutureTaskListener;
|
||||
import org.thoughtcrime.securesms.components.util.ListenableFutureTask;
|
||||
import org.thoughtcrime.securesms.util.BitmapDecodingException;
|
||||
import org.thoughtcrime.securesms.util.BitmapUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static android.graphics.Paint.ANTI_ALIAS_FLAG;
|
||||
import static android.graphics.Paint.FILTER_BITMAP_FLAG;
|
||||
import static android.graphics.PixelFormat.TRANSLUCENT;
|
||||
import static android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE;
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
public class EmojiProvider {
|
||||
|
||||
private static volatile EmojiProvider INSTANCE = null;
|
||||
|
||||
private static final Paint PAINT =
|
||||
new Paint(FILTER_BITMAP_FLAG | ANTI_ALIAS_FLAG);
|
||||
|
||||
@Inject
|
||||
AndroidExecutor androidExecutor;
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(EmojiProvider.class.getName());
|
||||
|
||||
private final SparseArray<DrawInfo> offsets = new SparseArray<>();
|
||||
|
||||
private static final Pattern EMOJI_RANGE = Pattern.compile(
|
||||
// 0x203c,0x2049 0x20a0-0x32ff 0x1f00-0x1fff 0xfe4e5-0xfe4ee
|
||||
// |=== !!, ?! ===||==== misc ===||========= emoticons =======||========== flags ==========|
|
||||
"[\\u203c\\u2049\\u20a0-\\u32ff\\ud83c\\udc00-\\ud83f\\udfff\\udbb9\\udce5-\\udbb9\\udcee]");
|
||||
|
||||
private static final int EMOJI_RAW_HEIGHT = 64;
|
||||
private static final int EMOJI_RAW_WIDTH = 64;
|
||||
private static final int EMOJI_VERT_PAD = 0;
|
||||
private static final int EMOJI_PER_ROW = 32;
|
||||
|
||||
private final Context context;
|
||||
private final float decodeScale;
|
||||
private final List<EmojiPageModel> staticPages;
|
||||
|
||||
static EmojiProvider getInstance(Context context) {
|
||||
if (INSTANCE == null) {
|
||||
synchronized (EmojiProvider.class) {
|
||||
if (INSTANCE == null) {
|
||||
LOG.info("Creating new instance of EmojiProvider");
|
||||
INSTANCE = new EmojiProvider(context);
|
||||
BriarApplication app =
|
||||
(BriarApplication) context.getApplicationContext();
|
||||
app.getApplicationComponent().inject(INSTANCE);
|
||||
}
|
||||
}
|
||||
}
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private EmojiProvider(Context context) {
|
||||
this.context = context.getApplicationContext();
|
||||
float drawerSize =
|
||||
context.getResources().getDimension(R.dimen.emoji_drawer_size);
|
||||
decodeScale = Math.min(1f, drawerSize / EMOJI_RAW_HEIGHT);
|
||||
staticPages = EmojiPages.getPages(context);
|
||||
for (EmojiPageModel page : staticPages) {
|
||||
if (page.hasSpriteMap()) {
|
||||
EmojiPageBitmap pageBitmap = new EmojiPageBitmap(page);
|
||||
for (int i = 0; i < page.getEmoji().length; i++) {
|
||||
offsets.put(Character.codePointAt(page.getEmoji()[i], 0),
|
||||
new DrawInfo(pageBitmap, i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@UiThread
|
||||
Spannable emojify(@Nullable CharSequence text, TextView tv) {
|
||||
if (text == null) return null;
|
||||
Matcher matches = EMOJI_RANGE.matcher(text);
|
||||
SpannableStringBuilder builder = new SpannableStringBuilder(text);
|
||||
|
||||
while (matches.find()) {
|
||||
int codePoint = matches.group().codePointAt(0);
|
||||
Drawable drawable = getEmojiDrawable(codePoint);
|
||||
if (drawable != null) {
|
||||
builder.setSpan(new EmojiSpan(drawable, tv), matches.start(),
|
||||
matches.end(), SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
}
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@UiThread
|
||||
Drawable getEmojiDrawable(int emojiCode) {
|
||||
return getEmojiDrawable(offsets.get(emojiCode));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Drawable getEmojiDrawable(@Nullable DrawInfo drawInfo) {
|
||||
if (drawInfo == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
EmojiDrawable drawable = new EmojiDrawable(drawInfo, decodeScale);
|
||||
drawInfo.page.get().addListener(new FutureTaskListener<Bitmap>() {
|
||||
@Override
|
||||
public void onSuccess(Bitmap result) {
|
||||
androidExecutor.runOnUiThread(() -> drawable.setBitmap(result));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable error) {
|
||||
logException(LOG, WARNING, error);
|
||||
}
|
||||
});
|
||||
return drawable;
|
||||
}
|
||||
|
||||
List<EmojiPageModel> getStaticPages() {
|
||||
return staticPages;
|
||||
}
|
||||
|
||||
|
||||
static class EmojiDrawable extends Drawable {
|
||||
|
||||
private final DrawInfo info;
|
||||
private final float intrinsicWidth, intrinsicHeight, verticalPad;
|
||||
|
||||
private Bitmap bmp;
|
||||
|
||||
private EmojiDrawable(DrawInfo info, float decodeScale) {
|
||||
this.info = info;
|
||||
intrinsicWidth = EMOJI_RAW_WIDTH * decodeScale;
|
||||
intrinsicHeight = EMOJI_RAW_HEIGHT * decodeScale;
|
||||
verticalPad = EMOJI_VERT_PAD * decodeScale;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIntrinsicWidth() {
|
||||
return (int) intrinsicWidth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIntrinsicHeight() {
|
||||
return (int) intrinsicHeight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
if (bmp == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
int row = info.index / EMOJI_PER_ROW;
|
||||
int rowIndex = info.index % EMOJI_PER_ROW;
|
||||
|
||||
int left = (int) (rowIndex * intrinsicWidth);
|
||||
int top = (int) (row * intrinsicHeight + row * verticalPad);
|
||||
int right = (int) ((rowIndex + 1) * intrinsicWidth);
|
||||
int bottom =
|
||||
(int) ((row + 1) * intrinsicHeight + row * verticalPad);
|
||||
canvas.drawBitmap(bmp, new Rect(left, top, right, bottom),
|
||||
getBounds(), PAINT);
|
||||
}
|
||||
|
||||
void setBitmap(Bitmap bitmap) {
|
||||
if (bmp == null || !bmp.sameAs(bitmap)) {
|
||||
bmp = bitmap;
|
||||
invalidateSelf();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOpacity() {
|
||||
return TRANSLUCENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAlpha(int alpha) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColorFilter(@Nullable ColorFilter cf) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class DrawInfo {
|
||||
|
||||
private final EmojiPageBitmap page;
|
||||
private final int index;
|
||||
|
||||
private DrawInfo(EmojiPageBitmap page, int index) {
|
||||
this.page = page;
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DrawInfo{ " + "page = " + page + ", index = " + index + '}';
|
||||
}
|
||||
}
|
||||
|
||||
private class EmojiPageBitmap {
|
||||
|
||||
private final EmojiPageModel model;
|
||||
|
||||
private ListenableFutureTask<Bitmap> task;
|
||||
|
||||
private volatile SoftReference<Bitmap> bitmapReference;
|
||||
|
||||
private EmojiPageBitmap(EmojiPageModel model) {
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private ListenableFutureTask<Bitmap> get() {
|
||||
if (bitmapReference != null) {
|
||||
Bitmap bitmap = bitmapReference.get();
|
||||
if (bitmap != null) return new ListenableFutureTask<>(bitmap);
|
||||
}
|
||||
if (task != null) return task;
|
||||
Callable<Bitmap> callable = () -> {
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Loading page " + model.getSprite());
|
||||
return loadPage();
|
||||
};
|
||||
task = new ListenableFutureTask<>(callable);
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
task.run();
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void aVoid) {
|
||||
task = null;
|
||||
}
|
||||
}.execute();
|
||||
return task;
|
||||
}
|
||||
|
||||
private Bitmap loadPage() throws IOException {
|
||||
if (bitmapReference != null) {
|
||||
Bitmap bitmap = bitmapReference.get();
|
||||
if (bitmap != null) return bitmap;
|
||||
}
|
||||
|
||||
try {
|
||||
Bitmap bitmap = BitmapUtil.createScaledBitmap(context,
|
||||
"file:///android_asset/" + model.getSprite(),
|
||||
decodeScale);
|
||||
bitmapReference = new SoftReference<>(bitmap);
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Loaded page " + model.getSprite());
|
||||
return bitmap;
|
||||
} catch (BitmapDecodingException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String toString() {
|
||||
return model.getSprite();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package org.thoughtcrime.securesms.components.emoji;
|
||||
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Paint.FontMetricsInt;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.UiThread;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.briarproject.briar.R;
|
||||
|
||||
@UiThread
|
||||
class EmojiSpan extends AnimatingImageSpan {
|
||||
|
||||
private final int size;
|
||||
private final FontMetricsInt fm;
|
||||
|
||||
EmojiSpan(@NonNull Drawable drawable, @NonNull TextView tv) {
|
||||
super(drawable, tv);
|
||||
fm = tv.getPaint().getFontMetricsInt();
|
||||
size = fm != null ? Math.abs(fm.descent) + Math.abs(fm.ascent)
|
||||
: tv.getResources().getDimensionPixelSize(
|
||||
R.dimen.conversation_item_body_text_size);
|
||||
getDrawable().setBounds(0, 0, size, size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize(Paint paint, CharSequence text, int start, int end,
|
||||
FontMetricsInt fm) {
|
||||
if (fm != null && this.fm != null) {
|
||||
fm.ascent = this.fm.ascent;
|
||||
fm.descent = this.fm.descent;
|
||||
fm.top = this.fm.top;
|
||||
fm.bottom = this.fm.bottom;
|
||||
return size;
|
||||
} else {
|
||||
return super.getSize(paint, text, start, end, fm);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
package org.thoughtcrime.securesms.components.emoji;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.UiThread;
|
||||
import android.support.v7.widget.AppCompatTextView;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.ViewConfiguration;
|
||||
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiProvider.EmojiDrawable;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static android.widget.TextView.BufferType.SPANNABLE;
|
||||
|
||||
@UiThread
|
||||
public class EmojiTextView extends AppCompatTextView {
|
||||
|
||||
public EmojiTextView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public EmojiTextView(Context context, @Nullable AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public EmojiTextView(Context context, @Nullable AttributeSet attrs,
|
||||
int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
// this ensures the view is redrawn when invalidated
|
||||
setLayerType(LAYER_TYPE_SOFTWARE, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setText(@Nullable CharSequence text, BufferType type) {
|
||||
CharSequence source =
|
||||
EmojiProvider.getInstance(getContext()).emojify(text, this);
|
||||
super.setText(source, SPANNABLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateDrawable(@NonNull Drawable drawable) {
|
||||
if (drawable instanceof EmojiDrawable) invalidate();
|
||||
else super.invalidateDrawable(drawable);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int left, int top, int right,
|
||||
int bottom) {
|
||||
// disable software layer if cache size is too small for it
|
||||
int drawingCacheSize = ViewConfiguration.get(getContext())
|
||||
.getScaledMaximumDrawingCacheSize();
|
||||
int width = right - left;
|
||||
int height = bottom - top;
|
||||
int size = width * height * 4;
|
||||
if (size > drawingCacheSize) {
|
||||
setLayerType(LAYER_TYPE_NONE, null);
|
||||
}
|
||||
super.onLayout(changed, left, top, right, bottom);
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
package org.thoughtcrime.securesms.components.emoji;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.support.annotation.UiThread;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v7.widget.AppCompatImageButton;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import org.briarproject.briar.R;
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiDrawer.EmojiDrawerListener;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@UiThread
|
||||
public class EmojiToggle extends AppCompatImageButton
|
||||
implements EmojiDrawerListener {
|
||||
|
||||
private final Drawable emojiToggle;
|
||||
private final Drawable imeToggle;
|
||||
|
||||
public EmojiToggle(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public EmojiToggle(Context context, @Nullable AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public EmojiToggle(Context context, @Nullable AttributeSet attrs,
|
||||
int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
|
||||
emojiToggle = ContextCompat
|
||||
.getDrawable(getContext(), R.drawable.ic_emoji_toggle);
|
||||
imeToggle = ContextCompat
|
||||
.getDrawable(getContext(), R.drawable.ic_keyboard);
|
||||
setToEmoji();
|
||||
}
|
||||
|
||||
public void setToEmoji() {
|
||||
setImageDrawable(emojiToggle);
|
||||
}
|
||||
|
||||
public void setToIme() {
|
||||
setImageDrawable(imeToggle);
|
||||
}
|
||||
|
||||
public void attach(EmojiDrawer drawer) {
|
||||
drawer.setDrawerListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShown() {
|
||||
setToIme();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHidden() {
|
||||
setToEmoji();
|
||||
}
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
package org.thoughtcrime.securesms.components.emoji;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.UiThread;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static android.graphics.Paint.ANTI_ALIAS_FLAG;
|
||||
import static android.graphics.Paint.Align.CENTER;
|
||||
import static android.graphics.Paint.FILTER_BITMAP_FLAG;
|
||||
import static org.briarproject.briar.android.util.UiUtils.resolveColorAttribute;
|
||||
|
||||
@UiThread
|
||||
public class EmojiView extends View implements Drawable.Callback {
|
||||
|
||||
private final Paint paint = new Paint(ANTI_ALIAS_FLAG | FILTER_BITMAP_FLAG);
|
||||
|
||||
private String emoji;
|
||||
private Drawable drawable;
|
||||
|
||||
public EmojiView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public EmojiView(Context context, @Nullable AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public EmojiView(Context context, @Nullable AttributeSet attrs,
|
||||
int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
public void setEmoji(String emoji) {
|
||||
this.emoji = emoji;
|
||||
this.drawable = EmojiProvider.getInstance(getContext())
|
||||
.getEmojiDrawable(Character.codePointAt(emoji, 0));
|
||||
postInvalidate();
|
||||
}
|
||||
|
||||
public String getEmoji() {
|
||||
return emoji;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
if (drawable != null) {
|
||||
drawable.setBounds(getPaddingLeft(),
|
||||
getPaddingTop(),
|
||||
getWidth() - getPaddingRight(),
|
||||
getHeight() - getPaddingBottom());
|
||||
drawable.setCallback(this);
|
||||
drawable.draw(canvas);
|
||||
} else {
|
||||
float targetFontSize =
|
||||
0.75f * getHeight() - getPaddingTop() - getPaddingBottom();
|
||||
paint.setTextSize(targetFontSize);
|
||||
int color = resolveColorAttribute(getContext(),
|
||||
android.R.attr.textColorPrimary);
|
||||
paint.setColor(color);
|
||||
paint.setTextAlign(CENTER);
|
||||
int xPos = (canvas.getWidth() / 2);
|
||||
int yPos = (int) ((canvas.getHeight() / 2) -
|
||||
((paint.descent() + paint.ascent()) / 2));
|
||||
|
||||
float overflow = paint.measureText(emoji) /
|
||||
(getWidth() - getPaddingLeft() - getPaddingRight());
|
||||
if (overflow > 1f) {
|
||||
paint.setTextSize(targetFontSize / overflow);
|
||||
yPos = (int) ((canvas.getHeight() / 2) -
|
||||
((paint.descent() + paint.ascent()) / 2));
|
||||
}
|
||||
canvas.drawText(emoji, xPos, yPos, paint);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateDrawable(@NonNull Drawable drawable) {
|
||||
super.invalidateDrawable(drawable);
|
||||
postInvalidate();
|
||||
}
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
package org.thoughtcrime.securesms.components.emoji;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.DrawableRes;
|
||||
|
||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||
import org.briarproject.bramble.api.settings.Settings;
|
||||
import org.briarproject.bramble.api.settings.SettingsManager;
|
||||
import org.briarproject.bramble.util.StringUtils;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.BriarApplication;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
import static org.briarproject.briar.android.settings.SettingsFragment.SETTINGS_NAMESPACE;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
public class RecentEmojiPageModel implements EmojiPageModel {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(RecentEmojiPageModel.class.getName());
|
||||
|
||||
private static final String EMOJI_LRU_PREFERENCE = "pref_emoji_recent2";
|
||||
private static final int EMOJI_LRU_SIZE = 50;
|
||||
|
||||
private final LinkedHashSet<String> recentlyUsed; // UI thread
|
||||
|
||||
@Inject
|
||||
SettingsManager settingsManager;
|
||||
|
||||
@Inject
|
||||
@DatabaseExecutor
|
||||
Executor dbExecutor;
|
||||
|
||||
RecentEmojiPageModel(Context context) {
|
||||
BriarApplication app =
|
||||
(BriarApplication) context.getApplicationContext();
|
||||
app.getApplicationComponent().inject(this);
|
||||
recentlyUsed = getPersistedCache();
|
||||
}
|
||||
|
||||
private LinkedHashSet<String> getPersistedCache() {
|
||||
String serialized;
|
||||
try {
|
||||
// FIXME: Don't make DB calls on the UI thread
|
||||
Settings settings = settingsManager.getSettings(SETTINGS_NAMESPACE);
|
||||
serialized = settings.get(EMOJI_LRU_PREFERENCE);
|
||||
} catch (DbException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
serialized = null;
|
||||
}
|
||||
return deserialize(serialized);
|
||||
}
|
||||
|
||||
@DrawableRes
|
||||
@Override
|
||||
public int getIcon() {
|
||||
return R.drawable.ic_emoji_recent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getEmoji() {
|
||||
return toReversePrimitiveArray(recentlyUsed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSpriteMap() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSprite() {
|
||||
return null;
|
||||
}
|
||||
|
||||
void onCodePointSelected(String emoji) {
|
||||
recentlyUsed.remove(emoji);
|
||||
recentlyUsed.add(emoji);
|
||||
|
||||
if (recentlyUsed.size() > EMOJI_LRU_SIZE) {
|
||||
Iterator<String> iterator = recentlyUsed.iterator();
|
||||
iterator.next();
|
||||
iterator.remove();
|
||||
}
|
||||
save(serialize(recentlyUsed));
|
||||
}
|
||||
|
||||
private String serialize(LinkedHashSet<String> emojis) {
|
||||
return StringUtils.join(emojis, "\t");
|
||||
}
|
||||
|
||||
private LinkedHashSet<String> deserialize(@Nullable String serialized) {
|
||||
if (serialized == null) return new LinkedHashSet<>();
|
||||
String[] list = serialized.split("\t");
|
||||
LinkedHashSet<String> result = new LinkedHashSet<>(list.length);
|
||||
Collections.addAll(result, list);
|
||||
return result;
|
||||
}
|
||||
|
||||
private void save(String serialized) {
|
||||
dbExecutor.execute(() -> {
|
||||
Settings settings = new Settings();
|
||||
settings.put(EMOJI_LRU_PREFERENCE, serialized);
|
||||
try {
|
||||
settingsManager.mergeSettings(settings, SETTINGS_NAMESPACE);
|
||||
} catch (DbException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private String[] toReversePrimitiveArray(LinkedHashSet<String> emojiSet) {
|
||||
String[] emojis = new String[emojiSet.size()];
|
||||
int i = emojiSet.size() - 1;
|
||||
for (String emoji : emojiSet) {
|
||||
emojis[i--] = emoji;
|
||||
}
|
||||
return emojis;
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
package org.thoughtcrime.securesms.components.emoji;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.ArrayRes;
|
||||
import android.support.annotation.DrawableRes;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.UiThread;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@UiThread
|
||||
class StaticEmojiPageModel implements EmojiPageModel {
|
||||
|
||||
@DrawableRes
|
||||
private final int icon;
|
||||
@NonNull
|
||||
private final String[] emoji;
|
||||
@Nullable
|
||||
private final String sprite;
|
||||
|
||||
StaticEmojiPageModel(@DrawableRes int icon, @NonNull String[] emoji,
|
||||
@Nullable String sprite) {
|
||||
this.icon = icon;
|
||||
this.emoji = emoji;
|
||||
this.sprite = sprite;
|
||||
}
|
||||
|
||||
StaticEmojiPageModel(Context ctx, @DrawableRes int icon,
|
||||
@ArrayRes int res, @Nullable String sprite) {
|
||||
this(icon, getEmoji(ctx, res), sprite);
|
||||
}
|
||||
|
||||
@DrawableRes
|
||||
@Override
|
||||
public int getIcon() {
|
||||
return icon;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public String[] getEmoji() {
|
||||
return emoji;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSpriteMap() {
|
||||
return sprite != null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getSprite() {
|
||||
return sprite;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static String[] getEmoji(Context ctx, @ArrayRes int res) {
|
||||
String[] rawStrings = ctx.getResources().getStringArray(res);
|
||||
String[] emoji = new String[rawStrings.length];
|
||||
int i = 0;
|
||||
for (String codePoint : rawStrings) {
|
||||
String[] bytes = codePoint.split(",");
|
||||
int[] codePoints = new int[bytes.length];
|
||||
int j = 0;
|
||||
for (String b : bytes) {
|
||||
codePoints[j] = Integer.valueOf(b, 16);
|
||||
}
|
||||
emoji[i] = new String(codePoints, 0, codePoints.length);
|
||||
i++;
|
||||
}
|
||||
return emoji;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package org.thoughtcrime.securesms.util;
|
||||
|
||||
public class BitmapDecodingException extends Exception {
|
||||
|
||||
BitmapDecodingException(String s) {
|
||||
super(s);
|
||||
}
|
||||
|
||||
BitmapDecodingException(Exception nested) {
|
||||
super(nested);
|
||||
}
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
package org.thoughtcrime.securesms.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.util.Pair;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.Priority;
|
||||
import com.bumptech.glide.load.DecodeFormat;
|
||||
import com.bumptech.glide.load.engine.Resource;
|
||||
import com.bumptech.glide.load.resource.bitmap.BitmapResource;
|
||||
import com.bumptech.glide.load.resource.bitmap.Downsampler;
|
||||
import com.bumptech.glide.load.resource.bitmap.FitCenter;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
|
||||
public class BitmapUtil {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(BitmapUtil.class.getName());
|
||||
|
||||
private static <T> InputStream getInputStreamForModel(Context context,
|
||||
T model)
|
||||
throws BitmapDecodingException {
|
||||
try {
|
||||
return Glide.buildStreamModelLoader(model, context)
|
||||
.getResourceFetcher(model, -1, -1)
|
||||
.loadData(Priority.NORMAL);
|
||||
} catch (Exception e) {
|
||||
throw new BitmapDecodingException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static <T> Bitmap createScaledBitmapInto(Context context, T model,
|
||||
int width, int height)
|
||||
throws BitmapDecodingException {
|
||||
Bitmap rough = Downsampler.AT_LEAST
|
||||
.decode(getInputStreamForModel(context, model),
|
||||
Glide.get(context).getBitmapPool(),
|
||||
width, height, DecodeFormat.PREFER_RGB_565);
|
||||
|
||||
Resource<Bitmap> resource = BitmapResource
|
||||
.obtain(rough, Glide.get(context).getBitmapPool());
|
||||
Resource<Bitmap> result =
|
||||
new FitCenter(context).transform(resource, width, height);
|
||||
|
||||
if (result == null) {
|
||||
throw new BitmapDecodingException("unable to transform Bitmap");
|
||||
}
|
||||
return result.get();
|
||||
}
|
||||
|
||||
public static <T> Bitmap createScaledBitmap(Context context, T model,
|
||||
float scale) throws BitmapDecodingException {
|
||||
Pair<Integer, Integer> dimens =
|
||||
getDimensions(getInputStreamForModel(context, model));
|
||||
return createScaledBitmapInto(context, model,
|
||||
(int) (dimens.first * scale), (int) (dimens.second * scale));
|
||||
}
|
||||
|
||||
private static BitmapFactory.Options getImageDimensions(
|
||||
InputStream inputStream)
|
||||
throws BitmapDecodingException {
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
BufferedInputStream fis = new BufferedInputStream(inputStream);
|
||||
BitmapFactory.decodeStream(fis, null, options);
|
||||
try {
|
||||
fis.close();
|
||||
} catch (IOException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
}
|
||||
|
||||
if (options.outWidth == -1 || options.outHeight == -1) {
|
||||
throw new BitmapDecodingException(
|
||||
"Failed to decode image dimensions: " + options.outWidth +
|
||||
", " + options.outHeight);
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
private static Pair<Integer, Integer> getDimensions(InputStream inputStream)
|
||||
throws BitmapDecodingException {
|
||||
BitmapFactory.Options options = getImageDimensions(inputStream);
|
||||
return new Pair<>(options.outWidth, options.outHeight);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
<include layout="@layout/contact_avatar_status"/>
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
<com.vanniktech.emoji.EmojiTextView
|
||||
android:id="@+id/contactName"
|
||||
style="@style/TextAppearance.AppCompat.Widget.ActionBar.Title.Inverse"
|
||||
android:textColor="@color/action_bar_text"
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
android:visibility="invisible"
|
||||
tools:ignore="ContentDescription"/>
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
<com.vanniktech.emoji.EmojiTextView
|
||||
android:id="@+id/authorName"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<merge
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<View
|
||||
style="@style/Divider.Horizontal"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<com.astuetz.PagerSlidingTabStrip
|
||||
android:id="@+id/tabs"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
app:pstsIndicatorColor="@color/color_primary"
|
||||
app:pstsIndicatorHeight="@dimen/emoji_drawer_indicator_height"
|
||||
app:pstsShouldExpand="true"
|
||||
app:pstsTabPaddingLeftRight="@dimen/emoji_drawer_left_right_padding"/>
|
||||
|
||||
<View
|
||||
android:layout_width="@dimen/margin_separator"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:background="@color/divider"/>
|
||||
|
||||
<org.thoughtcrime.securesms.components.RepeatableImageKey
|
||||
android:id="@+id/backspace"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:paddingLeft="@dimen/margin_medium"
|
||||
android:paddingRight="@dimen/margin_medium"
|
||||
android:src="@drawable/ic_backspace"
|
||||
app:tint="?attr/colorControlNormal"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
style="@style/Divider.Horizontal"/>
|
||||
|
||||
<android.support.v4.view.ViewPager
|
||||
android:id="@+id/emoji_pager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="visible"/>
|
||||
|
||||
</merge>
|
||||
@@ -1,19 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<GridView
|
||||
android:id="@+id/emoji"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:columnWidth="@dimen/emoji_drawer_size"
|
||||
android:gravity="center"
|
||||
android:horizontalSpacing="0dp"
|
||||
android:numColumns="auto_fit"
|
||||
android:stretchMode="columnWidth"
|
||||
android:verticalSpacing="0dp"
|
||||
android:visibility="visible"/>
|
||||
|
||||
</FrameLayout>
|
||||
@@ -36,7 +36,7 @@
|
||||
android:layout_marginStart="@dimen/listitem_horizontal_margin"
|
||||
tools:src="@mipmap/ic_launcher_round"/>
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
<com.vanniktech.emoji.EmojiTextView
|
||||
android:id="@+id/nameContact1"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
@@ -73,7 +73,7 @@
|
||||
android:layout_marginStart="@dimen/listitem_horizontal_margin"
|
||||
tools:src="@mipmap/ic_launcher_round"/>
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
<com.vanniktech.emoji.EmojiTextView
|
||||
android:id="@+id/nameContact2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
android:padding="@dimen/listitem_vertical_margin"
|
||||
app:persona="commenter"/>
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
<com.vanniktech.emoji.EmojiTextView
|
||||
android:id="@+id/bodyView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
android:src="@drawable/ic_repeat"
|
||||
app:tint="?attr/colorControlNormal"/>
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
<com.vanniktech.emoji.EmojiTextView
|
||||
android:id="@+id/bodyView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
android:layout_toRightOf="@+id/avatarFrameView"
|
||||
android:orientation="vertical">
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
<com.vanniktech.emoji.EmojiTextView
|
||||
android:id="@+id/nameView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
android:layout_marginStart="@dimen/listitem_horizontal_margin"
|
||||
tools:src="@mipmap/ic_launcher_round"/>
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
<com.vanniktech.emoji.EmojiTextView
|
||||
android:id="@+id/nameView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
android:elevation="@dimen/message_bubble_elevation"
|
||||
android:orientation="vertical">
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
<com.vanniktech.emoji.EmojiTextView
|
||||
android:id="@+id/text"
|
||||
style="@style/TextMessage"
|
||||
android:layout_width="wrap_content"
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
android:background="@drawable/msg_out"
|
||||
android:elevation="@dimen/message_bubble_elevation">
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
<com.vanniktech.emoji.EmojiTextView
|
||||
android:id="@+id/text"
|
||||
style="@style/TextMessage"
|
||||
android:layout_width="wrap_content"
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
android:layout_marginTop="@dimen/message_bubble_margin"
|
||||
android:orientation="vertical">
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
<com.vanniktech.emoji.EmojiTextView
|
||||
android:id="@+id/msgText"
|
||||
style="@style/TextMessage"
|
||||
android:layout_width="match_parent"
|
||||
@@ -29,7 +29,7 @@
|
||||
android:background="@drawable/notice_in_bottom"
|
||||
android:elevation="@dimen/message_bubble_elevation">
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
<com.vanniktech.emoji.EmojiTextView
|
||||
android:id="@+id/text"
|
||||
style="@style/TextMessage.Notice"
|
||||
android:layout_width="match_parent"
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
android:layout_marginTop="@dimen/message_bubble_margin"
|
||||
android:orientation="vertical">
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
<com.vanniktech.emoji.EmojiTextView
|
||||
android:id="@+id/msgText"
|
||||
style="@style/TextMessage"
|
||||
android:layout_width="match_parent"
|
||||
@@ -30,7 +30,7 @@
|
||||
android:background="@drawable/notice_out_bottom"
|
||||
android:elevation="@dimen/message_bubble_elevation">
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
<com.vanniktech.emoji.EmojiTextView
|
||||
android:id="@+id/text"
|
||||
style="@style/TextMessage.Notice"
|
||||
android:layout_width="match_parent"
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
android:layout_marginTop="@dimen/message_bubble_margin"
|
||||
android:orientation="vertical">
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
<com.vanniktech.emoji.EmojiTextView
|
||||
android:id="@+id/msgText"
|
||||
style="@style/TextMessage"
|
||||
android:layout_width="match_parent"
|
||||
@@ -29,7 +29,7 @@
|
||||
android:background="@drawable/notice_in_bottom"
|
||||
android:elevation="@dimen/message_bubble_elevation">
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
<com.vanniktech.emoji.EmojiTextView
|
||||
android:id="@+id/text"
|
||||
style="@style/TextMessage.Notice"
|
||||
android:layout_width="match_parent"
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
android:layout_marginRight="@dimen/listitem_horizontal_margin"
|
||||
android:layout_marginTop="@dimen/listitem_horizontal_margin"/>
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
<com.vanniktech.emoji.EmojiTextView
|
||||
android:id="@+id/forumNameView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
android:layout_marginRight="@dimen/listitem_horizontal_margin"
|
||||
android:layout_marginTop="@dimen/listitem_horizontal_margin"/>
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
<com.vanniktech.emoji.EmojiTextView
|
||||
android:id="@+id/nameView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
@@ -32,7 +32,7 @@
|
||||
android:textSize="@dimen/text_size_medium"
|
||||
tools:text="This is a name of a Private Group"/>
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
<com.vanniktech.emoji.EmojiTextView
|
||||
android:id="@+id/creatorView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
android:baselineAligned="false"
|
||||
android:orientation="vertical">
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
<com.vanniktech.emoji.EmojiTextView
|
||||
android:id="@+id/text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_marginRight="@dimen/listitem_horizontal_margin"/>
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
<com.vanniktech.emoji.EmojiTextView
|
||||
android:id="@+id/forumNameView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
@@ -30,7 +30,7 @@
|
||||
android:textSize="@dimen/text_size_medium"
|
||||
tools:text="This is a name of a forum that is available"/>
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
<com.vanniktech.emoji.EmojiTextView
|
||||
android:id="@+id/sharedByView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
android:layout_marginRight="@dimen/listitem_horizontal_margin"
|
||||
tools:src="@mipmap/ic_launcher_round"/>
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
<com.vanniktech.emoji.EmojiTextView
|
||||
android:id="@+id/nameView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
android:layout_marginTop="@dimen/listitem_horizontal_margin"
|
||||
android:background="?attr/selectableItemBackground">
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
<com.vanniktech.emoji.EmojiTextView
|
||||
android:id="@+id/titleView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
@@ -42,7 +42,7 @@
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:textSize="@dimen/text_size_small"/>
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
<com.vanniktech.emoji.EmojiTextView
|
||||
android:id="@+id/authorView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
@@ -101,7 +101,7 @@
|
||||
android:textSize="@dimen/text_size_small"
|
||||
tools:text="5 min. ago"/>
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
<com.vanniktech.emoji.EmojiTextView
|
||||
android:id="@+id/descriptionView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
android:layout_toRightOf="@+id/avatarView"
|
||||
android:orientation="vertical">
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
<com.vanniktech.emoji.EmojiTextView
|
||||
android:id="@+id/nameView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1">
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
<com.vanniktech.emoji.EmojiTextView
|
||||
android:id="@+id/text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
@@ -16,17 +16,18 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/card_background">
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiToggle
|
||||
<android.support.v7.widget.AppCompatImageButton
|
||||
android:id="@+id/emoji_toggle"
|
||||
android:layout_width="@dimen/text_input_height"
|
||||
android:layout_height="@dimen/text_input_height"
|
||||
android:layout_gravity="bottom"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:src="@drawable/ic_emoji_toggle"
|
||||
android:padding="@dimen/margin_small"
|
||||
android:scaleType="center"
|
||||
app:tint="?attr/colorControlNormal"/>
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiEditText
|
||||
<com.vanniktech.emoji.EmojiEditText
|
||||
android:id="@+id/input_text"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
@@ -56,11 +57,4 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiDrawer
|
||||
android:id="@+id/emoji_drawer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/input_text"
|
||||
android:visibility="gone"/>
|
||||
|
||||
</merge>
|
||||
|
||||
@@ -27,17 +27,18 @@
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiToggle
|
||||
<android.support.v7.widget.AppCompatImageButton
|
||||
android:id="@+id/emoji_toggle"
|
||||
android:layout_width="@dimen/text_input_height"
|
||||
android:layout_height="@dimen/text_input_height"
|
||||
android:layout_gravity="bottom"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:src="@drawable/ic_emoji_toggle"
|
||||
android:padding="@dimen/margin_small"
|
||||
android:scaleType="center"
|
||||
app:tint="?attr/colorControlNormal"/>
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiEditText
|
||||
<com.vanniktech.emoji.EmojiEditText
|
||||
android:id="@+id/input_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@@ -64,10 +65,4 @@
|
||||
android:layout_marginRight="@dimen/margin_small"
|
||||
android:layout_marginStart="@dimen/margin_small"/>
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiDrawer
|
||||
android:id="@+id/emoji_drawer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"/>
|
||||
|
||||
</merge>
|
||||
|
||||
@@ -63,7 +63,6 @@ dependencyVerification {
|
||||
'com.android.tools:repository:26.1.3:repository-26.1.3.jar:52d4539cc68db91b261e2a33b2c8206b26e05539078758dc28cfb3854adb4f59',
|
||||
'com.android.tools:sdk-common:26.1.3:sdk-common-26.1.3.jar:1948603ca9ff22c7ebb3178000bffa3a9dd2ca1cc5cb0c793cae08468b8fcfc1',
|
||||
'com.android.tools:sdklib:26.1.3:sdklib-26.1.3.jar:4adcfaad9514607098d2c51503c39811112d3050f4d1e744c01c7f08f591032b',
|
||||
'com.github.bumptech.glide:glide:3.8.0:glide-3.8.0.jar:750d9e7b940dc0ee48f8680623b55d46e14e8727acc922d7b156e57e7c549655',
|
||||
'com.google.android.apps.common.testing.accessibility.framework:accessibility-test-framework:2.0:accessibility-test-framework-2.0.jar:cdf16ef8f5b8023d003ce3cc1b0d51bda737762e2dab2fedf43d1c4292353f7f',
|
||||
'com.google.android.apps.common.testing.accessibility.framework:accessibility-test-framework:2.1:accessibility-test-framework-2.1.jar:7b0aa6ed7553597ce0610684a9f7eca8021eee218f2e2f427c04a7fbf5f920bd',
|
||||
'com.google.code.findbugs:jsr305:1.3.9:jsr305-1.3.9.jar:905721a0eea90a81534abb7ee6ef4ea2e5e645fa1def0a5cd88402df1b46c9ed',
|
||||
@@ -83,13 +82,14 @@ dependencyVerification {
|
||||
'com.google.zxing:core:3.3.0:core-3.3.0.jar:bba7724e02a997cec38213af77133ee8e24b0d5cf5fa7ecbc16a4fa93f11ee0d',
|
||||
'com.googlecode.json-simple:json-simple:1.1:json-simple-1.1.jar:2d9484f4c649f708f47f9a479465fc729770ee65617dca3011836602264f6439',
|
||||
'com.ibm.icu:icu4j:53.1:icu4j-53.1.jar:e37a4467bac5cdeb02c5c4b8e5063d2f4e67b69e3c7df6d6b610f13185572bab',
|
||||
'com.jpardogo.materialtabstrip:library:1.1.0:library-1.1.0.aar:24d19232b319f8c73e25793432357919a7ed972186f57a3b2c9093ea74ad8311',
|
||||
'com.squareup:javawriter:2.1.1:javawriter-2.1.1.jar:f699823d0081f69cbb676c1845ea222e0ada79bc88a53e5d22d8bd02d328f57e',
|
||||
'com.squareup:javawriter:2.5.0:javawriter-2.5.0.jar:fcfb09fb0ea0aa97d3cfe7ea792398081348e468f126b3603cb3803f240197f0',
|
||||
'com.sun.activation:javax.activation:1.2.0:javax.activation-1.2.0.jar:993302b16cd7056f21e779cc577d175a810bb4900ef73cd8fbf2b50f928ba9ce',
|
||||
'com.sun.istack:istack-commons-runtime:2.21:istack-commons-runtime-2.21.jar:c33e67a0807095f02a0e2da139412dd7c4f9cc1a4c054b3e434f96831ba950f4',
|
||||
'com.sun.xml.fastinfoset:FastInfoset:1.2.13:FastInfoset-1.2.13.jar:27a77db909f3c2833c0b1a37c55af1db06045118ad2eed96ce567b6632bce038',
|
||||
'com.thoughtworks.xstream:xstream:1.4.8:xstream-1.4.8.jar:a219a1abf948400b669d08be73a6f9209fb720d237e5ff74c223bffe5f9df93d',
|
||||
'com.vanniktech:emoji-google:0.5.1:emoji-google-0.5.1.aar:5f4a88e1a3bb5f694ddccf2e49dc9ccc44431f1d4f980bc453c178f57869dea0',
|
||||
'com.vanniktech:emoji:0.5.1:emoji-0.5.1.aar:d55f44e04e31895647d62c33c34a8d501995e9e1b5c5f5cee6cbb8630eeb37f7',
|
||||
'commons-codec:commons-codec:1.6:commons-codec-1.6.jar:54b34e941b8e1414bd3e40d736efd3481772dc26db3296f6aa45cec9f6203d86',
|
||||
'commons-logging:commons-logging:1.1.1:commons-logging-1.1.1.jar:ce6f913cad1f0db3aad70186d65c5bc7ffcc9a99e3fe8e0b137312819f7c362f',
|
||||
'de.hdodenhof:circleimageview:2.2.0:circleimageview-2.2.0.aar:bc34761dcd5036229ac1ffed6b5b1bef722ad5b097a2c8bba1c5ed2cd4b5c82b',
|
||||
|
||||