mirror of
https://github.com/oxen-io/session-android.git
synced 2024-12-24 00:37:47 +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>
|
||||
|
||||
<android.support.v4.view.ViewPager
|
||||
<org.thoughtcrime.securesms.components.ControllableViewPager
|
||||
android:id="@+id/emoji_pager"
|
||||
android:visibility="visible"
|
||||
android:layout_width="match_parent"
|
||||
|
@ -4,15 +4,11 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<GridView android:id="@+id/emoji"
|
||||
android:visibility="visible"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:stretchMode="columnWidth"
|
||||
android:columnWidth="@dimen/emoji_drawer_size"
|
||||
android:horizontalSpacing="0dp"
|
||||
android:verticalSpacing="0dp"
|
||||
android:numColumns="auto_fit"
|
||||
android:gravity="center"/>
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:id="@+id/emoji"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:paddingBottom="8dp"/>
|
||||
|
||||
</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_indicator_height">1.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_date_text_size">12sp</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.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.view.PagerAdapter;
|
||||
import android.support.v4.view.ViewPager;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiPageViewGridAdapter.VariationSelectorListener;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
@ -27,7 +28,7 @@ import org.thoughtcrime.securesms.util.ResUtil;
|
||||
import java.util.LinkedList;
|
||||
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();
|
||||
|
||||
@ -101,19 +102,23 @@ public class EmojiDrawer extends LinearLayout implements InputView {
|
||||
Log.i(TAG, "hide()");
|
||||
}
|
||||
|
||||
private void initializeEmojiGrid() {
|
||||
pager.setAdapter(new EmojiPagerAdapter(getContext(),
|
||||
models,
|
||||
new EmojiSelectionListener() {
|
||||
@Override
|
||||
public void onEmojiSelected(String emoji) {
|
||||
Log.i(TAG, "onEmojiSelected()");
|
||||
recentModel.onCodePointSelected(emoji);
|
||||
if (listener != null) listener.onEmojiSelected(emoji);
|
||||
}
|
||||
}));
|
||||
@Override
|
||||
public void onEmojiSelected(String emoji) {
|
||||
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);
|
||||
}
|
||||
strip.setViewPager(pager);
|
||||
@ -129,18 +134,21 @@ public class EmojiDrawer extends LinearLayout implements InputView {
|
||||
public static class EmojiPagerAdapter extends PagerAdapter
|
||||
implements PagerSlidingTabStrip.CustomTabProvider
|
||||
{
|
||||
private Context context;
|
||||
private List<EmojiPageModel> pages;
|
||||
private EmojiSelectionListener listener;
|
||||
private Context context;
|
||||
private List<EmojiPageModel> pages;
|
||||
private EmojiSelectionListener emojiSelectionListener;
|
||||
private VariationSelectorListener variationSelectorListener;
|
||||
|
||||
public EmojiPagerAdapter(@NonNull Context context,
|
||||
@NonNull List<EmojiPageModel> pages,
|
||||
@Nullable EmojiSelectionListener listener)
|
||||
@NonNull EmojiSelectionListener emojiSelectionListener,
|
||||
@NonNull VariationSelectorListener variationSelectorListener)
|
||||
{
|
||||
super();
|
||||
this.context = context;
|
||||
this.pages = pages;
|
||||
this.listener = listener;
|
||||
this.context = context;
|
||||
this.pages = pages;
|
||||
this.emojiSelectionListener = emojiSelectionListener;
|
||||
this.variationSelectorListener = variationSelectorListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -149,10 +157,9 @@ public class EmojiDrawer extends LinearLayout implements InputView {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object instantiateItem(ViewGroup container, int position) {
|
||||
EmojiPageView page = new EmojiPageView(context);
|
||||
public @NonNull Object instantiateItem(@NonNull ViewGroup container, int position) {
|
||||
EmojiPageView page = new EmojiPageView(context, emojiSelectionListener, variationSelectorListener);
|
||||
page.setModel(pages.get(position));
|
||||
page.setEmojiSelectedListener(listener);
|
||||
container.addView(page);
|
||||
return page;
|
||||
}
|
||||
|
@ -1,9 +1,11 @@
|
||||
package org.thoughtcrime.securesms.components.emoji;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface EmojiPageModel {
|
||||
int getIconAttr();
|
||||
String[] getEmoji();
|
||||
String[] getDisplayEmoji();
|
||||
List<String> getEmoji();
|
||||
List<Emoji> getDisplayEmoji();
|
||||
boolean hasSpriteMap();
|
||||
String getSprite();
|
||||
boolean isDynamic();
|
||||
|
@ -1,106 +1,92 @@
|
||||
package org.thoughtcrime.securesms.components.emoji;
|
||||
|
||||
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.MotionEvent;
|
||||
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.GridView;
|
||||
|
||||
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 EmojiPageModel model;
|
||||
private EmojiSelectionListener listener;
|
||||
private GridView grid;
|
||||
private EmojiPageModel model;
|
||||
private EmojiPageViewGridAdapter adapter;
|
||||
private RecyclerView recyclerView;
|
||||
private GridLayoutManager layoutManager;
|
||||
private RecyclerView.OnItemTouchListener scrollDisabler;
|
||||
private VariationSelectorListener variationSelectorListener;
|
||||
|
||||
public EmojiPageView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public EmojiPageView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public EmojiPageView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
public EmojiPageView(@NonNull Context context,
|
||||
@NonNull EmojiSelectionListener emojiSelectionListener,
|
||||
@NonNull VariationSelectorListener variationSelectorListener)
|
||||
{
|
||||
super(context);
|
||||
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));
|
||||
grid.setOnItemClickListener(new OnItemClickListener() {
|
||||
@Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
if (listener != null) listener.onEmojiSelected(((EmojiView)view).getEmoji());
|
||||
}
|
||||
});
|
||||
|
||||
this.variationSelectorListener = variationSelectorListener;
|
||||
|
||||
recyclerView = view.findViewById(R.id.emoji);
|
||||
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() {
|
||||
if (model.isDynamic() && grid != null && grid.getAdapter() != null) {
|
||||
((EmojiGridAdapter)grid.getAdapter()).notifyDataSetChanged();
|
||||
if (model.isDynamic() && adapter != null) {
|
||||
adapter.notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public void setModel(EmojiPageModel model) {
|
||||
this.model = model;
|
||||
grid.setAdapter(new EmojiGridAdapter(getContext(), model));
|
||||
adapter.setEmoji(model.getDisplayEmoji());
|
||||
}
|
||||
|
||||
public void setEmojiSelectedListener(EmojiSelectionListener listener) {
|
||||
this.listener = listener;
|
||||
@Override
|
||||
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 {
|
||||
|
||||
protected final Context context;
|
||||
private final int emojiSize;
|
||||
private final EmojiPageModel model;
|
||||
|
||||
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 void onVariationSelectorStateChanged(boolean open) {
|
||||
if (open) {
|
||||
recyclerView.addOnItemTouchListener(scrollDisabler);
|
||||
} else {
|
||||
post(() -> recyclerView.removeOnItemTouchListener(scrollDisabler));
|
||||
}
|
||||
|
||||
@Override public int getCount() {
|
||||
return model.getDisplayEmoji().length;
|
||||
}
|
||||
|
||||
@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;
|
||||
if (variationSelectorListener != null) {
|
||||
variationSelectorListener.onVariationSelectorStateChanged(open);
|
||||
}
|
||||
}
|
||||
|
||||
public interface EmojiSelectionListener {
|
||||
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.whispersystems.libsignal.util.Pair;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
class EmojiProvider {
|
||||
@ -64,8 +65,9 @@ class EmojiProvider {
|
||||
if (page.hasSpriteMap()) {
|
||||
EmojiPageBitmap pageBitmap = new EmojiPageBitmap(context, page, decodeScale);
|
||||
|
||||
for (int i=0;i<page.getEmoji().length;i++) {
|
||||
emojiTree.add(page.getEmoji()[i], new EmojiDrawInfo(pageBitmap, i));
|
||||
List<String> emojis = page.getEmoji();
|
||||
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.support.annotation.NonNull;
|
||||
|
||||
import com.annimon.stream.Stream;
|
||||
import com.fasterxml.jackson.databind.type.CollectionType;
|
||||
import com.fasterxml.jackson.databind.type.TypeFactory;
|
||||
|
||||
@ -14,8 +15,11 @@ import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.util.JsonUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
|
||||
public class RecentEmojiPageModel implements EmojiPageModel {
|
||||
private static final String TAG = RecentEmojiPageModel.class.getSimpleName();
|
||||
@ -46,12 +50,14 @@ public class RecentEmojiPageModel implements EmojiPageModel {
|
||||
return R.attr.emoji_category_recent;
|
||||
}
|
||||
|
||||
@Override public String[] getEmoji() {
|
||||
return toReversePrimitiveArray(recentlyUsed);
|
||||
@Override public List<String> getEmoji() {
|
||||
List<String> emoji = new ArrayList<>(recentlyUsed);
|
||||
Collections.reverse(emoji);
|
||||
return emoji;
|
||||
}
|
||||
|
||||
@Override public String[] getDisplayEmoji() {
|
||||
return getEmoji();
|
||||
@Override public List<Emoji> getDisplayEmoji() {
|
||||
return Stream.of(getEmoji()).map(Emoji::new).toList();
|
||||
}
|
||||
|
||||
@Override public boolean hasSpriteMap() {
|
||||
|
@ -4,20 +4,30 @@ import android.support.annotation.AttrRes;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
public class StaticEmojiPageModel implements EmojiPageModel {
|
||||
@AttrRes private final int iconAttr;
|
||||
@NonNull private final String[] emoji;
|
||||
@NonNull private final String[] displayEmoji;
|
||||
@Nullable private final String sprite;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public StaticEmojiPageModel(@AttrRes int iconAttr, @NonNull String[] emoji, @Nullable String sprite) {
|
||||
this(iconAttr, emoji, emoji, sprite);
|
||||
public class StaticEmojiPageModel implements EmojiPageModel {
|
||||
@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.emoji = emoji;
|
||||
this.displayEmoji = displayEmoji;
|
||||
this.emoji = Arrays.asList(emoji);
|
||||
this.sprite = sprite;
|
||||
}
|
||||
|
||||
@ -26,13 +36,17 @@ public class StaticEmojiPageModel implements EmojiPageModel {
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull String[] getEmoji() {
|
||||
return emoji;
|
||||
public @NonNull List<String> getEmoji() {
|
||||
List<String> emojis = new LinkedList<>();
|
||||
for (Emoji e : emoji) {
|
||||
emojis.addAll(e.getVariations());
|
||||
}
|
||||
return emojis;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull String[] getDisplayEmoji() {
|
||||
return displayEmoji;
|
||||
public @NonNull List<Emoji> getDisplayEmoji() {
|
||||
return emoji;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
Loading…
x
Reference in New Issue
Block a user