mirror of
https://github.com/oxen-io/session-android.git
synced 2024-12-24 16:57:50 +00:00
Allow the selection of fitzpatrick emoji.
This commit is contained in:
parent
1999d09901
commit
48ff9673b9
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
|
||||||
|
<corners android:radius="5dp" />
|
||||||
|
<solid android:color="@color/core_white" />
|
||||||
|
|
||||||
|
</shape>
|
13
res/drawable/triangle_bottom_right_corner.xml
Normal file
13
res/drawable/triangle_bottom_right_corner.xml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
|
||||||
|
<path
|
||||||
|
android:pathData="M24,0 L0,24 L24,24 Z"
|
||||||
|
android:strokeColor="#00000000"
|
||||||
|
android:fillColor="#ffffff"/>
|
||||||
|
|
||||||
|
</vector>
|
38
res/layout/emoji_display_item.xml
Normal file
38
res/layout/emoji_display_item.xml
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<FrameLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="2dp"
|
||||||
|
android:background="?selectableItemBackground">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/emoji_image"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:paddingTop="6dp"
|
||||||
|
android:paddingRight="6dp"
|
||||||
|
android:paddingLeft="6dp"
|
||||||
|
android:adjustViewBounds="true"
|
||||||
|
android:scaleType="fitCenter" />
|
||||||
|
|
||||||
|
<org.thoughtcrime.securesms.components.emoji.AsciiEmojiView
|
||||||
|
android:id="@+id/emoji_text"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingTop="6dp"
|
||||||
|
android:paddingRight="6dp"
|
||||||
|
android:paddingLeft="6dp"
|
||||||
|
android:visibility="gone"/>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/emoji_variation_hint"
|
||||||
|
android:layout_width="7dp"
|
||||||
|
android:layout_height="7dp"
|
||||||
|
android:layout_gravity="bottom|right|end"
|
||||||
|
app:srcCompat="@drawable/triangle_bottom_right_corner"
|
||||||
|
android:tint="@color/core_grey_25"/>
|
||||||
|
|
||||||
|
</FrameLayout>
|
@ -36,7 +36,7 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<android.support.v4.view.ViewPager
|
<org.thoughtcrime.securesms.components.ControllableViewPager
|
||||||
android:id="@+id/emoji_pager"
|
android:id="@+id/emoji_pager"
|
||||||
android:visibility="visible"
|
android:visibility="visible"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -4,15 +4,11 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<GridView android:id="@+id/emoji"
|
<android.support.v7.widget.RecyclerView
|
||||||
android:visibility="visible"
|
android:id="@+id/emoji"
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:stretchMode="columnWidth"
|
android:clipToPadding="false"
|
||||||
android:columnWidth="@dimen/emoji_drawer_size"
|
android:paddingBottom="8dp"/>
|
||||||
android:horizontalSpacing="0dp"
|
|
||||||
android:verticalSpacing="0dp"
|
|
||||||
android:numColumns="auto_fit"
|
|
||||||
android:gravity="center"/>
|
|
||||||
|
|
||||||
</FrameLayout>
|
</FrameLayout>
|
9
res/layout/emoji_variation_selector.xml
Normal file
9
res/layout/emoji_variation_selector.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/emoji_variation_selector_background_light">
|
||||||
|
|
||||||
|
</LinearLayout>
|
8
res/layout/emoji_variation_selector_item.xml
Normal file
8
res/layout/emoji_variation_selector_item.xml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ImageView
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="50dp"
|
||||||
|
android:layout_height="50dp"
|
||||||
|
android:padding="6dp">
|
||||||
|
|
||||||
|
</ImageView>
|
@ -8,6 +8,7 @@
|
|||||||
<dimen name="emoji_drawer_item_padding">5dp</dimen>
|
<dimen name="emoji_drawer_item_padding">5dp</dimen>
|
||||||
<dimen name="emoji_drawer_indicator_height">1.5dp</dimen>
|
<dimen name="emoji_drawer_indicator_height">1.5dp</dimen>
|
||||||
<dimen name="emoji_drawer_left_right_padding">5dp</dimen>
|
<dimen name="emoji_drawer_left_right_padding">5dp</dimen>
|
||||||
|
<dimen name="emoji_drawer_item_width">48dp</dimen>
|
||||||
<dimen name="conversation_item_body_text_size">16sp</dimen>
|
<dimen name="conversation_item_body_text_size">16sp</dimen>
|
||||||
<dimen name="conversation_item_date_text_size">12sp</dimen>
|
<dimen name="conversation_item_date_text_size">12sp</dimen>
|
||||||
<dimen name="transport_selection_popup_width">200sp</dimen>
|
<dimen name="transport_selection_popup_width">200sp</dimen>
|
||||||
|
@ -0,0 +1,60 @@
|
|||||||
|
package org.thoughtcrime.securesms.components.emoji;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.R;
|
||||||
|
import org.thoughtcrime.securesms.util.ResUtil;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
public class AsciiEmojiView extends View {
|
||||||
|
|
||||||
|
private final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
|
||||||
|
|
||||||
|
private String emoji;
|
||||||
|
|
||||||
|
public AsciiEmojiView(Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AsciiEmojiView(Context context, @Nullable @android.support.annotation.Nullable AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEmoji(String emoji) {
|
||||||
|
this.emoji = emoji;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDraw(Canvas canvas) {
|
||||||
|
if (TextUtils.isEmpty(emoji)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float targetFontSize = 0.75f * getHeight() - getPaddingTop() - getPaddingBottom();
|
||||||
|
|
||||||
|
paint.setTextSize(targetFontSize);
|
||||||
|
paint.setColor(ResUtil.getColor(getContext(), R.attr.emoji_text_color));
|
||||||
|
paint.setTextAlign(Paint.Align.CENTER);
|
||||||
|
|
||||||
|
int xPos = (getWidth() / 2);
|
||||||
|
int yPos = (int) ((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
|
||||||
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||||
|
super.onMeasure(widthMeasureSpec, widthMeasureSpec);
|
||||||
|
}
|
||||||
|
}
|
21
src/org/thoughtcrime/securesms/components/emoji/Emoji.java
Normal file
21
src/org/thoughtcrime/securesms/components/emoji/Emoji.java
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package org.thoughtcrime.securesms.components.emoji;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class Emoji {
|
||||||
|
|
||||||
|
private final List<String> variations;
|
||||||
|
|
||||||
|
public Emoji(String... variations) {
|
||||||
|
this.variations = Arrays.asList(variations);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValue() {
|
||||||
|
return variations.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getVariations() {
|
||||||
|
return variations;
|
||||||
|
}
|
||||||
|
}
|
@ -2,10 +2,11 @@ package org.thoughtcrime.securesms.components.emoji;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.support.v4.view.PagerAdapter;
|
import android.support.v4.view.PagerAdapter;
|
||||||
import android.support.v4.view.ViewPager;
|
import android.support.v4.view.ViewPager;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.components.emoji.EmojiPageViewGridAdapter.VariationSelectorListener;
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
@ -27,7 +28,7 @@ import org.thoughtcrime.securesms.util.ResUtil;
|
|||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class EmojiDrawer extends LinearLayout implements InputView {
|
public class EmojiDrawer extends LinearLayout implements InputView, EmojiSelectionListener, VariationSelectorListener {
|
||||||
|
|
||||||
private static final String TAG = EmojiDrawer.class.getSimpleName();
|
private static final String TAG = EmojiDrawer.class.getSimpleName();
|
||||||
|
|
||||||
@ -101,19 +102,23 @@ public class EmojiDrawer extends LinearLayout implements InputView {
|
|||||||
Log.i(TAG, "hide()");
|
Log.i(TAG, "hide()");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializeEmojiGrid() {
|
@Override
|
||||||
pager.setAdapter(new EmojiPagerAdapter(getContext(),
|
public void onEmojiSelected(String emoji) {
|
||||||
models,
|
recentModel.onCodePointSelected(emoji);
|
||||||
new EmojiSelectionListener() {
|
if (listener != null) {
|
||||||
@Override
|
listener.onEmojiSelected(emoji);
|
||||||
public void onEmojiSelected(String emoji) {
|
}
|
||||||
Log.i(TAG, "onEmojiSelected()");
|
}
|
||||||
recentModel.onCodePointSelected(emoji);
|
|
||||||
if (listener != null) listener.onEmojiSelected(emoji);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
if (recentModel.getEmoji().length == 0) {
|
@Override
|
||||||
|
public void onVariationSelectorStateChanged(boolean open) {
|
||||||
|
pager.setEnabled(!open);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeEmojiGrid() {
|
||||||
|
pager.setAdapter(new EmojiPagerAdapter(getContext(), models, this, this));
|
||||||
|
|
||||||
|
if (recentModel.getDisplayEmoji().size() == 0) {
|
||||||
pager.setCurrentItem(1);
|
pager.setCurrentItem(1);
|
||||||
}
|
}
|
||||||
strip.setViewPager(pager);
|
strip.setViewPager(pager);
|
||||||
@ -129,18 +134,21 @@ public class EmojiDrawer extends LinearLayout implements InputView {
|
|||||||
public static class EmojiPagerAdapter extends PagerAdapter
|
public static class EmojiPagerAdapter extends PagerAdapter
|
||||||
implements PagerSlidingTabStrip.CustomTabProvider
|
implements PagerSlidingTabStrip.CustomTabProvider
|
||||||
{
|
{
|
||||||
private Context context;
|
private Context context;
|
||||||
private List<EmojiPageModel> pages;
|
private List<EmojiPageModel> pages;
|
||||||
private EmojiSelectionListener listener;
|
private EmojiSelectionListener emojiSelectionListener;
|
||||||
|
private VariationSelectorListener variationSelectorListener;
|
||||||
|
|
||||||
public EmojiPagerAdapter(@NonNull Context context,
|
public EmojiPagerAdapter(@NonNull Context context,
|
||||||
@NonNull List<EmojiPageModel> pages,
|
@NonNull List<EmojiPageModel> pages,
|
||||||
@Nullable EmojiSelectionListener listener)
|
@NonNull EmojiSelectionListener emojiSelectionListener,
|
||||||
|
@NonNull VariationSelectorListener variationSelectorListener)
|
||||||
{
|
{
|
||||||
super();
|
super();
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.pages = pages;
|
this.pages = pages;
|
||||||
this.listener = listener;
|
this.emojiSelectionListener = emojiSelectionListener;
|
||||||
|
this.variationSelectorListener = variationSelectorListener;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -149,10 +157,9 @@ public class EmojiDrawer extends LinearLayout implements InputView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object instantiateItem(ViewGroup container, int position) {
|
public @NonNull Object instantiateItem(@NonNull ViewGroup container, int position) {
|
||||||
EmojiPageView page = new EmojiPageView(context);
|
EmojiPageView page = new EmojiPageView(context, emojiSelectionListener, variationSelectorListener);
|
||||||
page.setModel(pages.get(position));
|
page.setModel(pages.get(position));
|
||||||
page.setEmojiSelectedListener(listener);
|
|
||||||
container.addView(page);
|
container.addView(page);
|
||||||
return page;
|
return page;
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
package org.thoughtcrime.securesms.components.emoji;
|
package org.thoughtcrime.securesms.components.emoji;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public interface EmojiPageModel {
|
public interface EmojiPageModel {
|
||||||
int getIconAttr();
|
int getIconAttr();
|
||||||
String[] getEmoji();
|
List<String> getEmoji();
|
||||||
String[] getDisplayEmoji();
|
List<Emoji> getDisplayEmoji();
|
||||||
boolean hasSpriteMap();
|
boolean hasSpriteMap();
|
||||||
String getSprite();
|
String getSprite();
|
||||||
boolean isDynamic();
|
boolean isDynamic();
|
||||||
|
@ -1,106 +1,92 @@
|
|||||||
package org.thoughtcrime.securesms.components.emoji;
|
package org.thoughtcrime.securesms.components.emoji;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.util.AttributeSet;
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.v7.widget.GridLayoutManager;
|
||||||
|
import android.support.v7.widget.RecyclerView;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.AbsListView;
|
|
||||||
import android.widget.AdapterView;
|
|
||||||
import android.widget.AdapterView.OnItemClickListener;
|
|
||||||
import android.widget.BaseAdapter;
|
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
import android.widget.GridView;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.R;
|
import org.thoughtcrime.securesms.R;
|
||||||
|
import org.thoughtcrime.securesms.components.emoji.EmojiPageViewGridAdapter.VariationSelectorListener;
|
||||||
|
|
||||||
public class EmojiPageView extends FrameLayout {
|
public class EmojiPageView extends FrameLayout implements VariationSelectorListener {
|
||||||
private static final String TAG = EmojiPageView.class.getSimpleName();
|
private static final String TAG = EmojiPageView.class.getSimpleName();
|
||||||
|
|
||||||
private EmojiPageModel model;
|
private EmojiPageModel model;
|
||||||
private EmojiSelectionListener listener;
|
private EmojiPageViewGridAdapter adapter;
|
||||||
private GridView grid;
|
private RecyclerView recyclerView;
|
||||||
|
private GridLayoutManager layoutManager;
|
||||||
|
private RecyclerView.OnItemTouchListener scrollDisabler;
|
||||||
|
private VariationSelectorListener variationSelectorListener;
|
||||||
|
|
||||||
public EmojiPageView(Context context) {
|
public EmojiPageView(@NonNull Context context,
|
||||||
this(context, null);
|
@NonNull EmojiSelectionListener emojiSelectionListener,
|
||||||
}
|
@NonNull VariationSelectorListener variationSelectorListener)
|
||||||
|
{
|
||||||
public EmojiPageView(Context context, AttributeSet attrs) {
|
super(context);
|
||||||
this(context, attrs, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public EmojiPageView(Context context, AttributeSet attrs, int defStyleAttr) {
|
|
||||||
super(context, attrs, defStyleAttr);
|
|
||||||
final View view = LayoutInflater.from(getContext()).inflate(R.layout.emoji_grid_layout, this, true);
|
final View view = LayoutInflater.from(getContext()).inflate(R.layout.emoji_grid_layout, this, true);
|
||||||
grid = (GridView) view.findViewById(R.id.emoji);
|
|
||||||
grid.setColumnWidth(getResources().getDimensionPixelSize(R.dimen.emoji_drawer_size) + 2 * getResources().getDimensionPixelSize(R.dimen.emoji_drawer_item_padding));
|
this.variationSelectorListener = variationSelectorListener;
|
||||||
grid.setOnItemClickListener(new OnItemClickListener() {
|
|
||||||
@Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
recyclerView = view.findViewById(R.id.emoji);
|
||||||
if (listener != null) listener.onEmojiSelected(((EmojiView)view).getEmoji());
|
layoutManager = new GridLayoutManager(context, 8);
|
||||||
}
|
scrollDisabler = new ScrollDisabler();
|
||||||
});
|
adapter = new EmojiPageViewGridAdapter(EmojiProvider.getInstance(context),
|
||||||
|
new EmojiVariationSelectorPopup(context, emojiSelectionListener),
|
||||||
|
emojiSelectionListener,
|
||||||
|
this);
|
||||||
|
|
||||||
|
recyclerView.setLayoutManager(layoutManager);
|
||||||
|
recyclerView.setAdapter(adapter);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onSelected() {
|
public void onSelected() {
|
||||||
if (model.isDynamic() && grid != null && grid.getAdapter() != null) {
|
if (model.isDynamic() && adapter != null) {
|
||||||
((EmojiGridAdapter)grid.getAdapter()).notifyDataSetChanged();
|
adapter.notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setModel(EmojiPageModel model) {
|
public void setModel(EmojiPageModel model) {
|
||||||
this.model = model;
|
this.model = model;
|
||||||
grid.setAdapter(new EmojiGridAdapter(getContext(), model));
|
adapter.setEmoji(model.getDisplayEmoji());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setEmojiSelectedListener(EmojiSelectionListener listener) {
|
@Override
|
||||||
this.listener = listener;
|
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||||
|
int idealWidth = getContext().getResources().getDimensionPixelOffset(R.dimen.emoji_drawer_item_width);
|
||||||
|
layoutManager.setSpanCount(Math.max(w / idealWidth, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class EmojiGridAdapter extends BaseAdapter {
|
@Override
|
||||||
|
public void onVariationSelectorStateChanged(boolean open) {
|
||||||
protected final Context context;
|
if (open) {
|
||||||
private final int emojiSize;
|
recyclerView.addOnItemTouchListener(scrollDisabler);
|
||||||
private final EmojiPageModel model;
|
} else {
|
||||||
|
post(() -> recyclerView.removeOnItemTouchListener(scrollDisabler));
|
||||||
public EmojiGridAdapter(Context context, EmojiPageModel model) {
|
|
||||||
this.context = context;
|
|
||||||
this.emojiSize = (int) context.getResources().getDimension(R.dimen.emoji_drawer_size);
|
|
||||||
this.model = model;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public int getCount() {
|
if (variationSelectorListener != null) {
|
||||||
return model.getDisplayEmoji().length;
|
variationSelectorListener.onVariationSelectorStateChanged(open);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getItem(int position) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getItemId(int position) {
|
|
||||||
return position;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public View getView(final int position, final View convertView, final ViewGroup parent) {
|
|
||||||
final EmojiView view;
|
|
||||||
final int pad = context.getResources().getDimensionPixelSize(R.dimen.emoji_drawer_item_padding);
|
|
||||||
if (convertView != null && convertView instanceof EmojiView) {
|
|
||||||
view = (EmojiView)convertView;
|
|
||||||
} else {
|
|
||||||
final EmojiView emojiView = new EmojiView(context);
|
|
||||||
emojiView.setPadding(pad, pad, pad, pad);
|
|
||||||
emojiView.setLayoutParams(new AbsListView.LayoutParams(emojiSize + 2 * pad, emojiSize + 2 * pad));
|
|
||||||
view = emojiView;
|
|
||||||
}
|
|
||||||
|
|
||||||
view.setEmoji(model.getDisplayEmoji()[position]);
|
|
||||||
return view;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface EmojiSelectionListener {
|
public interface EmojiSelectionListener {
|
||||||
void onEmojiSelected(String emoji);
|
void onEmojiSelected(String emoji);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class ScrollDisabler implements RecyclerView.OnItemTouchListener {
|
||||||
|
@Override
|
||||||
|
public boolean onInterceptTouchEvent(@NonNull RecyclerView recyclerView, @NonNull MotionEvent motionEvent) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTouchEvent(@NonNull RecyclerView recyclerView, @NonNull MotionEvent motionEvent) { }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRequestDisallowInterceptTouchEvent(boolean b) { }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,116 @@
|
|||||||
|
package org.thoughtcrime.securesms.components.emoji;
|
||||||
|
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.v7.widget.RecyclerView;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.PopupWindow;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.R;
|
||||||
|
import org.thoughtcrime.securesms.components.emoji.EmojiPageView.EmojiSelectionListener;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class EmojiPageViewGridAdapter extends RecyclerView.Adapter<EmojiPageViewGridAdapter.EmojiViewHolder> implements PopupWindow.OnDismissListener {
|
||||||
|
|
||||||
|
private final List<Emoji> emojiList;
|
||||||
|
private final EmojiProvider emojiProvider;
|
||||||
|
private final EmojiVariationSelectorPopup popup;
|
||||||
|
private final VariationSelectorListener variationSelectorListener;
|
||||||
|
private final EmojiSelectionListener emojiSelectionListener;
|
||||||
|
|
||||||
|
public EmojiPageViewGridAdapter(@NonNull EmojiProvider emojiProvider,
|
||||||
|
@NonNull EmojiVariationSelectorPopup popup,
|
||||||
|
@NonNull EmojiSelectionListener emojiSelectionListener,
|
||||||
|
@NonNull VariationSelectorListener variationSelectorListener)
|
||||||
|
{
|
||||||
|
this.emojiList = new ArrayList<>();
|
||||||
|
this.emojiProvider = emojiProvider;
|
||||||
|
this.popup = popup;
|
||||||
|
this.emojiSelectionListener = emojiSelectionListener;
|
||||||
|
this.variationSelectorListener = variationSelectorListener;
|
||||||
|
|
||||||
|
popup.setOnDismissListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public EmojiViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
|
||||||
|
return new EmojiViewHolder(LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.emoji_display_item, viewGroup, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull EmojiViewHolder viewHolder, int i) {
|
||||||
|
Emoji emoji = emojiList.get(i);
|
||||||
|
|
||||||
|
Drawable drawable = emojiProvider.getEmojiDrawable(emoji.getValue());
|
||||||
|
|
||||||
|
if (drawable != null) {
|
||||||
|
viewHolder.textView.setVisibility(View.GONE);
|
||||||
|
viewHolder.imageView.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
|
viewHolder.imageView.setImageDrawable(drawable);
|
||||||
|
} else {
|
||||||
|
viewHolder.textView.setVisibility(View.VISIBLE);
|
||||||
|
viewHolder.imageView.setVisibility(View.GONE);
|
||||||
|
|
||||||
|
viewHolder.textView.setEmoji(emoji.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
viewHolder.itemView.setOnClickListener(v -> {
|
||||||
|
emojiSelectionListener.onEmojiSelected(emoji.getValue());
|
||||||
|
});
|
||||||
|
|
||||||
|
if (emoji.getVariations().size() > 1) {
|
||||||
|
viewHolder.itemView.setOnLongClickListener(v -> {
|
||||||
|
popup.dismiss();
|
||||||
|
popup.setVariations(emoji.getVariations());
|
||||||
|
popup.showAsDropDown(viewHolder.itemView, 0, -(2 * viewHolder.itemView.getHeight()));
|
||||||
|
variationSelectorListener.onVariationSelectorStateChanged(true);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
viewHolder.hintCorner.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
viewHolder.itemView.setOnLongClickListener(null);
|
||||||
|
viewHolder.hintCorner.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return emojiList.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEmoji(@NonNull List<Emoji> emojiList) {
|
||||||
|
this.emojiList.clear();
|
||||||
|
this.emojiList.addAll(emojiList);
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDismiss() {
|
||||||
|
variationSelectorListener.onVariationSelectorStateChanged(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static class EmojiViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
|
||||||
|
private final ImageView imageView;
|
||||||
|
private final AsciiEmojiView textView;
|
||||||
|
private final ImageView hintCorner;
|
||||||
|
|
||||||
|
public EmojiViewHolder(@NonNull View itemView) {
|
||||||
|
super(itemView);
|
||||||
|
this.imageView = itemView.findViewById(R.id.emoji_image);
|
||||||
|
this.textView = itemView.findViewById(R.id.emoji_text);
|
||||||
|
this.hintCorner = itemView.findViewById(R.id.emoji_variation_hint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface VariationSelectorListener {
|
||||||
|
void onVariationSelectorStateChanged(boolean open);
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because one or more lines are too long
@ -27,6 +27,7 @@ import org.thoughtcrime.securesms.util.FutureTaskListener;
|
|||||||
import org.thoughtcrime.securesms.util.Util;
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
import org.whispersystems.libsignal.util.Pair;
|
import org.whispersystems.libsignal.util.Pair;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
class EmojiProvider {
|
class EmojiProvider {
|
||||||
@ -64,8 +65,9 @@ class EmojiProvider {
|
|||||||
if (page.hasSpriteMap()) {
|
if (page.hasSpriteMap()) {
|
||||||
EmojiPageBitmap pageBitmap = new EmojiPageBitmap(context, page, decodeScale);
|
EmojiPageBitmap pageBitmap = new EmojiPageBitmap(context, page, decodeScale);
|
||||||
|
|
||||||
for (int i=0;i<page.getEmoji().length;i++) {
|
List<String> emojis = page.getEmoji();
|
||||||
emojiTree.add(page.getEmoji()[i], new EmojiDrawInfo(pageBitmap, i));
|
for (int i = 0; i < emojis.size(); i++) {
|
||||||
|
emojiTree.add(emojis.get(i), new EmojiDrawInfo(pageBitmap, i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,50 @@
|
|||||||
|
package org.thoughtcrime.securesms.components.emoji;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.PopupWindow;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.R;
|
||||||
|
import org.thoughtcrime.securesms.components.emoji.EmojiPageView.EmojiSelectionListener;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class EmojiVariationSelectorPopup extends PopupWindow {
|
||||||
|
|
||||||
|
private final Context context;
|
||||||
|
private final ViewGroup list;
|
||||||
|
private final EmojiSelectionListener listener;
|
||||||
|
|
||||||
|
public EmojiVariationSelectorPopup(@NonNull Context context, @NonNull EmojiSelectionListener listener) {
|
||||||
|
super(context);
|
||||||
|
this.context = context;
|
||||||
|
this.listener = listener;
|
||||||
|
this.list = (ViewGroup) LayoutInflater.from(context).inflate(R.layout.emoji_variation_selector, null);
|
||||||
|
|
||||||
|
setContentView(list);
|
||||||
|
setBackgroundDrawable(null);
|
||||||
|
setOutsideTouchable(true);
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= 21) {
|
||||||
|
setElevation(20);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVariations(List<String> variations) {
|
||||||
|
list.removeAllViews();
|
||||||
|
|
||||||
|
for (String variation : variations) {
|
||||||
|
ImageView imageView = (ImageView) LayoutInflater.from(context).inflate(R.layout.emoji_variation_selector_item, list, false);
|
||||||
|
imageView.setImageDrawable(EmojiProvider.getInstance(context).getEmojiDrawable(variation));
|
||||||
|
imageView.setOnClickListener(v -> {
|
||||||
|
listener.onEmojiSelected(variation);
|
||||||
|
dismiss();
|
||||||
|
});
|
||||||
|
list.addView(imageView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,77 +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.util.AttributeSet;
|
|
||||||
import android.view.View;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.R;
|
|
||||||
import org.thoughtcrime.securesms.util.ResUtil;
|
|
||||||
|
|
||||||
public class EmojiView extends View implements Drawable.Callback {
|
|
||||||
private String emoji;
|
|
||||||
private Drawable drawable;
|
|
||||||
|
|
||||||
private final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
|
|
||||||
|
|
||||||
public EmojiView(Context context) {
|
|
||||||
this(context, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public EmojiView(Context context, AttributeSet attrs) {
|
|
||||||
this(context, attrs, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public EmojiView(Context context, AttributeSet attrs, int defStyleAttr) {
|
|
||||||
super(context, attrs, defStyleAttr);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setEmoji(String emoji) {
|
|
||||||
this.emoji = emoji;
|
|
||||||
this.drawable = EmojiProvider.getInstance(getContext())
|
|
||||||
.getEmojiDrawable(emoji);
|
|
||||||
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);
|
|
||||||
paint.setColor(ResUtil.getColor(getContext(), R.attr.emoji_text_color));
|
|
||||||
paint.setTextAlign(Paint.Align.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();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
|
||||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
|
||||||
}
|
|
||||||
}
|
|
@ -6,6 +6,7 @@ import android.os.AsyncTask;
|
|||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
import com.annimon.stream.Stream;
|
||||||
import com.fasterxml.jackson.databind.type.CollectionType;
|
import com.fasterxml.jackson.databind.type.CollectionType;
|
||||||
import com.fasterxml.jackson.databind.type.TypeFactory;
|
import com.fasterxml.jackson.databind.type.TypeFactory;
|
||||||
|
|
||||||
@ -14,8 +15,11 @@ import org.thoughtcrime.securesms.logging.Log;
|
|||||||
import org.thoughtcrime.securesms.util.JsonUtils;
|
import org.thoughtcrime.securesms.util.JsonUtils;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class RecentEmojiPageModel implements EmojiPageModel {
|
public class RecentEmojiPageModel implements EmojiPageModel {
|
||||||
private static final String TAG = RecentEmojiPageModel.class.getSimpleName();
|
private static final String TAG = RecentEmojiPageModel.class.getSimpleName();
|
||||||
@ -46,12 +50,14 @@ public class RecentEmojiPageModel implements EmojiPageModel {
|
|||||||
return R.attr.emoji_category_recent;
|
return R.attr.emoji_category_recent;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public String[] getEmoji() {
|
@Override public List<String> getEmoji() {
|
||||||
return toReversePrimitiveArray(recentlyUsed);
|
List<String> emoji = new ArrayList<>(recentlyUsed);
|
||||||
|
Collections.reverse(emoji);
|
||||||
|
return emoji;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public String[] getDisplayEmoji() {
|
@Override public List<Emoji> getDisplayEmoji() {
|
||||||
return getEmoji();
|
return Stream.of(getEmoji()).map(Emoji::new).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public boolean hasSpriteMap() {
|
@Override public boolean hasSpriteMap() {
|
||||||
|
@ -4,20 +4,30 @@ import android.support.annotation.AttrRes;
|
|||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
public class StaticEmojiPageModel implements EmojiPageModel {
|
import java.util.ArrayList;
|
||||||
@AttrRes private final int iconAttr;
|
import java.util.Arrays;
|
||||||
@NonNull private final String[] emoji;
|
import java.util.LinkedList;
|
||||||
@NonNull private final String[] displayEmoji;
|
import java.util.List;
|
||||||
@Nullable private final String sprite;
|
|
||||||
|
|
||||||
public StaticEmojiPageModel(@AttrRes int iconAttr, @NonNull String[] emoji, @Nullable String sprite) {
|
public class StaticEmojiPageModel implements EmojiPageModel {
|
||||||
this(iconAttr, emoji, emoji, sprite);
|
@AttrRes private final int iconAttr;
|
||||||
|
@NonNull private final List<Emoji> emoji;
|
||||||
|
@Nullable private final String sprite;
|
||||||
|
|
||||||
|
public StaticEmojiPageModel(@AttrRes int iconAttr, @NonNull String[] strings, @Nullable String sprite) {
|
||||||
|
List<Emoji> emoji = new ArrayList<>(strings.length);
|
||||||
|
for (String s : strings) {
|
||||||
|
emoji.add(new Emoji(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.iconAttr = iconAttr;
|
||||||
|
this.emoji = emoji;
|
||||||
|
this.sprite = sprite;
|
||||||
}
|
}
|
||||||
|
|
||||||
public StaticEmojiPageModel(@AttrRes int iconAttr, @NonNull String[] emoji, @NonNull String[] displayEmoji, @Nullable String sprite) {
|
public StaticEmojiPageModel(@AttrRes int iconAttr, @NonNull Emoji[] emoji, @Nullable String sprite) {
|
||||||
this.iconAttr = iconAttr;
|
this.iconAttr = iconAttr;
|
||||||
this.emoji = emoji;
|
this.emoji = Arrays.asList(emoji);
|
||||||
this.displayEmoji = displayEmoji;
|
|
||||||
this.sprite = sprite;
|
this.sprite = sprite;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,13 +36,17 @@ public class StaticEmojiPageModel implements EmojiPageModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NonNull String[] getEmoji() {
|
public @NonNull List<String> getEmoji() {
|
||||||
return emoji;
|
List<String> emojis = new LinkedList<>();
|
||||||
|
for (Emoji e : emoji) {
|
||||||
|
emojis.addAll(e.getVariations());
|
||||||
|
}
|
||||||
|
return emojis;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NonNull String[] getDisplayEmoji() {
|
public @NonNull List<Emoji> getDisplayEmoji() {
|
||||||
return displayEmoji;
|
return emoji;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
Loading…
x
Reference in New Issue
Block a user