Merge branch '1343-vanniktech-emoji' into 'master'
Use vanniktech emoji library Closes #1343, #1314, #940, #930, #749, and #684 See merge request briar/briar!857
@@ -1,6 +1,7 @@
|
|||||||
package org.briarproject.bramble.api.settings;
|
package org.briarproject.bramble.api.settings;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
|
import org.briarproject.bramble.api.db.Transaction;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
@@ -11,6 +12,11 @@ public interface SettingsManager {
|
|||||||
*/
|
*/
|
||||||
Settings getSettings(String namespace) throws DbException;
|
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
|
* Merges the given settings with any existing settings in the given
|
||||||
* namespace.
|
* namespace.
|
||||||
|
|||||||
@@ -34,6 +34,12 @@ class SettingsManagerImpl implements SettingsManager {
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Settings getSettings(Transaction txn, String namespace)
|
||||||
|
throws DbException {
|
||||||
|
return db.getSettings(txn, namespace);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mergeSettings(Settings s, String namespace) throws DbException {
|
public void mergeSettings(Settings s, String namespace) throws DbException {
|
||||||
Transaction txn = db.startTransaction(false);
|
Transaction txn = db.startTransaction(false);
|
||||||
|
|||||||
@@ -31,9 +31,8 @@ dependencies {
|
|||||||
implementation 'info.guardianproject.trustedintents:trustedintents:0.2'
|
implementation 'info.guardianproject.trustedintents:trustedintents:0.2'
|
||||||
implementation 'de.hdodenhof:circleimageview:2.2.0'
|
implementation 'de.hdodenhof:circleimageview:2.2.0'
|
||||||
implementation 'com.google.zxing:core:3.3.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 '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'
|
annotationProcessor 'com.google.dagger:dagger-compiler:2.0.2'
|
||||||
|
|
||||||
@@ -80,7 +79,7 @@ android {
|
|||||||
buildToolsVersion '27.0.3'
|
buildToolsVersion '27.0.3'
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 14
|
minSdkVersion 15
|
||||||
targetSdkVersion 26
|
targetSdkVersion 26
|
||||||
versionCode 10013
|
versionCode 10013
|
||||||
versionName "1.0.13"
|
versionName "1.0.13"
|
||||||
|
|||||||
@@ -27,9 +27,4 @@
|
|||||||
-dontnote org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout
|
-dontnote org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout
|
||||||
|
|
||||||
# Emoji
|
# Emoji
|
||||||
-keep class org.thoughtcrime.securesms.**
|
-keep class com.vanniktech.emoji.**
|
||||||
-keep class com.astuetz.PagerSlidingTabStrip$OnTabReselectedListener
|
|
||||||
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
|
|
||||||
**[] $VALUES;
|
|
||||||
public *;
|
|
||||||
}
|
|
||||||
|
|||||||
|
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.BriarCoreModule;
|
||||||
import org.briarproject.briar.android.login.SignInReminderReceiver;
|
import org.briarproject.briar.android.login.SignInReminderReceiver;
|
||||||
import org.briarproject.briar.android.reporting.BriarReportSender;
|
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.AndroidNotificationManager;
|
||||||
import org.briarproject.briar.api.android.DozeWatchdog;
|
import org.briarproject.briar.api.android.DozeWatchdog;
|
||||||
import org.briarproject.briar.api.android.LockManager;
|
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.GroupInvitationFactory;
|
||||||
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager;
|
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager;
|
||||||
import org.briarproject.briar.api.test.TestDataCreator;
|
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;
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
@@ -161,12 +160,10 @@ public interface AndroidComponent
|
|||||||
|
|
||||||
void inject(BriarReportSender briarReportSender);
|
void inject(BriarReportSender briarReportSender);
|
||||||
|
|
||||||
void inject(EmojiProvider emojiProvider);
|
|
||||||
|
|
||||||
void inject(RecentEmojiPageModel recentEmojiPageModel);
|
|
||||||
|
|
||||||
void inject(NotificationCleanupService notificationCleanupService);
|
void inject(NotificationCleanupService notificationCleanupService);
|
||||||
|
|
||||||
|
void inject(TextInputView textInputView);
|
||||||
|
|
||||||
// Eager singleton load
|
// Eager singleton load
|
||||||
void inject(AppModule.EagerSingletons init);
|
void inject(AppModule.EagerSingletons init);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import android.content.Context;
|
|||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.os.StrictMode;
|
import android.os.StrictMode;
|
||||||
|
|
||||||
|
import com.vanniktech.emoji.RecentEmoji;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||||
@@ -65,6 +67,8 @@ public class AppModule {
|
|||||||
NetworkUsageLogger networkUsageLogger;
|
NetworkUsageLogger networkUsageLogger;
|
||||||
@Inject
|
@Inject
|
||||||
DozeWatchdog dozeWatchdog;
|
DozeWatchdog dozeWatchdog;
|
||||||
|
@Inject
|
||||||
|
RecentEmoji recentEmoji;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Application application;
|
private final Application application;
|
||||||
@@ -211,4 +215,11 @@ public class AppModule {
|
|||||||
return lockManager;
|
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.os.StrictMode.VmPolicy;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
|
|
||||||
|
import com.vanniktech.emoji.EmojiManager;
|
||||||
|
import com.vanniktech.emoji.google.GoogleEmojiProvider;
|
||||||
|
|
||||||
import org.acra.ACRA;
|
import org.acra.ACRA;
|
||||||
import org.acra.ReportingInteractionMode;
|
import org.acra.ReportingInteractionMode;
|
||||||
import org.acra.annotation.ReportsCrashes;
|
import org.acra.annotation.ReportsCrashes;
|
||||||
@@ -110,6 +113,7 @@ public class BriarApplicationImpl extends Application
|
|||||||
LOG.info("Created");
|
LOG.info("Created");
|
||||||
|
|
||||||
applicationComponent = createApplicationComponent();
|
applicationComponent = createApplicationComponent();
|
||||||
|
EmojiManager.install(new GoogleEmojiProvider());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected AndroidComponent createApplicationComponent() {
|
protected AndroidComponent createApplicationComponent() {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -119,6 +119,7 @@ public class ReblogFragment extends BaseFragment implements TextInputListener {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSendClick(String text) {
|
public void onSendClick(String text) {
|
||||||
|
ui.input.hideSoftKeyboard();
|
||||||
String comment = getComment();
|
String comment = getComment();
|
||||||
feedController.repeatPost(item, comment,
|
feedController.repeatPost(item, comment,
|
||||||
new UiExceptionHandler<DbException>(this) {
|
new UiExceptionHandler<DbException>(this) {
|
||||||
|
|||||||
@@ -134,6 +134,7 @@ public class WriteBlogPostActivity extends BriarActivity
|
|||||||
@Override
|
@Override
|
||||||
public void onSendClick(String body) {
|
public void onSendClick(String body) {
|
||||||
// hide publish button, show progress bar
|
// hide publish button, show progress bar
|
||||||
|
input.hideSoftKeyboard();
|
||||||
input.setVisibility(GONE);
|
input.setVisibility(GONE);
|
||||||
progressBar.setVisibility(VISIBLE);
|
progressBar.setVisibility(VISIBLE);
|
||||||
|
|
||||||
|
|||||||
@@ -202,7 +202,6 @@ public class GroupActivity extends
|
|||||||
if (!enabled) {
|
if (!enabled) {
|
||||||
textInput.setVisibility(GONE);
|
textInput.setVisibility(GONE);
|
||||||
if (textInput.isKeyboardOpen()) textInput.hideSoftKeyboard();
|
if (textInput.isKeyboardOpen()) textInput.hideSoftKeyboard();
|
||||||
if (textInput.isEmojiDrawerOpen()) textInput.hideEmojiDrawer();
|
|
||||||
} else {
|
} else {
|
||||||
textInput.setVisibility(VISIBLE);
|
textInput.setVisibility(VISIBLE);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import android.view.LayoutInflater;
|
|||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
@@ -15,6 +17,8 @@ import javax.annotation.Nullable;
|
|||||||
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
|
@MethodsNotNullByDefault
|
||||||
|
@ParametersNotNullByDefault
|
||||||
public class LargeTextInputView extends TextInputView {
|
public class LargeTextInputView extends TextInputView {
|
||||||
|
|
||||||
public LargeTextInputView(Context context) {
|
public LargeTextInputView(Context context) {
|
||||||
@@ -46,30 +50,28 @@ public class LargeTextInputView extends TextInputView {
|
|||||||
R.styleable.LargeTextInputView);
|
R.styleable.LargeTextInputView);
|
||||||
String buttonText =
|
String buttonText =
|
||||||
attributes.getString(R.styleable.LargeTextInputView_buttonText);
|
attributes.getString(R.styleable.LargeTextInputView_buttonText);
|
||||||
int maxLines =
|
int maxLines = attributes
|
||||||
attributes
|
.getInteger(R.styleable.LargeTextInputView_maxLines, 0);
|
||||||
.getInteger(R.styleable.LargeTextInputView_maxLines, 0);
|
|
||||||
boolean fillHeight = attributes
|
boolean fillHeight = attributes
|
||||||
.getBoolean(R.styleable.LargeTextInputView_fillHeight,
|
.getBoolean(R.styleable.LargeTextInputView_fillHeight, false);
|
||||||
false);
|
|
||||||
attributes.recycle();
|
attributes.recycle();
|
||||||
|
|
||||||
if (buttonText != null) setButtonText(buttonText);
|
if (buttonText != null) setButtonText(buttonText);
|
||||||
if (maxLines > 0) ui.editText.setMaxLines(maxLines);
|
if (maxLines > 0) editText.setMaxLines(maxLines);
|
||||||
if (fillHeight) {
|
if (fillHeight) {
|
||||||
ViewGroup layout = findViewById(R.id.input_layout);
|
ViewGroup layout = findViewById(R.id.input_layout);
|
||||||
LayoutParams params = (LayoutParams) layout.getLayoutParams();
|
LayoutParams params = (LayoutParams) layout.getLayoutParams();
|
||||||
params.height = 0;
|
params.height = 0;
|
||||||
params.weight = 1;
|
params.weight = 1;
|
||||||
layout.setLayoutParams(params);
|
layout.setLayoutParams(params);
|
||||||
ViewGroup.LayoutParams editParams = ui.editText.getLayoutParams();
|
ViewGroup.LayoutParams editParams = editText.getLayoutParams();
|
||||||
editParams.height = MATCH_PARENT;
|
editParams.height = MATCH_PARENT;
|
||||||
ui.editText.setLayoutParams(editParams);
|
editText.setLayoutParams(editParams);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setButtonText(String text) {
|
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.CallSuper;
|
||||||
import android.support.annotation.StringRes;
|
import android.support.annotation.StringRes;
|
||||||
import android.support.annotation.UiThread;
|
import android.support.annotation.UiThread;
|
||||||
|
import android.support.v7.widget.AppCompatImageButton;
|
||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
import android.text.TextWatcher;
|
import android.text.TextWatcher;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.view.KeyEvent;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.inputmethod.InputMethodManager;
|
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.R;
|
||||||
|
import org.briarproject.briar.android.BriarApplication;
|
||||||
import org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout;
|
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.annotation.Nullable;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static android.content.Context.INPUT_METHOD_SERVICE;
|
import static android.content.Context.INPUT_METHOD_SERVICE;
|
||||||
import static android.content.Context.LAYOUT_INFLATER_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.KeyEvent.KEYCODE_ENTER;
|
||||||
import static android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT;
|
import static android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT;
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
public class TextInputView extends KeyboardAwareLinearLayout
|
@MethodsNotNullByDefault
|
||||||
implements EmojiEventListener {
|
@ParametersNotNullByDefault
|
||||||
|
public class TextInputView extends KeyboardAwareLinearLayout {
|
||||||
|
|
||||||
protected final ViewHolder ui;
|
@Inject
|
||||||
protected TextInputListener listener;
|
RecentEmoji recentEmoji;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
TextInputListener listener;
|
||||||
|
|
||||||
|
AppCompatImageButton emojiToggle;
|
||||||
|
EmojiEditText editText;
|
||||||
|
EmojiPopup emojiPopup;
|
||||||
|
View sendButton;
|
||||||
|
|
||||||
public TextInputView(Context context) {
|
public TextInputView(Context context) {
|
||||||
this(context, null);
|
this(context, null);
|
||||||
@@ -49,11 +61,12 @@ public class TextInputView extends KeyboardAwareLinearLayout
|
|||||||
public TextInputView(Context context, @Nullable AttributeSet attrs,
|
public TextInputView(Context context, @Nullable AttributeSet attrs,
|
||||||
int defStyleAttr) {
|
int defStyleAttr) {
|
||||||
super(context, attrs, defStyleAttr);
|
super(context, attrs, defStyleAttr);
|
||||||
|
BriarApplication app =
|
||||||
|
(BriarApplication) context.getApplicationContext();
|
||||||
|
app.getApplicationComponent().inject(this);
|
||||||
setOrientation(VERTICAL);
|
setOrientation(VERTICAL);
|
||||||
setLayoutTransition(new LayoutTransition());
|
setLayoutTransition(new LayoutTransition());
|
||||||
|
|
||||||
inflateLayout(context);
|
inflateLayout(context);
|
||||||
ui = new ViewHolder();
|
|
||||||
setUpViews(context, attrs);
|
setUpViews(context, attrs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,89 +78,79 @@ public class TextInputView extends KeyboardAwareLinearLayout
|
|||||||
|
|
||||||
@CallSuper
|
@CallSuper
|
||||||
protected void setUpViews(Context context, @Nullable AttributeSet attrs) {
|
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
|
// get attributes
|
||||||
TypedArray attributes = context.obtainStyledAttributes(attrs,
|
TypedArray attributes = context.obtainStyledAttributes(attrs,
|
||||||
R.styleable.TextInputView);
|
R.styleable.TextInputView);
|
||||||
String hint = attributes.getString(R.styleable.TextInputView_hint);
|
String hint = attributes.getString(R.styleable.TextInputView_hint);
|
||||||
attributes.recycle();
|
attributes.recycle();
|
||||||
|
|
||||||
if (hint != null) {
|
if (hint != null) editText.setHint(hint);
|
||||||
ui.editText.setHint(hint);
|
|
||||||
}
|
|
||||||
|
|
||||||
ui.emojiToggle.attach(ui.emojiDrawer);
|
emojiToggle.setOnClickListener(v -> emojiPopup.toggle());
|
||||||
ui.emojiToggle.setOnClickListener(v -> onEmojiToggleClicked());
|
editText.setOnClickListener(v -> showSoftKeyboard());
|
||||||
ui.editText.setOnClickListener(v -> showSoftKeyboard());
|
editText.setOnKeyListener((v, keyCode, event) -> {
|
||||||
ui.editText.setOnKeyListener((v, keyCode, event) -> {
|
|
||||||
if (keyCode == KEYCODE_BACK && isEmojiDrawerOpen()) {
|
|
||||||
hideEmojiDrawer();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (keyCode == KEYCODE_ENTER && event.isCtrlPressed()) {
|
if (keyCode == KEYCODE_ENTER && event.isCtrlPressed()) {
|
||||||
trySendMessage();
|
trySendMessage();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
ui.sendButton.setOnClickListener(v -> trySendMessage());
|
sendButton.setOnClickListener(v -> trySendMessage());
|
||||||
ui.emojiDrawer.setEmojiEventListener(this);
|
}
|
||||||
|
|
||||||
|
private void showEmojiIcon() {
|
||||||
|
emojiToggle.setImageResource(R.drawable.ic_emoji_toggle);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showKeyboardIcon() {
|
||||||
|
emojiToggle.setImageResource(R.drawable.ic_keyboard);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void trySendMessage() {
|
private void trySendMessage() {
|
||||||
if (listener != null) {
|
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
|
@Override
|
||||||
public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
|
public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
|
||||||
return ui.editText.requestFocus(direction, previouslyFocusedRect);
|
return editText.requestFocus(direction, previouslyFocusedRect);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onEmojiToggleClicked() {
|
@Override
|
||||||
if (isEmojiDrawerOpen()) {
|
public void onDetachedFromWindow() {
|
||||||
showSoftKeyboard();
|
super.onDetachedFromWindow();
|
||||||
} else {
|
if (emojiPopup.isShowing()) emojiPopup.dismiss();
|
||||||
showEmojiDrawer();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setText(String text) {
|
public void setText(String text) {
|
||||||
ui.editText.setText(text);
|
editText.setText(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Editable getText() {
|
public Editable getText() {
|
||||||
return ui.editText.getText();
|
return editText.getText();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setHint(@StringRes int res) {
|
public void setHint(@StringRes int res) {
|
||||||
ui.editText.setHint(res);
|
editText.setHint(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSendButtonEnabled(boolean enabled) {
|
public void setSendButtonEnabled(boolean enabled) {
|
||||||
ui.sendButton.setEnabled(enabled);
|
sendButton.setEnabled(enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addTextChangedListener(TextWatcher watcher) {
|
public void addTextChangedListener(TextWatcher watcher) {
|
||||||
ui.editText.addTextChangedListener(watcher);
|
editText.addTextChangedListener(watcher);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setListener(TextInputListener listener) {
|
public void setListener(TextInputListener listener) {
|
||||||
@@ -155,58 +158,17 @@ public class TextInputView extends KeyboardAwareLinearLayout
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void showSoftKeyboard() {
|
public void showSoftKeyboard() {
|
||||||
if (isKeyboardOpen()) return;
|
Object o = getContext().getSystemService(INPUT_METHOD_SERVICE);
|
||||||
|
((InputMethodManager) o).showSoftInput(editText, SHOW_IMPLICIT);
|
||||||
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);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void hideSoftKeyboard() {
|
public void hideSoftKeyboard() {
|
||||||
IBinder token = ui.editText.getWindowToken();
|
if (emojiPopup.isShowing()) emojiPopup.dismiss();
|
||||||
|
IBinder token = editText.getWindowToken();
|
||||||
Object o = getContext().getSystemService(INPUT_METHOD_SERVICE);
|
Object o = getContext().getSystemService(INPUT_METHOD_SERVICE);
|
||||||
((InputMethodManager) o).hideSoftInputFromWindow(token, 0);
|
((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 {
|
public interface TextInputListener {
|
||||||
void onSendClick(String text);
|
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:viewportHeight="24"
|
|
||||||
android:viewportWidth="24">
|
|
||||||
<path
|
|
||||||
android:fillColor="#000000"
|
|
||||||
android:pathData="M7.5,7.5C9.17,5.87 11.29,4.69 13.37,4.18C15.46,3.67 17.5,3.83 18.6,4C19.71,4.15 19.87,4.31 20.03,5.41C20.18,6.5 20.33,8.55 19.82,10.63C19.31,12.71 18.13,14.83 16.5,16.5C14.83,18.13 12.71,19.31 10.63,19.82C8.55,20.33 6.5,20.18 5.41,20.03C4.31,19.87 4.15,19.71 4,18.6C3.83,17.5 3.67,15.46 4.18,13.37C4.69,11.29 5.87,9.17 7.5,7.5M7.3,15.79L8.21,16.7L9.42,15.5L10.63,16.7L11.54,15.79L10.34,14.58L12,12.91L13.21,14.12L14.12,13.21L12.91,12L14.58,10.34L15.79,11.54L16.7,10.63L15.5,9.42L16.7,8.21L15.79,7.3L14.58,8.5L13.37,7.3L12.46,8.21L13.66,9.42L12,11.09L10.79,9.88L9.88,10.79L11.09,12L9.42,13.66L8.21,12.46L7.3,13.37L8.5,14.58L7.3,15.79Z"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:viewportHeight="24.0"
|
|
||||||
android:viewportWidth="24.0">
|
|
||||||
<path
|
|
||||||
android:fillColor="#000000"
|
|
||||||
android:pathData="M18.7,12.4c-0.28,-0.16 -0.57,-0.29 -0.86,-0.4 0.29,-0.11 0.58,-0.24 0.86,-0.4 1.92,-1.11 2.99,-3.12 3,-5.19 -1.79,-1.03 -4.07,-1.11 -6,0 -0.28,0.16 -0.54,0.35 -0.78,0.54 0.05,-0.31 0.08,-0.63 0.08,-0.95 0,-2.22 -1.21,-4.15 -3,-5.19C10.21,1.85 9,3.78 9,6c0,0.32 0.03,0.64 0.08,0.95 -0.24,-0.2 -0.5,-0.39 -0.78,-0.55 -1.92,-1.11 -4.2,-1.03 -6,0 0,2.07 1.07,4.08 3,5.19 0.28,0.16 0.57,0.29 0.86,0.4 -0.29,0.11 -0.58,0.24 -0.86,0.4 -1.92,1.11 -2.99,3.12 -3,5.19 1.79,1.03 4.07,1.11 6,0 0.28,-0.16 0.54,-0.35 0.78,-0.54 -0.05,0.32 -0.08,0.64 -0.08,0.96 0,2.22 1.21,4.15 3,5.19 1.79,-1.04 3,-2.97 3,-5.19 0,-0.32 -0.03,-0.64 -0.08,-0.95 0.24,0.2 0.5,0.38 0.78,0.54 1.92,1.11 4.2,1.03 6,0 -0.01,-2.07 -1.08,-4.08 -3,-5.19zM12,16c-2.21,0 -4,-1.79 -4,-4s1.79,-4 4,-4 4,1.79 4,4 -1.79,4 -4,4z"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:viewportHeight="24"
|
|
||||||
android:viewportWidth="24">
|
|
||||||
|
|
||||||
<path
|
|
||||||
android:fillColor="#000000"
|
|
||||||
android:pathData="M15.4839,3.8557 C14.8224,3.89611,14.9476,5.06823,15.4034,5.47978
|
|
||||||
C16.7652,6.93176,17.1508,8.98786,17.299,10.9051
|
|
||||||
C17.4081,13.1214,17.2144,15.4608,16.1275,17.4387
|
|
||||||
C15.7657,18.1699,14.7879,18.7118,14.9747,19.6345
|
|
||||||
C15.2618,20.6719,16.1617,19.8774,16.5955,19.4508
|
|
||||||
C18.5872,17.7088,19.4632,15.0228,19.5268,12.4373
|
|
||||||
C19.5469,10.5193,19.295,8.516,18.3141,6.8329
|
|
||||||
C17.6499,5.74222,16.9234,4.59896,15.8167,3.91355
|
|
||||||
C15.6887,3.86763,15.5784,3.84987,15.4839,3.85564 Z M5.91182,7.65831
|
|
||||||
A1.3631614,1.3631614,0,0,0,4.54866,9.02147
|
|
||||||
A1.3631614,1.3631614,0,0,0,5.91182,10.3846
|
|
||||||
A1.3631614,1.3631614,0,0,0,7.27498,9.02147
|
|
||||||
A1.3631614,1.3631614,0,0,0,5.91182,7.65831 Z M8.98492,10.6595 L8.98492,12.7042
|
|
||||||
L13.756,12.7042 L13.756,10.6595 L8.98494,10.6595 Z M5.83527,14.1306
|
|
||||||
A1.3631614,1.3631614,0,0,0,4.47211,15.4938
|
|
||||||
A1.3631614,1.3631614,0,0,0,5.83527,16.857
|
|
||||||
A1.3631614,1.3631614,0,0,0,7.19843,15.4938
|
|
||||||
A1.3631614,1.3631614,0,0,0,5.83527,14.1306 Z"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:viewportHeight="24.0"
|
|
||||||
android:viewportWidth="24.0">
|
|
||||||
<path
|
|
||||||
android:fillColor="#000000"
|
|
||||||
android:pathData="M14.4,6L14,4H5v17h2v-7h5.6l0.4,2h7V6z"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:viewportHeight="24.0"
|
|
||||||
android:viewportWidth="24.0">
|
|
||||||
<path
|
|
||||||
android:fillColor="#000000"
|
|
||||||
android:pathData="M12,6c1.11,0 2,-0.9 2,-2 0,-0.38 -0.1,-0.73 -0.29,-1.03L12,0l-1.71,2.97c-0.19,0.3 -0.29,0.65 -0.29,1.03 0,1.1 0.9,2 2,2zM16.6,15.99l-1.07,-1.07 -1.08,1.07c-1.3,1.3 -3.58,1.31 -4.89,0l-1.07,-1.07 -1.09,1.07C6.75,16.64 5.88,17 4.96,17c-0.73,0 -1.4,-0.23 -1.96,-0.61L3,21c0,0.55 0.45,1 1,1h16c0.55,0 1,-0.45 1,-1v-4.61c-0.56,0.38 -1.23,0.61 -1.96,0.61 -0.92,0 -1.79,-0.36 -2.44,-1.01zM18,9h-5L13,7h-2v2L6,9c-1.66,0 -3,1.34 -3,3v1.54c0,1.08 0.88,1.96 1.96,1.96 0.52,0 1.02,-0.2 1.38,-0.57l2.14,-2.13 2.13,2.13c0.74,0.74 2.03,0.74 2.77,0l2.14,-2.13 2.13,2.13c0.37,0.37 0.86,0.57 1.38,0.57 1.08,0 1.96,-0.88 1.96,-1.96L20.99,12C21,10.34 19.66,9 18,9z"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:viewportHeight="24"
|
|
||||||
android:viewportWidth="24">
|
|
||||||
<path
|
|
||||||
android:fillColor="#000000"
|
|
||||||
android:pathData="M5,16L3,5L8.5,12L12,5L15.5,12L21,5L19,16H5M19,19A1,1 0 0,1 18,20H6A1,1 0 0,1 5,19V18H19V19Z"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:viewportHeight="24.0"
|
|
||||||
android:viewportWidth="24.0">
|
|
||||||
<path
|
|
||||||
android:fillColor="#000000"
|
|
||||||
android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8zM12.5,7H11v6l5.25,3.15 0.75,-1.23 -4.5,-2.67z"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:viewportHeight="24.0"
|
|
||||||
android:viewportWidth="24.0">
|
|
||||||
<path
|
|
||||||
android:fillColor="#000000"
|
|
||||||
android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8zM15.5,11c0.83,0 1.5,-0.67 1.5,-1.5S16.33,8 15.5,8 14,8.67 14,9.5s0.67,1.5 1.5,1.5zM8.5,11c0.83,0 1.5,-0.67 1.5,-1.5S9.33,8 8.5,8 7,8.67 7,9.5 7.67,11 8.5,11zM12,17.5c2.33,0 4.31,-1.46 5.11,-3.5L6.89,14c0.8,2.04 2.78,3.5 5.11,3.5z"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:viewportHeight="24.0"
|
|
||||||
android:viewportWidth="24.0">
|
|
||||||
<path
|
|
||||||
android:fillColor="#000000"
|
|
||||||
android:pathData="M12,7.77L18.39,18H5.61L12,7.77M12,4L2,20h20L12,4z"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:viewportHeight="24.0"
|
|
||||||
android:viewportWidth="24.0">
|
|
||||||
<path
|
|
||||||
android:fillColor="#000000"
|
|
||||||
android:pathData="M18.92,6.01C18.72,5.42 18.16,5 17.5,5h-11c-0.66,0 -1.21,0.42 -1.42,1.01L3,12v8c0,0.55 0.45,1 1,1h1c0.55,0 1,-0.45 1,-1v-1h12v1c0,0.55 0.45,1 1,1h1c0.55,0 1,-0.45 1,-1v-8l-2.08,-5.99zM6.5,16c-0.83,0 -1.5,-0.67 -1.5,-1.5S5.67,13 6.5,13s1.5,0.67 1.5,1.5S7.33,16 6.5,16zM17.5,16c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5zM5,11l1.5,-4.5h11L19,11L5,11z"/>
|
|
||||||
</vector>
|
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
|
|
||||||
<include layout="@layout/contact_avatar_status"/>
|
<include layout="@layout/contact_avatar_status"/>
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
<com.vanniktech.emoji.EmojiTextView
|
||||||
android:id="@+id/contactName"
|
android:id="@+id/contactName"
|
||||||
style="@style/TextAppearance.AppCompat.Widget.ActionBar.Title.Inverse"
|
style="@style/TextAppearance.AppCompat.Widget.ActionBar.Title.Inverse"
|
||||||
android:textColor="@color/action_bar_text"
|
android:textColor="@color/action_bar_text"
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
android:visibility="invisible"
|
android:visibility="invisible"
|
||||||
tools:ignore="ContentDescription"/>
|
tools:ignore="ContentDescription"/>
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
<com.vanniktech.emoji.EmojiTextView
|
||||||
android:id="@+id/authorName"
|
android:id="@+id/authorName"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="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"
|
android:layout_marginStart="@dimen/listitem_horizontal_margin"
|
||||||
tools:src="@mipmap/ic_launcher_round"/>
|
tools:src="@mipmap/ic_launcher_round"/>
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
<com.vanniktech.emoji.EmojiTextView
|
||||||
android:id="@+id/nameContact1"
|
android:id="@+id/nameContact1"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@@ -73,7 +73,7 @@
|
|||||||
android:layout_marginStart="@dimen/listitem_horizontal_margin"
|
android:layout_marginStart="@dimen/listitem_horizontal_margin"
|
||||||
tools:src="@mipmap/ic_launcher_round"/>
|
tools:src="@mipmap/ic_launcher_round"/>
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
<com.vanniktech.emoji.EmojiTextView
|
||||||
android:id="@+id/nameContact2"
|
android:id="@+id/nameContact2"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
android:padding="@dimen/listitem_vertical_margin"
|
android:padding="@dimen/listitem_vertical_margin"
|
||||||
app:persona="commenter"/>
|
app:persona="commenter"/>
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
<com.vanniktech.emoji.EmojiTextView
|
||||||
android:id="@+id/bodyView"
|
android:id="@+id/bodyView"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|||||||
@@ -50,7 +50,7 @@
|
|||||||
android:src="@drawable/ic_repeat"
|
android:src="@drawable/ic_repeat"
|
||||||
app:tint="?attr/colorControlNormal"/>
|
app:tint="?attr/colorControlNormal"/>
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
<com.vanniktech.emoji.EmojiTextView
|
||||||
android:id="@+id/bodyView"
|
android:id="@+id/bodyView"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|||||||
@@ -59,7 +59,7 @@
|
|||||||
android:layout_toRightOf="@+id/avatarFrameView"
|
android:layout_toRightOf="@+id/avatarFrameView"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
<com.vanniktech.emoji.EmojiTextView
|
||||||
android:id="@+id/nameView"
|
android:id="@+id/nameView"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
android:layout_marginStart="@dimen/listitem_horizontal_margin"
|
android:layout_marginStart="@dimen/listitem_horizontal_margin"
|
||||||
tools:src="@mipmap/ic_launcher_round"/>
|
tools:src="@mipmap/ic_launcher_round"/>
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
<com.vanniktech.emoji.EmojiTextView
|
||||||
android:id="@+id/nameView"
|
android:id="@+id/nameView"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
android:elevation="@dimen/message_bubble_elevation"
|
android:elevation="@dimen/message_bubble_elevation"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
<com.vanniktech.emoji.EmojiTextView
|
||||||
android:id="@+id/text"
|
android:id="@+id/text"
|
||||||
style="@style/TextMessage"
|
style="@style/TextMessage"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
android:background="@drawable/msg_out"
|
android:background="@drawable/msg_out"
|
||||||
android:elevation="@dimen/message_bubble_elevation">
|
android:elevation="@dimen/message_bubble_elevation">
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
<com.vanniktech.emoji.EmojiTextView
|
||||||
android:id="@+id/text"
|
android:id="@+id/text"
|
||||||
style="@style/TextMessage"
|
style="@style/TextMessage"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
android:layout_marginTop="@dimen/message_bubble_margin"
|
android:layout_marginTop="@dimen/message_bubble_margin"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
<com.vanniktech.emoji.EmojiTextView
|
||||||
android:id="@+id/msgText"
|
android:id="@+id/msgText"
|
||||||
style="@style/TextMessage"
|
style="@style/TextMessage"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
android:background="@drawable/notice_in_bottom"
|
android:background="@drawable/notice_in_bottom"
|
||||||
android:elevation="@dimen/message_bubble_elevation">
|
android:elevation="@dimen/message_bubble_elevation">
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
<com.vanniktech.emoji.EmojiTextView
|
||||||
android:id="@+id/text"
|
android:id="@+id/text"
|
||||||
style="@style/TextMessage.Notice"
|
style="@style/TextMessage.Notice"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
android:layout_marginTop="@dimen/message_bubble_margin"
|
android:layout_marginTop="@dimen/message_bubble_margin"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
<com.vanniktech.emoji.EmojiTextView
|
||||||
android:id="@+id/msgText"
|
android:id="@+id/msgText"
|
||||||
style="@style/TextMessage"
|
style="@style/TextMessage"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
android:background="@drawable/notice_out_bottom"
|
android:background="@drawable/notice_out_bottom"
|
||||||
android:elevation="@dimen/message_bubble_elevation">
|
android:elevation="@dimen/message_bubble_elevation">
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
<com.vanniktech.emoji.EmojiTextView
|
||||||
android:id="@+id/text"
|
android:id="@+id/text"
|
||||||
style="@style/TextMessage.Notice"
|
style="@style/TextMessage.Notice"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
android:layout_marginTop="@dimen/message_bubble_margin"
|
android:layout_marginTop="@dimen/message_bubble_margin"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
<com.vanniktech.emoji.EmojiTextView
|
||||||
android:id="@+id/msgText"
|
android:id="@+id/msgText"
|
||||||
style="@style/TextMessage"
|
style="@style/TextMessage"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
android:background="@drawable/notice_in_bottom"
|
android:background="@drawable/notice_in_bottom"
|
||||||
android:elevation="@dimen/message_bubble_elevation">
|
android:elevation="@dimen/message_bubble_elevation">
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
<com.vanniktech.emoji.EmojiTextView
|
||||||
android:id="@+id/text"
|
android:id="@+id/text"
|
||||||
style="@style/TextMessage.Notice"
|
style="@style/TextMessage.Notice"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
android:layout_marginRight="@dimen/listitem_horizontal_margin"
|
android:layout_marginRight="@dimen/listitem_horizontal_margin"
|
||||||
android:layout_marginTop="@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:id="@+id/forumNameView"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
android:layout_marginRight="@dimen/listitem_horizontal_margin"
|
android:layout_marginRight="@dimen/listitem_horizontal_margin"
|
||||||
android:layout_marginTop="@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:id="@+id/nameView"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
android:textSize="@dimen/text_size_medium"
|
android:textSize="@dimen/text_size_medium"
|
||||||
tools:text="This is a name of a Private Group"/>
|
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:id="@+id/creatorView"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
android:baselineAligned="false"
|
android:baselineAligned="false"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
<com.vanniktech.emoji.EmojiTextView
|
||||||
android:id="@+id/text"
|
android:id="@+id/text"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
android:layout_alignParentStart="true"
|
android:layout_alignParentStart="true"
|
||||||
android:layout_marginRight="@dimen/listitem_horizontal_margin"/>
|
android:layout_marginRight="@dimen/listitem_horizontal_margin"/>
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
<com.vanniktech.emoji.EmojiTextView
|
||||||
android:id="@+id/forumNameView"
|
android:id="@+id/forumNameView"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
android:textSize="@dimen/text_size_medium"
|
android:textSize="@dimen/text_size_medium"
|
||||||
tools:text="This is a name of a forum that is available"/>
|
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:id="@+id/sharedByView"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
android:layout_marginRight="@dimen/listitem_horizontal_margin"
|
android:layout_marginRight="@dimen/listitem_horizontal_margin"
|
||||||
tools:src="@mipmap/ic_launcher_round"/>
|
tools:src="@mipmap/ic_launcher_round"/>
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
<com.vanniktech.emoji.EmojiTextView
|
||||||
android:id="@+id/nameView"
|
android:id="@+id/nameView"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
android:layout_marginTop="@dimen/listitem_horizontal_margin"
|
android:layout_marginTop="@dimen/listitem_horizontal_margin"
|
||||||
android:background="?attr/selectableItemBackground">
|
android:background="?attr/selectableItemBackground">
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
<com.vanniktech.emoji.EmojiTextView
|
||||||
android:id="@+id/titleView"
|
android:id="@+id/titleView"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@@ -42,7 +42,7 @@
|
|||||||
android:textColor="?android:attr/textColorSecondary"
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
android:textSize="@dimen/text_size_small"/>
|
android:textSize="@dimen/text_size_small"/>
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
<com.vanniktech.emoji.EmojiTextView
|
||||||
android:id="@+id/authorView"
|
android:id="@+id/authorView"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@@ -101,7 +101,7 @@
|
|||||||
android:textSize="@dimen/text_size_small"
|
android:textSize="@dimen/text_size_small"
|
||||||
tools:text="5 min. ago"/>
|
tools:text="5 min. ago"/>
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
<com.vanniktech.emoji.EmojiTextView
|
||||||
android:id="@+id/descriptionView"
|
android:id="@+id/descriptionView"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
android:layout_toRightOf="@+id/avatarView"
|
android:layout_toRightOf="@+id/avatarView"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
<com.vanniktech.emoji.EmojiTextView
|
||||||
android:id="@+id/nameView"
|
android:id="@+id/nameView"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|||||||
@@ -71,7 +71,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1">
|
android:layout_weight="1">
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
<com.vanniktech.emoji.EmojiTextView
|
||||||
android:id="@+id/text"
|
android:id="@+id/text"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|||||||
@@ -16,17 +16,18 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="@color/card_background">
|
android:background="@color/card_background">
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.emoji.EmojiToggle
|
<android.support.v7.widget.AppCompatImageButton
|
||||||
android:id="@+id/emoji_toggle"
|
android:id="@+id/emoji_toggle"
|
||||||
android:layout_width="@dimen/text_input_height"
|
android:layout_width="@dimen/text_input_height"
|
||||||
android:layout_height="@dimen/text_input_height"
|
android:layout_height="@dimen/text_input_height"
|
||||||
android:layout_gravity="bottom"
|
android:layout_gravity="bottom"
|
||||||
android:background="?attr/selectableItemBackground"
|
android:background="?attr/selectableItemBackground"
|
||||||
|
android:src="@drawable/ic_emoji_toggle"
|
||||||
android:padding="@dimen/margin_small"
|
android:padding="@dimen/margin_small"
|
||||||
android:scaleType="center"
|
android:scaleType="center"
|
||||||
app:tint="?attr/colorControlNormal"/>
|
app:tint="?attr/colorControlNormal"/>
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.emoji.EmojiEditText
|
<com.vanniktech.emoji.EmojiEditText
|
||||||
android:id="@+id/input_text"
|
android:id="@+id/input_text"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@@ -56,11 +57,4 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</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>
|
</merge>
|
||||||
|
|||||||
@@ -27,17 +27,18 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal">
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.emoji.EmojiToggle
|
<android.support.v7.widget.AppCompatImageButton
|
||||||
android:id="@+id/emoji_toggle"
|
android:id="@+id/emoji_toggle"
|
||||||
android:layout_width="@dimen/text_input_height"
|
android:layout_width="@dimen/text_input_height"
|
||||||
android:layout_height="@dimen/text_input_height"
|
android:layout_height="@dimen/text_input_height"
|
||||||
android:layout_gravity="bottom"
|
android:layout_gravity="bottom"
|
||||||
android:background="?attr/selectableItemBackground"
|
android:background="?attr/selectableItemBackground"
|
||||||
|
android:src="@drawable/ic_emoji_toggle"
|
||||||
android:padding="@dimen/margin_small"
|
android:padding="@dimen/margin_small"
|
||||||
android:scaleType="center"
|
android:scaleType="center"
|
||||||
app:tint="?attr/colorControlNormal"/>
|
app:tint="?attr/colorControlNormal"/>
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.emoji.EmojiEditText
|
<com.vanniktech.emoji.EmojiEditText
|
||||||
android:id="@+id/input_text"
|
android:id="@+id/input_text"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@@ -64,10 +65,4 @@
|
|||||||
android:layout_marginRight="@dimen/margin_small"
|
android:layout_marginRight="@dimen/margin_small"
|
||||||
android:layout_marginStart="@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>
|
</merge>
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ import android.app.Application;
|
|||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
|
|
||||||
|
import com.vanniktech.emoji.EmojiManager;
|
||||||
|
import com.vanniktech.emoji.google.GoogleEmojiProvider;
|
||||||
|
|
||||||
import org.briarproject.bramble.BrambleCoreModule;
|
import org.briarproject.bramble.BrambleCoreModule;
|
||||||
import org.briarproject.briar.BriarCoreModule;
|
import org.briarproject.briar.BriarCoreModule;
|
||||||
|
|
||||||
@@ -41,6 +44,7 @@ public class TestBriarApplication extends Application
|
|||||||
BrambleCoreModule.initEagerSingletons(applicationComponent);
|
BrambleCoreModule.initEagerSingletons(applicationComponent);
|
||||||
BriarCoreModule.initEagerSingletons(applicationComponent);
|
BriarCoreModule.initEagerSingletons(applicationComponent);
|
||||||
AndroidEagerSingletons.initEagerSingletons(applicationComponent);
|
AndroidEagerSingletons.initEagerSingletons(applicationComponent);
|
||||||
|
EmojiManager.install(new GoogleEmojiProvider());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -63,7 +63,6 @@ dependencyVerification {
|
|||||||
'com.android.tools:repository:26.1.3:repository-26.1.3.jar:52d4539cc68db91b261e2a33b2c8206b26e05539078758dc28cfb3854adb4f59',
|
'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: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.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.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.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',
|
'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.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.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.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.1.1:javawriter-2.1.1.jar:f699823d0081f69cbb676c1845ea222e0ada79bc88a53e5d22d8bd02d328f57e',
|
||||||
'com.squareup:javawriter:2.5.0:javawriter-2.5.0.jar:fcfb09fb0ea0aa97d3cfe7ea792398081348e468f126b3603cb3803f240197f0',
|
'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.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.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.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.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-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',
|
'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',
|
'de.hdodenhof:circleimageview:2.2.0:circleimageview-2.2.0.aar:bc34761dcd5036229ac1ffed6b5b1bef722ad5b097a2c8bba1c5ed2cd4b5c82b',
|
||||||
|
|||||||