diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/settings/SettingsManager.java b/bramble-api/src/main/java/org/briarproject/bramble/api/settings/SettingsManager.java index c8283640b..56ec0c3f0 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/settings/SettingsManager.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/settings/SettingsManager.java @@ -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. diff --git a/bramble-core/src/main/java/org/briarproject/bramble/settings/SettingsManagerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/settings/SettingsManagerImpl.java index 5663f8e8b..13d2c6a25 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/settings/SettingsManagerImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/settings/SettingsManagerImpl.java @@ -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); diff --git a/briar-android/build.gradle b/briar-android/build.gradle index d9e683775..bb4988e82 100644 --- a/briar-android/build.gradle +++ b/briar-android/build.gradle @@ -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" diff --git a/briar-android/proguard-rules.txt b/briar-android/proguard-rules.txt index 88fb5b29b..f673f857a 100644 --- a/briar-android/proguard-rules.txt +++ b/briar-android/proguard-rules.txt @@ -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.** diff --git a/briar-android/src/main/assets/emoji_activity.png b/briar-android/src/main/assets/emoji_activity.png deleted file mode 100644 index 908370dd1..000000000 Binary files a/briar-android/src/main/assets/emoji_activity.png and /dev/null differ diff --git a/briar-android/src/main/assets/emoji_animals_nature.png b/briar-android/src/main/assets/emoji_animals_nature.png deleted file mode 100644 index 7a0661fe1..000000000 Binary files a/briar-android/src/main/assets/emoji_animals_nature.png and /dev/null differ diff --git a/briar-android/src/main/assets/emoji_flags.png b/briar-android/src/main/assets/emoji_flags.png deleted file mode 100644 index 80f6bbd0d..000000000 Binary files a/briar-android/src/main/assets/emoji_flags.png and /dev/null differ diff --git a/briar-android/src/main/assets/emoji_food_drink.png b/briar-android/src/main/assets/emoji_food_drink.png deleted file mode 100644 index 33d7cd0a6..000000000 Binary files a/briar-android/src/main/assets/emoji_food_drink.png and /dev/null differ diff --git a/briar-android/src/main/assets/emoji_objects.png b/briar-android/src/main/assets/emoji_objects.png deleted file mode 100644 index f2b6dfbf9..000000000 Binary files a/briar-android/src/main/assets/emoji_objects.png and /dev/null differ diff --git a/briar-android/src/main/assets/emoji_smiley_people.png b/briar-android/src/main/assets/emoji_smiley_people.png deleted file mode 100644 index 9325d703a..000000000 Binary files a/briar-android/src/main/assets/emoji_smiley_people.png and /dev/null differ diff --git a/briar-android/src/main/assets/emoji_symbols.png b/briar-android/src/main/assets/emoji_symbols.png deleted file mode 100644 index e88275b2f..000000000 Binary files a/briar-android/src/main/assets/emoji_symbols.png and /dev/null differ diff --git a/briar-android/src/main/assets/emoji_travel_places.png b/briar-android/src/main/assets/emoji_travel_places.png deleted file mode 100644 index 16e2be37d..000000000 Binary files a/briar-android/src/main/assets/emoji_travel_places.png and /dev/null differ diff --git a/briar-android/src/main/java/org/briarproject/briar/android/AndroidComponent.java b/briar-android/src/main/java/org/briarproject/briar/android/AndroidComponent.java index 771a2ad57..6971b49a6 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/AndroidComponent.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/AndroidComponent.java @@ -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); } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/AppModule.java b/briar-android/src/main/java/org/briarproject/briar/android/AppModule.java index 3c3512281..606647958 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/AppModule.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/AppModule.java @@ -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; + } } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/BriarApplicationImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/BriarApplicationImpl.java index e56adf30e..040d83871 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/BriarApplicationImpl.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/BriarApplicationImpl.java @@ -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 diff --git a/briar-android/src/main/java/org/briarproject/briar/android/RecentEmojiImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/RecentEmojiImpl.java new file mode 100644 index 000000000..72e4b7e96 --- /dev/null +++ b/briar-android/src/main/java/org/briarproject/briar/android/RecentEmojiImpl.java @@ -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 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 getRecentEmojis() { + return new ArrayList<>(recentlyUsed); + } + + @Override + public void addEmoji(Emoji emoji) { + recentlyUsed.remove(emoji); + recentlyUsed.add(emoji); + + if (recentlyUsed.size() > EMOJI_LRU_SIZE) { + Iterator 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 emojis) { + Collection strings = new ArrayList<>(emojis.size()); + for (Emoji emoji : emojis) strings.add(emoji.getUnicode()); + return StringUtils.join(strings, "\t"); + } + + private Collection deserialize(String serialized) { + Collection ranges = EmojiUtils.emojis(serialized); + Collection 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); + } + }); + } +} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/conversation/GroupActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/conversation/GroupActivity.java index fd77da85d..822262459 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/conversation/GroupActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/conversation/GroupActivity.java @@ -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); } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/view/LargeTextInputView.java b/briar-android/src/main/java/org/briarproject/briar/android/view/LargeTextInputView.java index 9bcd6c451..1427f33ef 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/view/LargeTextInputView.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/view/LargeTextInputView.java @@ -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); } } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/view/TextInputView.java b/briar-android/src/main/java/org/briarproject/briar/android/view/TextInputView.java index 06bd583e6..b92abe138 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/view/TextInputView.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/view/TextInputView.java @@ -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); } diff --git a/briar-android/src/main/java/org/thoughtcrime/securesms/components/RepeatableImageKey.java b/briar-android/src/main/java/org/thoughtcrime/securesms/components/RepeatableImageKey.java deleted file mode 100644 index 51e740186..000000000 --- a/briar-android/src/main/java/org/thoughtcrime/securesms/components/RepeatableImageKey.java +++ /dev/null @@ -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(); - } -} diff --git a/briar-android/src/main/java/org/thoughtcrime/securesms/components/emoji/AnimatingImageSpan.java b/briar-android/src/main/java/org/thoughtcrime/securesms/components/emoji/AnimatingImageSpan.java deleted file mode 100644 index 28a71933f..000000000 --- a/briar-android/src/main/java/org/thoughtcrime/securesms/components/emoji/AnimatingImageSpan.java +++ /dev/null @@ -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); - } -} diff --git a/briar-android/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiDrawer.java b/briar-android/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiDrawer.java deleted file mode 100644 index d54f4cbca..000000000 --- a/briar-android/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiDrawer.java +++ /dev/null @@ -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 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 pages; - private EmojiSelectionListener listener; - - private EmojiPagerAdapter(@NonNull Context context, - @NonNull List 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(); - } -} diff --git a/briar-android/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiEditText.java b/briar-android/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiEditText.java deleted file mode 100644 index 6e325a9ad..000000000 --- a/briar-android/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiEditText.java +++ /dev/null @@ -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); - } -} diff --git a/briar-android/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiFilter.java b/briar-android/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiFilter.java deleted file mode 100644 index 3afc44c2f..000000000 --- a/briar-android/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiFilter.java +++ /dev/null @@ -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; - } -} diff --git a/briar-android/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiPageModel.java b/briar-android/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiPageModel.java deleted file mode 100644 index eeaf12409..000000000 --- a/briar-android/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiPageModel.java +++ /dev/null @@ -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(); -} diff --git a/briar-android/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiPageView.java b/briar-android/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiPageView.java deleted file mode 100644 index 91598c381..000000000 --- a/briar-android/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiPageView.java +++ /dev/null @@ -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); - } -} diff --git a/briar-android/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiPages.java b/briar-android/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiPages.java deleted file mode 100644 index 6d77f74b9..000000000 --- a/briar-android/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiPages.java +++ /dev/null @@ -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 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)); - } -} diff --git a/briar-android/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiProvider.java b/briar-android/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiProvider.java deleted file mode 100644 index 90afbdeca..000000000 --- a/briar-android/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiProvider.java +++ /dev/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 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 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() { - @Override - public void onSuccess(Bitmap result) { - androidExecutor.runOnUiThread(() -> drawable.setBitmap(result)); - } - - @Override - public void onFailure(Throwable error) { - logException(LOG, WARNING, error); - } - }); - return drawable; - } - - List 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 task; - - private volatile SoftReference bitmapReference; - - private EmojiPageBitmap(EmojiPageModel model) { - this.model = model; - } - - @UiThread - private ListenableFutureTask get() { - if (bitmapReference != null) { - Bitmap bitmap = bitmapReference.get(); - if (bitmap != null) return new ListenableFutureTask<>(bitmap); - } - if (task != null) return task; - Callable callable = () -> { - if (LOG.isLoggable(INFO)) - LOG.info("Loading page " + model.getSprite()); - return loadPage(); - }; - task = new ListenableFutureTask<>(callable); - new AsyncTask() { - @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(); - } - } - -} diff --git a/briar-android/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiSpan.java b/briar-android/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiSpan.java deleted file mode 100644 index 7cab093d8..000000000 --- a/briar-android/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiSpan.java +++ /dev/null @@ -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); - } - } -} diff --git a/briar-android/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiTextView.java b/briar-android/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiTextView.java deleted file mode 100644 index 76458decf..000000000 --- a/briar-android/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiTextView.java +++ /dev/null @@ -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); - } -} diff --git a/briar-android/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiToggle.java b/briar-android/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiToggle.java deleted file mode 100644 index 5858af0a7..000000000 --- a/briar-android/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiToggle.java +++ /dev/null @@ -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(); - } -} diff --git a/briar-android/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiView.java b/briar-android/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiView.java deleted file mode 100644 index 2b6d61039..000000000 --- a/briar-android/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiView.java +++ /dev/null @@ -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(); - } -} diff --git a/briar-android/src/main/java/org/thoughtcrime/securesms/components/emoji/RecentEmojiPageModel.java b/briar-android/src/main/java/org/thoughtcrime/securesms/components/emoji/RecentEmojiPageModel.java deleted file mode 100644 index f47ce308f..000000000 --- a/briar-android/src/main/java/org/thoughtcrime/securesms/components/emoji/RecentEmojiPageModel.java +++ /dev/null @@ -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 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 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 iterator = recentlyUsed.iterator(); - iterator.next(); - iterator.remove(); - } - save(serialize(recentlyUsed)); - } - - private String serialize(LinkedHashSet emojis) { - return StringUtils.join(emojis, "\t"); - } - - private LinkedHashSet deserialize(@Nullable String serialized) { - if (serialized == null) return new LinkedHashSet<>(); - String[] list = serialized.split("\t"); - LinkedHashSet 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 emojiSet) { - String[] emojis = new String[emojiSet.size()]; - int i = emojiSet.size() - 1; - for (String emoji : emojiSet) { - emojis[i--] = emoji; - } - return emojis; - } -} diff --git a/briar-android/src/main/java/org/thoughtcrime/securesms/components/emoji/StaticEmojiPageModel.java b/briar-android/src/main/java/org/thoughtcrime/securesms/components/emoji/StaticEmojiPageModel.java deleted file mode 100644 index 4f9c9b94c..000000000 --- a/briar-android/src/main/java/org/thoughtcrime/securesms/components/emoji/StaticEmojiPageModel.java +++ /dev/null @@ -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; - } - -} diff --git a/briar-android/src/main/java/org/thoughtcrime/securesms/util/BitmapDecodingException.java b/briar-android/src/main/java/org/thoughtcrime/securesms/util/BitmapDecodingException.java deleted file mode 100644 index 777fdfb2e..000000000 --- a/briar-android/src/main/java/org/thoughtcrime/securesms/util/BitmapDecodingException.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.thoughtcrime.securesms.util; - -public class BitmapDecodingException extends Exception { - - BitmapDecodingException(String s) { - super(s); - } - - BitmapDecodingException(Exception nested) { - super(nested); - } -} diff --git a/briar-android/src/main/java/org/thoughtcrime/securesms/util/BitmapUtil.java b/briar-android/src/main/java/org/thoughtcrime/securesms/util/BitmapUtil.java deleted file mode 100644 index 32d924d7b..000000000 --- a/briar-android/src/main/java/org/thoughtcrime/securesms/util/BitmapUtil.java +++ /dev/null @@ -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 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 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 resource = BitmapResource - .obtain(rough, Glide.get(context).getBitmapPool()); - Resource result = - new FitCenter(context).transform(resource, width, height); - - if (result == null) { - throw new BitmapDecodingException("unable to transform Bitmap"); - } - return result.get(); - } - - public static Bitmap createScaledBitmap(Context context, T model, - float scale) throws BitmapDecodingException { - Pair 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 getDimensions(InputStream inputStream) - throws BitmapDecodingException { - BitmapFactory.Options options = getImageDimensions(inputStream); - return new Pair<>(options.outWidth, options.outHeight); - } - -} diff --git a/briar-android/src/main/res/layout/activity_conversation.xml b/briar-android/src/main/res/layout/activity_conversation.xml index 6208ec5fd..229c1d13e 100644 --- a/briar-android/src/main/res/layout/activity_conversation.xml +++ b/briar-android/src/main/res/layout/activity_conversation.xml @@ -24,7 +24,7 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/briar-android/src/main/res/layout/emoji_grid_layout.xml b/briar-android/src/main/res/layout/emoji_grid_layout.xml deleted file mode 100644 index c1486e2dc..000000000 --- a/briar-android/src/main/res/layout/emoji_grid_layout.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/briar-android/src/main/res/layout/introduction_message.xml b/briar-android/src/main/res/layout/introduction_message.xml index 9fe6196d5..303352afa 100644 --- a/briar-android/src/main/res/layout/introduction_message.xml +++ b/briar-android/src/main/res/layout/introduction_message.xml @@ -36,7 +36,7 @@ android:layout_marginStart="@dimen/listitem_horizontal_margin" tools:src="@mipmap/ic_launcher_round"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/briar-android/src/main/res/layout/text_input_view_large.xml b/briar-android/src/main/res/layout/text_input_view_large.xml index a4a9d7b02..b5ced660d 100644 --- a/briar-android/src/main/res/layout/text_input_view_large.xml +++ b/briar-android/src/main/res/layout/text_input_view_large.xml @@ -27,17 +27,18 @@ android:layout_height="match_parent" android:orientation="horizontal"> - - - - diff --git a/briar-android/witness.gradle b/briar-android/witness.gradle index 8fe1e3e98..e262ffb0c 100644 --- a/briar-android/witness.gradle +++ b/briar-android/witness.gradle @@ -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',