mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-18 21:59:54 +01:00
Emoji: minor bug fixes, code cleanup, logging, visibility,
This commit is contained in:
@@ -10,6 +10,6 @@
|
|||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
app:layout_behavior="org.briarproject.android.util.BriarRecyclerViewBehavior"/>
|
app:layout_behavior="org.briarproject.android.view.BriarRecyclerViewBehavior"/>
|
||||||
|
|
||||||
</android.support.design.widget.CoordinatorLayout>
|
</android.support.design.widget.CoordinatorLayout>
|
||||||
|
|||||||
@@ -1,33 +1,14 @@
|
|||||||
/**
|
|
||||||
* Copyright (C) 2014 Open Whisper Systems
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
package org.thoughtcrime.securesms.components;
|
package org.thoughtcrime.securesms.components;
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Build.VERSION_CODES;
|
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.annotation.UiThread;
|
import android.support.annotation.UiThread;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.util.Log;
|
|
||||||
import android.view.Surface;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
import android.widget.RelativeLayout;
|
import android.widget.RelativeLayout;
|
||||||
@@ -37,15 +18,23 @@ import org.briarproject.R;
|
|||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import static android.content.Context.WINDOW_SERVICE;
|
||||||
|
import static android.view.Surface.ROTATION_270;
|
||||||
|
import static android.view.Surface.ROTATION_90;
|
||||||
|
import static java.util.logging.Level.INFO;
|
||||||
|
import static java.util.logging.Level.WARNING;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* RelativeLayout that, when a view container, will report back when it thinks a soft keyboard
|
* RelativeLayout that, when a view container, will report back when it thinks
|
||||||
* has been opened and what its height would be.
|
* a soft keyboard has been opened and what its height would be.
|
||||||
*/
|
*/
|
||||||
@UiThread
|
@UiThread
|
||||||
public class KeyboardAwareRelativeLayout extends RelativeLayout {
|
public class KeyboardAwareRelativeLayout extends RelativeLayout {
|
||||||
private static final String TAG =
|
|
||||||
KeyboardAwareRelativeLayout.class.getSimpleName();
|
private static final Logger LOG =
|
||||||
|
Logger.getLogger(KeyboardAwareRelativeLayout.class.getName());
|
||||||
|
|
||||||
private final Rect rect = new Rect();
|
private final Rect rect = new Rect();
|
||||||
private final Set<OnKeyboardHiddenListener> hiddenListeners =
|
private final Set<OnKeyboardHiddenListener> hiddenListeners =
|
||||||
@@ -100,7 +89,7 @@ public class KeyboardAwareRelativeLayout extends RelativeLayout {
|
|||||||
int oldRotation = rotation;
|
int oldRotation = rotation;
|
||||||
rotation = getDeviceRotation();
|
rotation = getDeviceRotation();
|
||||||
if (oldRotation != rotation) {
|
if (oldRotation != rotation) {
|
||||||
Log.w(TAG, "rotation changed");
|
LOG.info("Rotation changed");
|
||||||
onKeyboardClose();
|
onKeyboardClose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -111,13 +100,13 @@ public class KeyboardAwareRelativeLayout extends RelativeLayout {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (viewInset == 0 && Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP)
|
if (viewInset == 0 && Build.VERSION.SDK_INT >= 21)
|
||||||
viewInset = getViewInset();
|
viewInset = getViewInset();
|
||||||
final int availableHeight =
|
int availableHeight =
|
||||||
this.getRootView().getHeight() - statusBarHeight - viewInset;
|
getRootView().getHeight() - statusBarHeight - viewInset;
|
||||||
getWindowVisibleDisplayFrame(rect);
|
getWindowVisibleDisplayFrame(rect);
|
||||||
|
|
||||||
final int keyboardHeight = availableHeight - (rect.bottom - rect.top);
|
int keyboardHeight = availableHeight - (rect.bottom - rect.top);
|
||||||
|
|
||||||
if (keyboardHeight > minKeyboardSize) {
|
if (keyboardHeight > minKeyboardSize) {
|
||||||
if (getKeyboardHeight() != keyboardHeight)
|
if (getKeyboardHeight() != keyboardHeight)
|
||||||
@@ -128,7 +117,7 @@ public class KeyboardAwareRelativeLayout extends RelativeLayout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@TargetApi(VERSION_CODES.LOLLIPOP)
|
@TargetApi(21)
|
||||||
private int getViewInset() {
|
private int getViewInset() {
|
||||||
try {
|
try {
|
||||||
Field attachInfoField = View.class.getDeclaredField("mAttachInfo");
|
Field attachInfoField = View.class.getDeclaredField("mAttachInfo");
|
||||||
@@ -141,25 +130,26 @@ public class KeyboardAwareRelativeLayout extends RelativeLayout {
|
|||||||
Rect insets = (Rect) stableInsetsField.get(attachInfo);
|
Rect insets = (Rect) stableInsetsField.get(attachInfo);
|
||||||
return insets.bottom;
|
return insets.bottom;
|
||||||
}
|
}
|
||||||
} catch (NoSuchFieldException nsfe) {
|
} catch (NoSuchFieldException e) {
|
||||||
Log.w(TAG, "field reflection error when measuring view inset",
|
LOG.log(WARNING,
|
||||||
nsfe);
|
"field reflection error when measuring view inset", e);
|
||||||
} catch (IllegalAccessException iae) {
|
} catch (IllegalAccessException e) {
|
||||||
Log.w(TAG, "access reflection error when measuring view inset",
|
LOG.log(WARNING,
|
||||||
iae);
|
"access reflection error when measuring view inset", e);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void onKeyboardOpen(int keyboardHeight) {
|
protected void onKeyboardOpen(int keyboardHeight) {
|
||||||
Log.w(TAG, "onKeyboardOpen(" + keyboardHeight + ")");
|
if (LOG.isLoggable(INFO))
|
||||||
|
LOG.info("onKeyboardOpen(" + keyboardHeight + ")");
|
||||||
keyboardOpen = true;
|
keyboardOpen = true;
|
||||||
|
|
||||||
notifyShownListeners();
|
notifyShownListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void onKeyboardClose() {
|
protected void onKeyboardClose() {
|
||||||
Log.w(TAG, "onKeyboardClose()");
|
LOG.info("onKeyboardClose()");
|
||||||
keyboardOpen = false;
|
keyboardOpen = false;
|
||||||
notifyHiddenListeners();
|
notifyHiddenListeners();
|
||||||
}
|
}
|
||||||
@@ -175,13 +165,12 @@ public class KeyboardAwareRelativeLayout extends RelativeLayout {
|
|||||||
|
|
||||||
public boolean isLandscape() {
|
public boolean isLandscape() {
|
||||||
int rotation = getDeviceRotation();
|
int rotation = getDeviceRotation();
|
||||||
return rotation == Surface.ROTATION_90 ||
|
return rotation == ROTATION_90 || rotation == ROTATION_270;
|
||||||
rotation == Surface.ROTATION_270;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getDeviceRotation() {
|
private int getDeviceRotation() {
|
||||||
WindowManager windowManager = (WindowManager) getContext()
|
WindowManager windowManager =
|
||||||
.getSystemService(Activity.WINDOW_SERVICE);
|
(WindowManager) getContext().getSystemService(WINDOW_SERVICE);
|
||||||
return windowManager.getDefaultDisplay().getRotation();
|
return windowManager.getDefaultDisplay().getRotation();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,10 +179,10 @@ public class KeyboardAwareRelativeLayout extends RelativeLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private int getKeyboardPortraitHeight() {
|
private int getKeyboardPortraitHeight() {
|
||||||
int keyboardHeight =
|
SharedPreferences prefs =
|
||||||
PreferenceManager.getDefaultSharedPreferences(getContext())
|
PreferenceManager.getDefaultSharedPreferences(getContext());
|
||||||
.getInt("keyboard_height_portrait",
|
int keyboardHeight = prefs.getInt("keyboard_height_portrait",
|
||||||
defaultCustomKeyboardSize);
|
defaultCustomKeyboardSize);
|
||||||
return clamp(keyboardHeight, minCustomKeyboardSize,
|
return clamp(keyboardHeight, minCustomKeyboardSize,
|
||||||
getRootView().getHeight() - minCustomKeyboardTopMargin);
|
getRootView().getHeight() - minCustomKeyboardTopMargin);
|
||||||
}
|
}
|
||||||
@@ -203,8 +192,9 @@ public class KeyboardAwareRelativeLayout extends RelativeLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void setKeyboardPortraitHeight(int height) {
|
private void setKeyboardPortraitHeight(int height) {
|
||||||
PreferenceManager.getDefaultSharedPreferences(getContext())
|
SharedPreferences prefs =
|
||||||
.edit().putInt("keyboard_height_portrait", height).apply();
|
PreferenceManager.getDefaultSharedPreferences(getContext());
|
||||||
|
prefs.edit().putInt("keyboard_height_portrait", height).apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void postOnKeyboardClose(final Runnable runnable) {
|
public void postOnKeyboardClose(final Runnable runnable) {
|
||||||
@@ -254,7 +244,8 @@ public class KeyboardAwareRelativeLayout extends RelativeLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void notifyHiddenListeners() {
|
private void notifyHiddenListeners() {
|
||||||
final Set<OnKeyboardHiddenListener> listeners =
|
// Make a copy as listeners may remove themselves when called
|
||||||
|
Set<OnKeyboardHiddenListener> listeners =
|
||||||
new HashSet<>(hiddenListeners);
|
new HashSet<>(hiddenListeners);
|
||||||
for (OnKeyboardHiddenListener listener : listeners) {
|
for (OnKeyboardHiddenListener listener : listeners) {
|
||||||
listener.onKeyboardHidden();
|
listener.onKeyboardHidden();
|
||||||
@@ -262,8 +253,8 @@ public class KeyboardAwareRelativeLayout extends RelativeLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void notifyShownListeners() {
|
private void notifyShownListeners() {
|
||||||
final Set<OnKeyboardShownListener> listeners =
|
// Make a copy as listeners may remove themselves when called
|
||||||
new HashSet<>(shownListeners);
|
Set<OnKeyboardShownListener> listeners = new HashSet<>(shownListeners);
|
||||||
for (OnKeyboardShownListener listener : listeners) {
|
for (OnKeyboardShownListener listener : listeners) {
|
||||||
listener.onKeyboardShown();
|
listener.onKeyboardShown();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,18 @@
|
|||||||
package org.thoughtcrime.securesms.components;
|
package org.thoughtcrime.securesms.components;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Build.VERSION;
|
|
||||||
import android.os.Build.VERSION_CODES;
|
|
||||||
import android.support.annotation.UiThread;
|
import android.support.annotation.UiThread;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.view.HapticFeedbackConstants;
|
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewConfiguration;
|
import android.view.ViewConfiguration;
|
||||||
import android.widget.ImageButton;
|
import android.widget.ImageButton;
|
||||||
|
|
||||||
|
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
|
@UiThread
|
||||||
public class RepeatableImageKey extends ImageButton {
|
public class RepeatableImageKey extends ImageButton {
|
||||||
|
|
||||||
@@ -42,7 +44,7 @@ public class RepeatableImageKey extends ImageButton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void notifyListener() {
|
private void notifyListener() {
|
||||||
if (this.listener != null) this.listener.onKeyEvent();
|
if (listener != null) listener.onKeyEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private class RepeaterClickListener implements OnClickListener {
|
private class RepeaterClickListener implements OnClickListener {
|
||||||
@@ -56,31 +58,28 @@ public class RepeatableImageKey extends ImageButton {
|
|||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
notifyListener();
|
notifyListener();
|
||||||
postDelayed(this, VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB_MR1
|
postDelayed(this, ViewConfiguration.getKeyRepeatDelay());
|
||||||
? ViewConfiguration.getKeyRepeatDelay()
|
|
||||||
: 50);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class RepeaterTouchListener implements OnTouchListener {
|
private class RepeaterTouchListener implements OnTouchListener {
|
||||||
private Repeater repeater;
|
|
||||||
|
private final Repeater repeater;
|
||||||
|
|
||||||
private RepeaterTouchListener() {
|
private RepeaterTouchListener() {
|
||||||
this.repeater = new Repeater();
|
repeater = new Repeater();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onTouch(View view, MotionEvent motionEvent) {
|
public boolean onTouch(View view, MotionEvent motionEvent) {
|
||||||
switch (motionEvent.getAction()) {
|
switch (motionEvent.getAction()) {
|
||||||
case MotionEvent.ACTION_DOWN:
|
case ACTION_DOWN:
|
||||||
view.postDelayed(repeater,
|
view.postDelayed(repeater,
|
||||||
VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB_MR1
|
ViewConfiguration.getKeyRepeatTimeout());
|
||||||
? ViewConfiguration.getKeyRepeatTimeout()
|
performHapticFeedback(KEYBOARD_TAP);
|
||||||
: ViewConfiguration.getLongPressTimeout());
|
|
||||||
performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP);
|
|
||||||
return false;
|
return false;
|
||||||
case MotionEvent.ACTION_CANCEL:
|
case ACTION_CANCEL:
|
||||||
case MotionEvent.ACTION_UP:
|
case ACTION_UP:
|
||||||
view.removeCallbacks(repeater);
|
view.removeCallbacks(repeater);
|
||||||
return false;
|
return false;
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -6,8 +6,9 @@ import android.support.annotation.UiThread;
|
|||||||
import android.text.style.ImageSpan;
|
import android.text.style.ImageSpan;
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
public class AnimatingImageSpan extends ImageSpan {
|
class AnimatingImageSpan extends ImageSpan {
|
||||||
public AnimatingImageSpan(Drawable drawable, Callback callback) {
|
|
||||||
|
AnimatingImageSpan(Drawable drawable, Callback callback) {
|
||||||
super(drawable, ALIGN_BOTTOM);
|
super(drawable, ALIGN_BOTTOM);
|
||||||
drawable.setCallback(callback);
|
drawable.setCallback(callback);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import android.view.LayoutInflater;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.ImageView.ScaleType;
|
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
import com.astuetz.PagerSlidingTabStrip;
|
import com.astuetz.PagerSlidingTabStrip;
|
||||||
@@ -27,15 +26,18 @@ import java.util.LinkedList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
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;
|
import static java.util.logging.Level.INFO;
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
public class EmojiDrawer extends LinearLayout {
|
public class EmojiDrawer extends LinearLayout {
|
||||||
|
|
||||||
private static final String TAG = EmojiDrawer.class.getName();
|
private static final Logger LOG =
|
||||||
private static final Logger LOG = Logger.getLogger(TAG);
|
Logger.getLogger(EmojiDrawer.class.getName());
|
||||||
private static final KeyEvent DELETE_KEY_EVENT =
|
private static final KeyEvent DELETE_KEY_EVENT =
|
||||||
new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL);
|
new KeyEvent(ACTION_DOWN, KEYCODE_DEL);
|
||||||
|
|
||||||
private ViewPager pager;
|
private ViewPager pager;
|
||||||
private List<EmojiPageModel> models;
|
private List<EmojiPageModel> models;
|
||||||
@@ -70,7 +72,6 @@ public class EmojiDrawer extends LinearLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initializeResources(View v) {
|
private void initializeResources(View v) {
|
||||||
LOG.info("initializeResources()");
|
|
||||||
this.pager = (ViewPager) v.findViewById(R.id.emoji_pager);
|
this.pager = (ViewPager) v.findViewById(R.id.emoji_pager);
|
||||||
this.strip = (PagerSlidingTabStrip) v.findViewById(R.id.tabs);
|
this.strip = (PagerSlidingTabStrip) v.findViewById(R.id.tabs);
|
||||||
|
|
||||||
@@ -93,7 +94,7 @@ public class EmojiDrawer extends LinearLayout {
|
|||||||
ViewGroup.LayoutParams params = getLayoutParams();
|
ViewGroup.LayoutParams params = getLayoutParams();
|
||||||
params.height = height;
|
params.height = height;
|
||||||
if (LOG.isLoggable(INFO))
|
if (LOG.isLoggable(INFO))
|
||||||
LOG.info("showing emoji drawer with height " + params.height);
|
LOG.info("Showing emoji drawer with height " + params.height);
|
||||||
setLayoutParams(params);
|
setLayoutParams(params);
|
||||||
setVisibility(VISIBLE);
|
setVisibility(VISIBLE);
|
||||||
if (drawerListener != null) drawerListener.onShown();
|
if (drawerListener != null) drawerListener.onShown();
|
||||||
@@ -102,7 +103,6 @@ public class EmojiDrawer extends LinearLayout {
|
|||||||
public void hide() {
|
public void hide() {
|
||||||
setVisibility(GONE);
|
setVisibility(GONE);
|
||||||
if (drawerListener != null) drawerListener.onHidden();
|
if (drawerListener != null) drawerListener.onHidden();
|
||||||
LOG.info("hide()");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializeEmojiGrid() {
|
private void initializeEmojiGrid() {
|
||||||
@@ -111,7 +111,6 @@ public class EmojiDrawer extends LinearLayout {
|
|||||||
new EmojiSelectionListener() {
|
new EmojiSelectionListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onEmojiSelected(String emoji) {
|
public void onEmojiSelected(String emoji) {
|
||||||
LOG.info("onEmojiSelected()");
|
|
||||||
recentModel.onCodePointSelected(emoji);
|
recentModel.onCodePointSelected(emoji);
|
||||||
if (listener != null) listener.onEmojiSelected(emoji);
|
if (listener != null) listener.onEmojiSelected(emoji);
|
||||||
}
|
}
|
||||||
@@ -174,7 +173,7 @@ public class EmojiDrawer extends LinearLayout {
|
|||||||
@Override
|
@Override
|
||||||
public View getCustomTabView(ViewGroup viewGroup, int i) {
|
public View getCustomTabView(ViewGroup viewGroup, int i) {
|
||||||
ImageView image = new ImageView(context);
|
ImageView image = new ImageView(context);
|
||||||
image.setScaleType(ScaleType.CENTER_INSIDE);
|
image.setScaleType(CENTER_INSIDE);
|
||||||
image.setImageResource(pages.get(i).getIcon());
|
image.setImageResource(pages.get(i).getIcon());
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import android.text.TextUtils;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
public class EmojiFilter implements InputFilter {
|
class EmojiFilter implements InputFilter {
|
||||||
|
|
||||||
private final TextView view;
|
private final TextView view;
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ import android.support.annotation.DrawableRes;
|
|||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
public interface EmojiPageModel {
|
interface EmojiPageModel {
|
||||||
|
|
||||||
@DrawableRes
|
@DrawableRes
|
||||||
int getIcon();
|
int getIcon();
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import org.briarproject.R;
|
|||||||
public class EmojiPageView extends FrameLayout {
|
public class EmojiPageView extends FrameLayout {
|
||||||
|
|
||||||
private final GridView grid;
|
private final GridView grid;
|
||||||
|
|
||||||
private EmojiSelectionListener listener;
|
private EmojiSelectionListener listener;
|
||||||
|
|
||||||
public EmojiPageView(Context context) {
|
public EmojiPageView(Context context) {
|
||||||
@@ -60,15 +61,15 @@ public class EmojiPageView extends FrameLayout {
|
|||||||
|
|
||||||
private static class EmojiGridAdapter extends BaseAdapter {
|
private static class EmojiGridAdapter extends BaseAdapter {
|
||||||
|
|
||||||
protected final Context context;
|
private final Context context;
|
||||||
private final int emojiSize;
|
|
||||||
private final EmojiPageModel model;
|
private final EmojiPageModel model;
|
||||||
|
private final int emojiSize;
|
||||||
|
|
||||||
private EmojiGridAdapter(Context context, EmojiPageModel model) {
|
private EmojiGridAdapter(Context context, EmojiPageModel model) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.emojiSize = (int) context.getResources()
|
|
||||||
.getDimension(R.dimen.emoji_drawer_size);
|
|
||||||
this.model = model;
|
this.model = model;
|
||||||
|
emojiSize = (int) context.getResources()
|
||||||
|
.getDimension(R.dimen.emoji_drawer_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -88,15 +89,14 @@ public class EmojiPageView extends FrameLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View getView(final int position, final View convertView,
|
public View getView(int position, View convertView, ViewGroup parent) {
|
||||||
final ViewGroup parent) {
|
EmojiView view;
|
||||||
final EmojiView view;
|
int pad = context.getResources()
|
||||||
final int pad = context.getResources()
|
|
||||||
.getDimensionPixelSize(R.dimen.emoji_drawer_item_padding);
|
.getDimensionPixelSize(R.dimen.emoji_drawer_item_padding);
|
||||||
if (convertView != null && convertView instanceof EmojiView) {
|
if (convertView != null && convertView instanceof EmojiView) {
|
||||||
view = (EmojiView) convertView;
|
view = (EmojiView) convertView;
|
||||||
} else {
|
} else {
|
||||||
final EmojiView emojiView = new EmojiView(context);
|
EmojiView emojiView = new EmojiView(context);
|
||||||
emojiView.setPadding(pad, pad, pad, pad);
|
emojiView.setPadding(pad, pad, pad, pad);
|
||||||
emojiView.setLayoutParams(
|
emojiView.setLayoutParams(
|
||||||
new AbsListView.LayoutParams(emojiSize + 2 * pad,
|
new AbsListView.LayoutParams(emojiSize + 2 * pad,
|
||||||
@@ -109,7 +109,7 @@ public class EmojiPageView extends FrameLayout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface EmojiSelectionListener {
|
interface EmojiSelectionListener {
|
||||||
void onEmojiSelected(String emoji);
|
void onEmojiSelected(String emoji);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,12 +8,14 @@ import java.util.Arrays;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
class EmojiPages {
|
class EmojiPages {
|
||||||
|
|
||||||
static List<EmojiPageModel> getPages(Context ctx) {
|
static List<EmojiPageModel> getPages(Context ctx) {
|
||||||
return Arrays.<EmojiPageModel>asList(
|
return Arrays.<EmojiPageModel>asList(
|
||||||
new StaticEmojiPageModel(ctx, R.drawable.ic_emoji_smiley_people,
|
new StaticEmojiPageModel(ctx, R.drawable.ic_emoji_smiley_people,
|
||||||
R.array.emoji_smiley_people,
|
R.array.emoji_smiley_people,
|
||||||
"emoji_smiley_people.png"),
|
"emoji_smiley_people.png"),
|
||||||
new StaticEmojiPageModel(ctx, R.drawable.ic_emoji_animals_nature,
|
new StaticEmojiPageModel(ctx,
|
||||||
|
R.drawable.ic_emoji_animals_nature,
|
||||||
R.array.emoji_animals_nature,
|
R.array.emoji_animals_nature,
|
||||||
"emoji_animals_nature.png"),
|
"emoji_animals_nature.png"),
|
||||||
new StaticEmojiPageModel(ctx, R.drawable.ic_emoji_food_drink,
|
new StaticEmojiPageModel(ctx, R.drawable.ic_emoji_food_drink,
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import android.graphics.Bitmap;
|
|||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.ColorFilter;
|
import android.graphics.ColorFilter;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
import android.graphics.PixelFormat;
|
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
@@ -34,25 +33,30 @@ import java.util.regex.Pattern;
|
|||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
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 java.util.logging.Level.WARNING;
|
||||||
|
|
||||||
public class EmojiProvider {
|
public class EmojiProvider {
|
||||||
private static final String TAG = EmojiProvider.class.getSimpleName();
|
|
||||||
private static volatile EmojiProvider instance = null;
|
private static volatile EmojiProvider INSTANCE = null;
|
||||||
private static final Paint paint =
|
|
||||||
|
private static final Paint PAINT =
|
||||||
new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG);
|
new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG);
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
AndroidExecutor androidExecutor;
|
AndroidExecutor androidExecutor;
|
||||||
|
|
||||||
private static final Logger LOG = Logger.getLogger(TAG);
|
private static final Logger LOG =
|
||||||
|
Logger.getLogger(EmojiProvider.class.getName());
|
||||||
|
|
||||||
private final SparseArray<DrawInfo> offsets = new SparseArray<>();
|
private final SparseArray<DrawInfo> offsets = new SparseArray<>();
|
||||||
|
|
||||||
private static final Pattern EMOJI_RANGE = Pattern.compile(
|
private static final Pattern EMOJI_RANGE = Pattern.compile(
|
||||||
// 0x203c,0x2049 0x20a0-0x32ff 0x1f00-0x1fff 0xfe4e5-0xfe4ee
|
// 0x203c,0x2049 0x20a0-0x32ff 0x1f00-0x1fff 0xfe4e5-0xfe4ee
|
||||||
// |=== !!, ?! ===||==== misc ===||========= emoticons =======||========== flags ==========|
|
// |=== !!, ?! ===||==== misc ===||========= emoticons =======||========== flags ==========|
|
||||||
"[\\u203c\\u2049\\u20a0-\\u32ff\\ud83c\\udc00-\\ud83f\\udfff\\udbb9\\udce5-\\udbb9\\udcee]");
|
"[\\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_HEIGHT = 64;
|
||||||
private static final int EMOJI_RAW_WIDTH = 64;
|
private static final int EMOJI_RAW_WIDTH = 64;
|
||||||
@@ -60,30 +64,29 @@ public class EmojiProvider {
|
|||||||
private static final int EMOJI_PER_ROW = 32;
|
private static final int EMOJI_PER_ROW = 32;
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private final float decodeScale;
|
private final float decodeScale, verticalPad;
|
||||||
private final float verticalPad;
|
|
||||||
private final List<EmojiPageModel> staticPages;
|
private final List<EmojiPageModel> staticPages;
|
||||||
|
|
||||||
public static EmojiProvider getInstance(Context context) {
|
static EmojiProvider getInstance(Context context) {
|
||||||
if (instance == null) {
|
if (INSTANCE == null) {
|
||||||
synchronized (EmojiProvider.class) {
|
synchronized (EmojiProvider.class) {
|
||||||
if (instance == null) {
|
if (INSTANCE == null) {
|
||||||
LOG.info("Creating new instance of EmojiProvider");
|
LOG.info("Creating new instance of EmojiProvider");
|
||||||
instance = new EmojiProvider(context);
|
INSTANCE = new EmojiProvider(context);
|
||||||
((BaseActivity) context).getActivityComponent()
|
((BaseActivity) context).getActivityComponent()
|
||||||
.inject(instance);
|
.inject(INSTANCE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return instance;
|
return INSTANCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
private EmojiProvider(Context context) {
|
private EmojiProvider(Context context) {
|
||||||
this.context = context.getApplicationContext();
|
this.context = context.getApplicationContext();
|
||||||
this.decodeScale = Math.min(1f,
|
float drawerSize =
|
||||||
context.getResources().getDimension(R.dimen.emoji_drawer_size) /
|
context.getResources().getDimension(R.dimen.emoji_drawer_size);
|
||||||
EMOJI_RAW_HEIGHT);
|
decodeScale = Math.min(1f, drawerSize / EMOJI_RAW_HEIGHT);
|
||||||
this.verticalPad = EMOJI_VERT_PAD * this.decodeScale;
|
verticalPad = EMOJI_VERT_PAD * this.decodeScale;
|
||||||
staticPages = EmojiPages.getPages(context);
|
staticPages = EmojiPages.getPages(context);
|
||||||
for (EmojiPageModel page : staticPages) {
|
for (EmojiPageModel page : staticPages) {
|
||||||
if (page.hasSpriteMap()) {
|
if (page.hasSpriteMap()) {
|
||||||
@@ -97,7 +100,8 @@ public class EmojiProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public Spannable emojify(@Nullable CharSequence text, @NonNull TextView tv) {
|
Spannable emojify(@Nullable CharSequence text,
|
||||||
|
@NonNull TextView tv) {
|
||||||
if (text == null) return null;
|
if (text == null) return null;
|
||||||
Matcher matches = EMOJI_RANGE.matcher(text);
|
Matcher matches = EMOJI_RANGE.matcher(text);
|
||||||
SpannableStringBuilder builder = new SpannableStringBuilder(text);
|
SpannableStringBuilder builder = new SpannableStringBuilder(text);
|
||||||
@@ -107,15 +111,14 @@ public class EmojiProvider {
|
|||||||
Drawable drawable = getEmojiDrawable(codePoint);
|
Drawable drawable = getEmojiDrawable(codePoint);
|
||||||
if (drawable != null) {
|
if (drawable != null) {
|
||||||
builder.setSpan(new EmojiSpan(drawable, tv), matches.start(),
|
builder.setSpan(new EmojiSpan(drawable, tv), matches.start(),
|
||||||
matches.end(),
|
matches.end(), SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public Drawable getEmojiDrawable(int emojiCode) {
|
Drawable getEmojiDrawable(int emojiCode) {
|
||||||
return getEmojiDrawable(offsets.get(emojiCode));
|
return getEmojiDrawable(offsets.get(emojiCode));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,22 +142,30 @@ public class EmojiProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(Throwable error) {
|
public void onFailure(Throwable error) {
|
||||||
LOG.log(WARNING, error.toString(), error);
|
if (LOG.isLoggable(WARNING))
|
||||||
|
LOG.log(WARNING, error.toString(), error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return drawable;
|
return drawable;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<EmojiPageModel> getStaticPages() {
|
List<EmojiPageModel> getStaticPages() {
|
||||||
return staticPages;
|
return staticPages;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public class EmojiDrawable extends Drawable {
|
class EmojiDrawable extends Drawable {
|
||||||
|
|
||||||
private final DrawInfo info;
|
private final DrawInfo info;
|
||||||
|
private final float intrinsicWidth, intrinsicHeight;
|
||||||
|
|
||||||
private Bitmap bmp;
|
private Bitmap bmp;
|
||||||
private float intrinsicWidth;
|
|
||||||
private float intrinsicHeight;
|
private EmojiDrawable(DrawInfo info, float decodeScale) {
|
||||||
|
this.info = info;
|
||||||
|
intrinsicWidth = EMOJI_RAW_WIDTH * decodeScale;
|
||||||
|
intrinsicHeight = EMOJI_RAW_HEIGHT * decodeScale;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getIntrinsicWidth() {
|
public int getIntrinsicWidth() {
|
||||||
@@ -166,32 +177,25 @@ public class EmojiProvider {
|
|||||||
return (int) intrinsicHeight;
|
return (int) intrinsicHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
private EmojiDrawable(DrawInfo info, float decodeScale) {
|
|
||||||
this.info = info;
|
|
||||||
this.intrinsicWidth = EMOJI_RAW_WIDTH * decodeScale;
|
|
||||||
this.intrinsicHeight = EMOJI_RAW_HEIGHT * decodeScale;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void draw(@NonNull Canvas canvas) {
|
public void draw(@NonNull Canvas canvas) {
|
||||||
if (bmp == null) {
|
if (bmp == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final int row = info.index / EMOJI_PER_ROW;
|
int row = info.index / EMOJI_PER_ROW;
|
||||||
final int row_index = info.index % EMOJI_PER_ROW;
|
int rowIndex = info.index % EMOJI_PER_ROW;
|
||||||
|
|
||||||
canvas.drawBitmap(bmp,
|
int left = (int) (rowIndex * intrinsicWidth);
|
||||||
new Rect((int) (row_index * intrinsicWidth),
|
int top = (int) (row * intrinsicHeight + row * verticalPad);
|
||||||
(int) (row * intrinsicHeight + row * verticalPad),
|
int right = (int) ((rowIndex + 1) * intrinsicWidth);
|
||||||
(int) ((row_index + 1) * intrinsicWidth),
|
int bottom =
|
||||||
(int) ((row + 1) * intrinsicHeight +
|
(int) ((row + 1) * intrinsicHeight + row * verticalPad);
|
||||||
row * verticalPad)),
|
canvas.drawBitmap(bmp, new Rect(left, top, right, bottom),
|
||||||
getBounds(),
|
getBounds(), PAINT);
|
||||||
paint);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setBitmap(Bitmap bitmap) {
|
void setBitmap(Bitmap bitmap) {
|
||||||
if (bmp == null || !bmp.sameAs(bitmap)) {
|
if (bmp == null || !bmp.sameAs(bitmap)) {
|
||||||
bmp = bitmap;
|
bmp = bitmap;
|
||||||
invalidateSelf();
|
invalidateSelf();
|
||||||
@@ -200,7 +204,7 @@ public class EmojiProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getOpacity() {
|
public int getOpacity() {
|
||||||
return PixelFormat.TRANSLUCENT;
|
return TRANSLUCENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -214,26 +218,29 @@ public class EmojiProvider {
|
|||||||
|
|
||||||
|
|
||||||
private static class DrawInfo {
|
private static class DrawInfo {
|
||||||
private EmojiPageBitmap page;
|
|
||||||
int index;
|
|
||||||
|
|
||||||
private DrawInfo(final EmojiPageBitmap page, final int index) {
|
private final EmojiPageBitmap page;
|
||||||
|
private final int index;
|
||||||
|
|
||||||
|
private DrawInfo(EmojiPageBitmap page, int index) {
|
||||||
this.page = page;
|
this.page = page;
|
||||||
this.index = index;
|
this.index = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "DrawInfo{ " +"page = " + page +", index = " + index + '}';
|
return "DrawInfo{ " + "page = " + page + ", index = " + index + '}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private class EmojiPageBitmap {
|
private class EmojiPageBitmap {
|
||||||
private EmojiPageModel model;
|
|
||||||
private SoftReference<Bitmap> bitmapReference;
|
private final EmojiPageModel model;
|
||||||
|
|
||||||
private ListenableFutureTask<Bitmap> task;
|
private ListenableFutureTask<Bitmap> task;
|
||||||
|
|
||||||
|
private volatile SoftReference<Bitmap> bitmapReference;
|
||||||
|
|
||||||
private EmojiPageBitmap(EmojiPageModel model) {
|
private EmojiPageBitmap(EmojiPageModel model) {
|
||||||
this.model = model;
|
this.model = model;
|
||||||
}
|
}
|
||||||
@@ -249,7 +256,8 @@ public class EmojiProvider {
|
|||||||
@Nullable
|
@Nullable
|
||||||
public Bitmap call() throws Exception {
|
public Bitmap call() throws Exception {
|
||||||
try {
|
try {
|
||||||
LOG.info("loading page " + model.getSprite());
|
if (LOG.isLoggable(INFO))
|
||||||
|
LOG.info("Loading page " + model.getSprite());
|
||||||
return loadPage();
|
return loadPage();
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
LOG.log(WARNING, ioe.toString(), ioe);
|
LOG.log(WARNING, ioe.toString(), ioe);
|
||||||
@@ -283,7 +291,8 @@ public class EmojiProvider {
|
|||||||
"file:///android_asset/" + model.getSprite(),
|
"file:///android_asset/" + model.getSprite(),
|
||||||
decodeScale);
|
decodeScale);
|
||||||
bitmapReference = new SoftReference<>(bitmap);
|
bitmapReference = new SoftReference<>(bitmap);
|
||||||
LOG.info("onPageLoaded(" + model.getSprite() + ")");
|
if (LOG.isLoggable(INFO))
|
||||||
|
LOG.info("Loaded page " + model.getSprite());
|
||||||
return bitmap;
|
return bitmap;
|
||||||
} catch (BitmapDecodingException e) {
|
} catch (BitmapDecodingException e) {
|
||||||
LOG.log(WARNING, e.toString(), e);
|
LOG.log(WARNING, e.toString(), e);
|
||||||
|
|||||||
@@ -10,11 +10,12 @@ import android.widget.TextView;
|
|||||||
import org.briarproject.R;
|
import org.briarproject.R;
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
public class EmojiSpan extends AnimatingImageSpan {
|
class EmojiSpan extends AnimatingImageSpan {
|
||||||
|
|
||||||
private final int size;
|
private final int size;
|
||||||
private final FontMetricsInt fm;
|
private final FontMetricsInt fm;
|
||||||
|
|
||||||
public EmojiSpan(@NonNull Drawable drawable, @NonNull TextView tv) {
|
EmojiSpan(@NonNull Drawable drawable, @NonNull TextView tv) {
|
||||||
super(drawable, tv);
|
super(drawable, tv);
|
||||||
fm = tv.getPaint().getFontMetricsInt();
|
fm = tv.getPaint().getFontMetricsInt();
|
||||||
size = fm != null ? Math.abs(fm.descent) + Math.abs(fm.ascent)
|
size = fm != null ? Math.abs(fm.descent) + Math.abs(fm.ascent)
|
||||||
|
|||||||
@@ -7,12 +7,16 @@ import android.support.annotation.NonNull;
|
|||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.annotation.UiThread;
|
import android.support.annotation.UiThread;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.text.TextUtils.TruncateAt;
|
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.components.emoji.EmojiProvider.EmojiDrawable;
|
import org.thoughtcrime.securesms.components.emoji.EmojiProvider.EmojiDrawable;
|
||||||
|
|
||||||
|
import static android.text.TextUtils.TruncateAt.END;
|
||||||
|
import static android.view.View.MeasureSpec.AT_MOST;
|
||||||
|
import static android.view.View.MeasureSpec.EXACTLY;
|
||||||
|
import static android.widget.TextView.BufferType.SPANNABLE;
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
public class EmojiTextView extends TextView {
|
public class EmojiTextView extends TextView {
|
||||||
|
|
||||||
@@ -41,9 +45,7 @@ public class EmojiTextView extends TextView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void setTextEllipsized(final @Nullable CharSequence source) {
|
private void setTextEllipsized(final @Nullable CharSequence source) {
|
||||||
super.setText(
|
super.setText(needsEllipsizing ? ellipsize(source) : source, SPANNABLE);
|
||||||
needsEllipsizing ? ellipsize(source) : source,
|
|
||||||
BufferType.SPANNABLE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -56,18 +58,16 @@ public class EmojiTextView extends TextView {
|
|||||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||||
final int size = MeasureSpec.getSize(widthMeasureSpec);
|
final int size = MeasureSpec.getSize(widthMeasureSpec);
|
||||||
final int mode = MeasureSpec.getMode(widthMeasureSpec);
|
final int mode = MeasureSpec.getMode(widthMeasureSpec);
|
||||||
if (getEllipsize() == TruncateAt.END &&
|
if (getEllipsize() == END &&
|
||||||
!TextUtils.isEmpty(source) &&
|
!TextUtils.isEmpty(source) &&
|
||||||
(mode == MeasureSpec.AT_MOST || mode == MeasureSpec.EXACTLY) &&
|
(mode == AT_MOST || mode == EXACTLY) &&
|
||||||
getPaint().breakText(source, 0, source.length() - 1, true, size,
|
getPaint().breakText(source, 0, source.length() - 1, true, size,
|
||||||
null) != source.length()) {
|
null) != source.length()) {
|
||||||
needsEllipsizing = true;
|
needsEllipsizing = true;
|
||||||
FontMetricsInt font = getPaint().getFontMetricsInt();
|
FontMetricsInt font = getPaint().getFontMetricsInt();
|
||||||
super.onMeasure(
|
int height = Math.abs(font.top - font.bottom);
|
||||||
MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY),
|
super.onMeasure(MeasureSpec.makeMeasureSpec(size, EXACTLY),
|
||||||
MeasureSpec
|
MeasureSpec.makeMeasureSpec(height, EXACTLY));
|
||||||
.makeMeasureSpec(Math.abs(font.top - font.bottom),
|
|
||||||
MeasureSpec.EXACTLY));
|
|
||||||
} else {
|
} else {
|
||||||
needsEllipsizing = false;
|
needsEllipsizing = false;
|
||||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||||
@@ -84,13 +84,11 @@ public class EmojiTextView extends TextView {
|
|||||||
@Nullable
|
@Nullable
|
||||||
public CharSequence ellipsize(@Nullable CharSequence text) {
|
public CharSequence ellipsize(@Nullable CharSequence text) {
|
||||||
if (TextUtils.isEmpty(text) || getWidth() == 0 ||
|
if (TextUtils.isEmpty(text) || getWidth() == 0 ||
|
||||||
getEllipsize() != TruncateAt.END) {
|
getEllipsize() != END) {
|
||||||
return text;
|
return text;
|
||||||
} else {
|
} else {
|
||||||
return TextUtils.ellipsize(text,
|
return TextUtils.ellipsize(text, getPaint(),
|
||||||
getPaint(),
|
getWidth() - getPaddingRight() - getPaddingLeft(), END);
|
||||||
getWidth() - getPaddingRight() - getPaddingLeft(),
|
|
||||||
TruncateAt.END);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,14 +13,18 @@ import android.view.View;
|
|||||||
|
|
||||||
import org.briarproject.R;
|
import org.briarproject.R;
|
||||||
|
|
||||||
|
import static android.graphics.Paint.ANTI_ALIAS_FLAG;
|
||||||
|
import static android.graphics.Paint.Align.CENTER;
|
||||||
|
import static android.graphics.Paint.FILTER_BITMAP_FLAG;
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
public class EmojiView extends View implements Drawable.Callback {
|
public class EmojiView extends View implements Drawable.Callback {
|
||||||
|
|
||||||
|
private final Paint paint = new Paint(ANTI_ALIAS_FLAG | FILTER_BITMAP_FLAG);
|
||||||
|
|
||||||
private String emoji;
|
private String emoji;
|
||||||
private Drawable drawable;
|
private Drawable drawable;
|
||||||
|
|
||||||
private final Paint paint =
|
|
||||||
new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
|
|
||||||
|
|
||||||
public EmojiView(Context context) {
|
public EmojiView(Context context) {
|
||||||
this(context, null);
|
this(context, null);
|
||||||
}
|
}
|
||||||
@@ -60,7 +64,7 @@ public class EmojiView extends View implements Drawable.Callback {
|
|||||||
paint.setTextSize(targetFontSize);
|
paint.setTextSize(targetFontSize);
|
||||||
paint.setColor(ContextCompat
|
paint.setColor(ContextCompat
|
||||||
.getColor(getContext(), R.color.emoji_text_color));
|
.getColor(getContext(), R.color.emoji_text_color));
|
||||||
paint.setTextAlign(Paint.Align.CENTER);
|
paint.setTextAlign(CENTER);
|
||||||
int xPos = (canvas.getWidth() / 2);
|
int xPos = (canvas.getWidth() / 2);
|
||||||
int yPos = (int) ((canvas.getHeight() / 2) -
|
int yPos = (int) ((canvas.getHeight() / 2) -
|
||||||
((paint.descent() + paint.ascent()) / 2));
|
((paint.descent() + paint.ascent()) / 2));
|
||||||
@@ -81,9 +85,4 @@ public class EmojiView extends View implements Drawable.Callback {
|
|||||||
super.invalidateDrawable(drawable);
|
super.invalidateDrawable(drawable);
|
||||||
postInvalidate();
|
postInvalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
|
||||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,16 +20,14 @@ import java.util.logging.Logger;
|
|||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static java.util.logging.Level.INFO;
|
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static org.briarproject.android.fragment.SettingsFragment.SETTINGS_NAMESPACE;
|
import static org.briarproject.android.fragment.SettingsFragment.SETTINGS_NAMESPACE;
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
public class RecentEmojiPageModel implements EmojiPageModel {
|
public class RecentEmojiPageModel implements EmojiPageModel {
|
||||||
|
|
||||||
private static final String TAG =
|
private static final Logger LOG =
|
||||||
RecentEmojiPageModel.class.getSimpleName();
|
Logger.getLogger(RecentEmojiPageModel.class.getName());
|
||||||
private static final Logger LOG = Logger.getLogger(TAG);
|
|
||||||
|
|
||||||
private static final String EMOJI_LRU_PREFERENCE = "pref_emoji_recent";
|
private static final String EMOJI_LRU_PREFERENCE = "pref_emoji_recent";
|
||||||
private static final int EMOJI_LRU_SIZE = 50;
|
private static final int EMOJI_LRU_SIZE = 50;
|
||||||
@@ -38,11 +36,11 @@ public class RecentEmojiPageModel implements EmojiPageModel {
|
|||||||
private Settings settings;
|
private Settings settings;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
protected SettingsManager settingsManager;
|
SettingsManager settingsManager;
|
||||||
@Inject
|
@Inject
|
||||||
protected DbController dbController;
|
DbController dbController;
|
||||||
|
|
||||||
public RecentEmojiPageModel(Context context) {
|
RecentEmojiPageModel(Context context) {
|
||||||
if (!(context instanceof BaseActivity)) {
|
if (!(context instanceof BaseActivity)) {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
"Needs to be created from BaseActivity");
|
"Needs to be created from BaseActivity");
|
||||||
@@ -85,9 +83,7 @@ public class RecentEmojiPageModel implements EmojiPageModel {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onCodePointSelected(String emoji) {
|
void onCodePointSelected(String emoji) {
|
||||||
if (LOG.isLoggable(INFO))
|
|
||||||
LOG.info("onCodePointSelected(" + emoji + ")");
|
|
||||||
recentlyUsed.remove(emoji);
|
recentlyUsed.remove(emoji);
|
||||||
recentlyUsed.add(emoji);
|
recentlyUsed.add(emoji);
|
||||||
|
|
||||||
@@ -105,7 +101,7 @@ public class RecentEmojiPageModel implements EmojiPageModel {
|
|||||||
result += emoji + ";";
|
result += emoji + ";";
|
||||||
}
|
}
|
||||||
if (!emojis.isEmpty())
|
if (!emojis.isEmpty())
|
||||||
result = result.substring(0, result.length()-1);
|
result = result.substring(0, result.length() - 1);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import android.support.annotation.Nullable;
|
|||||||
import android.support.annotation.UiThread;
|
import android.support.annotation.UiThread;
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
public class StaticEmojiPageModel implements EmojiPageModel {
|
class StaticEmojiPageModel implements EmojiPageModel {
|
||||||
|
|
||||||
@DrawableRes
|
@DrawableRes
|
||||||
private final int icon;
|
private final int icon;
|
||||||
@@ -17,14 +17,14 @@ public class StaticEmojiPageModel implements EmojiPageModel {
|
|||||||
@Nullable
|
@Nullable
|
||||||
private final String sprite;
|
private final String sprite;
|
||||||
|
|
||||||
public StaticEmojiPageModel(@DrawableRes int icon, @NonNull String[] emoji,
|
StaticEmojiPageModel(@DrawableRes int icon, @NonNull String[] emoji,
|
||||||
@Nullable String sprite) {
|
@Nullable String sprite) {
|
||||||
this.icon = icon;
|
this.icon = icon;
|
||||||
this.emoji = emoji;
|
this.emoji = emoji;
|
||||||
this.sprite = sprite;
|
this.sprite = sprite;
|
||||||
}
|
}
|
||||||
|
|
||||||
public StaticEmojiPageModel(Context ctx, @DrawableRes int icon,
|
StaticEmojiPageModel(Context ctx, @DrawableRes int icon,
|
||||||
@ArrayRes int res, @Nullable String sprite) {
|
@ArrayRes int res, @Nullable String sprite) {
|
||||||
this(icon, getEmoji(ctx, res), sprite);
|
this(icon, getEmoji(ctx, res), sprite);
|
||||||
}
|
}
|
||||||
@@ -35,6 +35,7 @@ public class StaticEmojiPageModel implements EmojiPageModel {
|
|||||||
return icon;
|
return icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
@NonNull
|
@NonNull
|
||||||
public String[] getEmoji() {
|
public String[] getEmoji() {
|
||||||
return emoji;
|
return emoji;
|
||||||
|
|||||||
@@ -2,11 +2,11 @@ package org.thoughtcrime.securesms.util;
|
|||||||
|
|
||||||
public class BitmapDecodingException extends Exception {
|
public class BitmapDecodingException extends Exception {
|
||||||
|
|
||||||
public BitmapDecodingException(String s) {
|
BitmapDecodingException(String s) {
|
||||||
super(s);
|
super(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
public BitmapDecodingException(Exception nested) {
|
BitmapDecodingException(Exception nested) {
|
||||||
super(nested);
|
super(nested);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package org.thoughtcrime.securesms.util;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.BitmapFactory;
|
import android.graphics.BitmapFactory;
|
||||||
import android.util.Log;
|
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
|
|
||||||
import com.bumptech.glide.Glide;
|
import com.bumptech.glide.Glide;
|
||||||
@@ -17,10 +16,14 @@ import com.bumptech.glide.load.resource.bitmap.FitCenter;
|
|||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import static java.util.logging.Level.WARNING;
|
||||||
|
|
||||||
public class BitmapUtil {
|
public class BitmapUtil {
|
||||||
|
|
||||||
private static final String TAG = BitmapUtil.class.getSimpleName();
|
private static final Logger LOG =
|
||||||
|
Logger.getLogger(BitmapUtil.class.getName());
|
||||||
|
|
||||||
private static <T> InputStream getInputStreamForModel(Context context,
|
private static <T> InputStream getInputStreamForModel(Context context,
|
||||||
T model)
|
T model)
|
||||||
@@ -40,8 +43,7 @@ public class BitmapUtil {
|
|||||||
final Bitmap rough = Downsampler.AT_LEAST
|
final Bitmap rough = Downsampler.AT_LEAST
|
||||||
.decode(getInputStreamForModel(context, model),
|
.decode(getInputStreamForModel(context, model),
|
||||||
Glide.get(context).getBitmapPool(),
|
Glide.get(context).getBitmapPool(),
|
||||||
width, height,
|
width, height, DecodeFormat.PREFER_RGB_565);
|
||||||
DecodeFormat.PREFER_RGB_565);
|
|
||||||
|
|
||||||
final Resource<Bitmap> resource = BitmapResource
|
final Resource<Bitmap> resource = BitmapResource
|
||||||
.obtain(rough, Glide.get(context).getBitmapPool());
|
.obtain(rough, Glide.get(context).getBitmapPool());
|
||||||
@@ -55,8 +57,7 @@ public class BitmapUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static <T> Bitmap createScaledBitmap(Context context, T model,
|
public static <T> Bitmap createScaledBitmap(Context context, T model,
|
||||||
float scale)
|
float scale) throws BitmapDecodingException {
|
||||||
throws BitmapDecodingException {
|
|
||||||
Pair<Integer, Integer> dimens =
|
Pair<Integer, Integer> dimens =
|
||||||
getDimensions(getInputStreamForModel(context, model));
|
getDimensions(getInputStreamForModel(context, model));
|
||||||
return createScaledBitmapInto(context, model,
|
return createScaledBitmapInto(context, model,
|
||||||
@@ -72,9 +73,8 @@ public class BitmapUtil {
|
|||||||
BitmapFactory.decodeStream(fis, null, options);
|
BitmapFactory.decodeStream(fis, null, options);
|
||||||
try {
|
try {
|
||||||
fis.close();
|
fis.close();
|
||||||
} catch (IOException ioe) {
|
} catch (IOException e) {
|
||||||
Log.w(TAG,
|
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||||
"failed to close the InputStream after reading image dimensions");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.outWidth == -1 || options.outHeight == -1) {
|
if (options.outWidth == -1 || options.outHeight == -1) {
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ import java.util.List;
|
|||||||
|
|
||||||
import static junit.framework.Assert.assertEquals;
|
import static junit.framework.Assert.assertEquals;
|
||||||
import static junit.framework.Assert.assertTrue;
|
import static junit.framework.Assert.assertTrue;
|
||||||
|
import static org.briarproject.api.identity.Author.Status.UNKNOWN;
|
||||||
|
import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
|
||||||
import static org.mockito.Mockito.times;
|
import static org.mockito.Mockito.times;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
@@ -77,11 +79,12 @@ public class ForumActivityTest {
|
|||||||
private List<ForumEntry> getDummyData() {
|
private List<ForumEntry> getDummyData() {
|
||||||
ForumEntry[] forumEntries = new ForumEntry[6];
|
ForumEntry[] forumEntries = new ForumEntry[6];
|
||||||
for (int i = 0; i < forumEntries.length; i++) {
|
for (int i = 0; i < forumEntries.length; i++) {
|
||||||
forumEntries[i] =
|
AuthorId authorId = new AuthorId(TestUtils.getRandomId());
|
||||||
new ForumEntry(new MessageId(TestUtils.getRandomId()),
|
byte[] publicKey = TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH);
|
||||||
AUTHORS[i], LEVELS[i], System.currentTimeMillis(),
|
Author author = new Author(authorId, AUTHORS[i], publicKey);
|
||||||
AUTHORS[i], new AuthorId(TestUtils.getRandomId()),
|
forumEntries[i] = new ForumEntry(
|
||||||
Author.Status.UNKNOWN);
|
new MessageId(TestUtils.getRandomId()), AUTHORS[i],
|
||||||
|
LEVELS[i], System.currentTimeMillis(), author, UNKNOWN);
|
||||||
}
|
}
|
||||||
return new ArrayList<>(Arrays.asList(forumEntries));
|
return new ArrayList<>(Arrays.asList(forumEntries));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user