Allow for switching between emoji categories using swipe left/right (#1488)

This commit is contained in:
Eran Leshem 2025-05-18 20:18:23 +03:00 committed by GitHub
parent 199f177c2d
commit 731c6cdd5e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 217 additions and 252 deletions

View file

@ -107,6 +107,7 @@ dependencies {
implementation("androidx.core:core-ktx:1.16.0") implementation("androidx.core:core-ktx:1.16.0")
implementation("androidx.recyclerview:recyclerview:1.4.0") implementation("androidx.recyclerview:recyclerview:1.4.0")
implementation("androidx.autofill:autofill:1.1.0") implementation("androidx.autofill:autofill:1.1.0")
implementation("androidx.viewpager2:viewpager2:1.1.0")
// kotlin // kotlin
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.8.1") implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.8.1")

View file

@ -479,7 +479,6 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
if (mKeyboardViewWrapper.getOneHandedModeEnabled() == enabled) { if (mKeyboardViewWrapper.getOneHandedModeEnabled() == enabled) {
return; return;
} }
mEmojiPalettesView.clearKeyboardCache();
final Settings settings = Settings.getInstance(); final Settings settings = Settings.getInstance();
mKeyboardViewWrapper.setOneHandedModeEnabled(enabled); mKeyboardViewWrapper.setOneHandedModeEnabled(enabled);
mKeyboardViewWrapper.setOneHandedGravity(settings.getCurrent().mOneHandedModeGravity); mKeyboardViewWrapper.setOneHandedGravity(settings.getCurrent().mOneHandedModeGravity);
@ -645,6 +644,12 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
} }
} }
public void trimMemory() {
if (mEmojiPalettesView != null) {
mEmojiPalettesView.clearKeyboardCache();
}
}
@SuppressLint("InflateParams") @SuppressLint("InflateParams")
public View onCreateInputView(@NonNull Context displayContext, final boolean isHardwareAcceleratedDrawingEnabled) { public View onCreateInputView(@NonNull Context displayContext, final boolean isHardwareAcceleratedDrawingEnabled) {
if (mKeyboardView != null) { if (mKeyboardView != null) {

View file

@ -261,20 +261,6 @@ final class EmojiCategory {
return 0; return 0;
} }
// Returns the view pager's page position for the categoryId
public int getPagerPageIdFromCategoryAndPageId(final int categoryId, final int categoryPageId) {
int sum = 0;
for (int i = 0; i < mShownCategories.size(); ++i) {
final CategoryProperties props = mShownCategories.get(i);
if (props.mCategoryId == categoryId) {
return sum + categoryPageId;
}
sum += props.getPageCount();
}
Log.w(TAG, "categoryId not found: " + categoryId);
return 0;
}
public int getRecentTabId() { public int getRecentTabId() {
return getTabIdFromCategoryId(EmojiCategory.ID_RECENTS); return getTabIdFromCategoryId(EmojiCategory.ID_RECENTS);
} }
@ -285,11 +271,11 @@ final class EmojiCategory {
} }
// Returns a keyboard from the recycler view's adapter position. // Returns a keyboard from the recycler view's adapter position.
public DynamicGridKeyboard getKeyboardFromAdapterPosition(final int position) { public DynamicGridKeyboard getKeyboardFromAdapterPosition(int categoryId, final int position) {
if (position >= 0 && position < getCurrentCategoryPageCount()) { if (position >= 0 && position < getCategoryPageCount(categoryId)) {
return getKeyboard(mCurrentCategoryId, position); return getKeyboard(categoryId, position);
} }
Log.w(TAG, "invalid position for categoryId : " + mCurrentCategoryId); Log.w(TAG, "invalid position for categoryId : " + categoryId);
return null; return null;
} }

View file

@ -8,7 +8,7 @@ package helium314.keyboard.keyboard.emoji
import android.content.res.Resources import android.content.res.Resources
import android.view.View import android.view.View
import android.widget.LinearLayout import android.widget.LinearLayout
import androidx.recyclerview.widget.RecyclerView import androidx.viewpager2.widget.ViewPager2
import helium314.keyboard.keyboard.internal.KeyboardParams import helium314.keyboard.keyboard.internal.KeyboardParams
import helium314.keyboard.latin.R import helium314.keyboard.latin.R
import helium314.keyboard.latin.settings.Settings import helium314.keyboard.latin.settings.Settings
@ -47,7 +47,7 @@ internal class EmojiLayoutParams(res: Resources) {
emojiKeyboardHeight = emojiListHeight - emojiCategoryPageIdViewHeight - emojiListBottomMargin emojiKeyboardHeight = emojiListHeight - emojiCategoryPageIdViewHeight - emojiListBottomMargin
} }
fun setEmojiListProperties(vp: RecyclerView) { fun setEmojiListProperties(vp: ViewPager2) {
val lp = vp.layoutParams as LinearLayout.LayoutParams val lp = vp.layoutParams as LinearLayout.LayoutParams
lp.height = emojiKeyboardHeight lp.height = emojiKeyboardHeight
lp.bottomMargin = emojiListBottomMargin lp.bottomMargin = emojiListBottomMargin

View file

@ -12,156 +12,32 @@ import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import helium314.keyboard.keyboard.Key;
import helium314.keyboard.keyboard.Keyboard; import helium314.keyboard.keyboard.Keyboard;
import helium314.keyboard.keyboard.KeyboardView;
import helium314.keyboard.latin.R; import helium314.keyboard.latin.R;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import helium314.keyboard.latin.settings.Settings;
final class EmojiPalettesAdapter extends RecyclerView.Adapter<EmojiPalettesAdapter.ViewHolder>{ final class EmojiPalettesAdapter extends RecyclerView.Adapter<EmojiPalettesAdapter.ViewHolder>{
private static final String TAG = EmojiPalettesAdapter.class.getSimpleName(); private static final String TAG = EmojiPalettesAdapter.class.getSimpleName();
private static final boolean DEBUG_PAGER = false; private static final boolean DEBUG_PAGER = false;
private final int mCategoryId;
private final OnKeyEventListener mListener; private final OnKeyEventListener mListener;
private final DynamicGridKeyboard mRecentsKeyboard;
private final SparseArray<EmojiPageKeyboardView> mActiveKeyboardViews = new SparseArray<>();
private final EmojiCategory mEmojiCategory; private final EmojiCategory mEmojiCategory;
private int mActivePosition = 0;
public EmojiPalettesAdapter(final EmojiCategory emojiCategory, public EmojiPalettesAdapter(final EmojiCategory emojiCategory, int categoryId, final OnKeyEventListener listener) {
final OnKeyEventListener listener) {
mEmojiCategory = emojiCategory; mEmojiCategory = emojiCategory;
mCategoryId = categoryId;
mListener = listener; mListener = listener;
mRecentsKeyboard = mEmojiCategory.getKeyboard(EmojiCategory.ID_RECENTS, 0);
} }
public void flushPendingRecentKeys() {
mRecentsKeyboard.flushPendingRecentKeys();
final KeyboardView recentKeyboardView =
mActiveKeyboardViews.get(mEmojiCategory.getRecentTabId());
if (recentKeyboardView != null) {
recentKeyboardView.invalidateAllKeys();
}
}
public void addRecentKey(final Key key) {
if (Settings.getValues().mIncognitoModeEnabled) {
// We do not want to log recent keys while being in incognito
return;
}
if (mEmojiCategory.isInRecentTab()) {
mRecentsKeyboard.addPendingKey(key);
return;
}
mRecentsKeyboard.addKeyFirst(key);
final KeyboardView recentKeyboardView =
mActiveKeyboardViews.get(mEmojiCategory.getRecentTabId());
if (recentKeyboardView != null) {
recentKeyboardView.invalidateAllKeys();
}
}
public void onPageScrolled() {
releaseCurrentKey(false /* withKeyRegistering */);
}
public void releaseCurrentKey(final boolean withKeyRegistering) {
// Make sure the delayed key-down event (highlight effect and haptic feedback) will be
// canceled.
final EmojiPageKeyboardView currentKeyboardView =
mActiveKeyboardViews.get(mActivePosition);
if (currentKeyboardView == null) {
return;
}
currentKeyboardView.releaseCurrentKey(withKeyRegistering);
}
/*
@Override
public Object instantiateItem(final ViewGroup container, final int position) {
if (DEBUG_PAGER) {
Log.d(TAG, "instantiate item: " + position);
}
final EmojiPageKeyboardView oldKeyboardView = mActiveKeyboardViews.get(position);
if (oldKeyboardView != null) {
oldKeyboardView.deallocateMemory();
// This may be redundant but wanted to be safer..
mActiveKeyboardViews.remove(position);
}
final Keyboard keyboard =
mEmojiCategory.getKeyboardFromPagePosition(position);
final LayoutInflater inflater = LayoutInflater.from(container.getContext());
final EmojiPageKeyboardView keyboardView = (EmojiPageKeyboardView) inflater.inflate(
R.layout.emoji_keyboard_page, container, false);
keyboardView.setKeyboard(keyboard);
keyboardView.setOnKeyEventListener(mListener);
container.addView(keyboardView);
mActiveKeyboardViews.put(position, keyboardView);
return keyboardView;
}
@Override
public void setPrimaryItem(final ViewGroup container, final int position,
final Object object) {
if (mActivePosition == position) {
return;
}
final EmojiPageKeyboardView oldKeyboardView = mActiveKeyboardViews.get(mActivePosition);
if (oldKeyboardView != null) {
oldKeyboardView.releaseCurrentKey(false);
oldKeyboardView.deallocateMemory();
}
mActivePosition = position;
}
@Override
public boolean isViewFromObject(final View view, final Object object) {
return view == object;
}
@Override
public void destroyItem(final ViewGroup container, final int position,
final Object object) {
if (DEBUG_PAGER) {
Log.d(TAG, "destroy item: " + position + ", " + object.getClass().getSimpleName());
}
final EmojiPageKeyboardView keyboardView = mActiveKeyboardViews.get(position);
if (keyboardView != null) {
keyboardView.deallocateMemory();
mActiveKeyboardViews.remove(position);
}
if (object instanceof View) {
container.removeView((View)object);
} else {
Log.w(TAG, "Warning!!! Emoji palette may be leaking. " + object);
}
}
*/
@NonNull @NonNull
@Override @Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
/*if (DEBUG_PAGER) {
Log.d(TAG, "instantiate item: " + viewType);
}
final EmojiPageKeyboardView oldKeyboardView = mActiveKeyboardViews.get(viewType);
if (oldKeyboardView != null) {
oldKeyboardView.deallocateMemory();
// This may be redundant but wanted to be safer..
mActiveKeyboardViews.remove(viewType);
}
final Keyboard keyboard =
mEmojiCategory.getKeyboardFromPagePosition(parent.getVerticalScrollbarPosition());*/
final LayoutInflater inflater = LayoutInflater.from(parent.getContext()); final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
final EmojiPageKeyboardView keyboardView = (EmojiPageKeyboardView)inflater.inflate( final EmojiPageKeyboardView keyboardView = (EmojiPageKeyboardView)inflater.inflate(
R.layout.emoji_keyboard_page, parent, false); R.layout.emoji_keyboard_page, parent, false);
/*keyboardView.setKeyboard(keyboard);
keyboardView.setOnKeyEventListener(mListener);
parent.addView(keyboardView);
mActiveKeyboardViews.put(parent.getVerticalScrollbarPosition(), keyboardView);*/
return new ViewHolder(keyboardView); return new ViewHolder(keyboardView);
} }
@ -170,33 +46,22 @@ final class EmojiPalettesAdapter extends RecyclerView.Adapter<EmojiPalettesAdapt
if (DEBUG_PAGER) { if (DEBUG_PAGER) {
Log.d(TAG, "instantiate item: " + position); Log.d(TAG, "instantiate item: " + position);
} }
final EmojiPageKeyboardView oldKeyboardView = mActiveKeyboardViews.get(position);
if (oldKeyboardView != null) {
oldKeyboardView.deallocateMemory();
// This may be redundant but wanted to be safer..
mActiveKeyboardViews.remove(position);
}
final Keyboard keyboard = final Keyboard keyboard =
mEmojiCategory.getKeyboardFromAdapterPosition(position); mEmojiCategory.getKeyboardFromAdapterPosition(mCategoryId, position);
holder.getKeyboardView().setKeyboard(keyboard); holder.getKeyboardView().setKeyboard(keyboard);
holder.getKeyboardView().setOnKeyEventListener(mListener); holder.getKeyboardView().setOnKeyEventListener(mListener);
//parent.addView(keyboardView); }
mActiveKeyboardViews.put(position, holder.getKeyboardView());
/*if (mActivePosition == position) { @Override
return; public void onViewDetachedFromWindow(@NonNull ViewHolder holder) {
} holder.getKeyboardView().releaseCurrentKey(false);
final EmojiPageKeyboardView oldKeyboardView = mActiveKeyboardViews.get(mActivePosition); holder.getKeyboardView().deallocateMemory();
if (oldKeyboardView != null) {
oldKeyboardView.releaseCurrentKey(false);
oldKeyboardView.deallocateMemory();
}
mActivePosition = position;*/
} }
@Override @Override
public int getItemCount() { public int getItemCount() {
return mEmojiCategory.getCurrentCategoryPageCount(); return mEmojiCategory.getCategoryPageCount(mCategoryId);
} }
static class ViewHolder extends RecyclerView.ViewHolder { static class ViewHolder extends RecyclerView.ViewHolder {
@ -210,7 +75,6 @@ final class EmojiPalettesAdapter extends RecyclerView.Adapter<EmojiPalettesAdapt
public EmojiPageKeyboardView getKeyboardView() { public EmojiPageKeyboardView getKeyboardView() {
return customView; return customView;
} }
} }
} }

View file

@ -11,6 +11,7 @@ import android.content.Context;
import android.content.res.Resources; import android.content.res.Resources;
import android.content.res.TypedArray; import android.content.res.TypedArray;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo; import android.view.inputmethod.EditorInfo;
@ -20,6 +21,7 @@ import android.widget.LinearLayout;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import androidx.viewpager2.widget.ViewPager2;
import helium314.keyboard.keyboard.Key; import helium314.keyboard.keyboard.Key;
import helium314.keyboard.keyboard.Keyboard; import helium314.keyboard.keyboard.Keyboard;
import helium314.keyboard.keyboard.KeyboardActionListener; import helium314.keyboard.keyboard.KeyboardActionListener;
@ -41,8 +43,6 @@ import helium314.keyboard.latin.settings.Settings;
import helium314.keyboard.latin.settings.SettingsValues; import helium314.keyboard.latin.settings.SettingsValues;
import helium314.keyboard.latin.utils.ResourceUtils; import helium314.keyboard.latin.utils.ResourceUtils;
import org.jetbrains.annotations.NotNull;
import static helium314.keyboard.latin.common.Constants.NOT_A_COORDINATE; import static helium314.keyboard.latin.common.Constants.NOT_A_COORDINATE;
/** /**
@ -58,20 +58,132 @@ import static helium314.keyboard.latin.common.Constants.NOT_A_COORDINATE;
*/ */
public final class EmojiPalettesView extends LinearLayout public final class EmojiPalettesView extends LinearLayout
implements View.OnClickListener, OnKeyEventListener { implements View.OnClickListener, OnKeyEventListener {
private static final class PagerViewHolder extends RecyclerView.ViewHolder {
private long mCategoryId;
private PagerViewHolder(View itemView) {
super(itemView);
}
}
private final class PagerAdapter extends RecyclerView.Adapter<PagerViewHolder> {
private boolean mInitialized;
private PagerAdapter() {
setHasStableIds(true);
}
@Override
public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
recyclerView.setItemViewCacheSize(mEmojiCategory.getShownCategories().size());
}
@NonNull
@Override
public PagerViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
var view = LayoutInflater.from(parent.getContext()).inflate(R.layout.emoji_category_view, parent, false);
var viewHolder = new PagerViewHolder(view);
var emojiRecyclerView = getRecyclerView(view);
emojiRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
// Ignore this message. Only want the actual page selected.
}
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
updateState(recyclerView, viewHolder);
}
});
emojiRecyclerView.setPersistentDrawingCache(PERSISTENT_NO_CACHE);
return viewHolder;
}
@Override
public void onBindViewHolder(PagerViewHolder holder, int position) {
holder.mCategoryId = getItemId(position);
var recyclerView = getRecyclerView(holder.itemView);
recyclerView.setAdapter(new EmojiPalettesAdapter(mEmojiCategory, (int) holder.mCategoryId,
EmojiPalettesView.this));
if (! mInitialized) {
recyclerView.scrollToPosition(mEmojiCategory.getCurrentCategoryPageId());
mInitialized = true;
}
}
@Override
public int getItemCount() {
return mEmojiCategory.getShownCategories().size();
}
@Override
public void onViewAttachedToWindow(PagerViewHolder holder) {
if (mPager.getScrollState() == ViewPager2.SCROLL_STATE_DRAGGING // swipe
|| holder.getBindingAdapterPosition() == mPager.getCurrentItem() // tab
) {
setCurrentCategoryId((int) getItemId(holder.getBindingAdapterPosition()), false);
updateState(getRecyclerView(holder.itemView), holder);
}
}
@Override
public void onViewDetachedFromWindow(PagerViewHolder holder) {
if (holder.mCategoryId == EmojiCategory.ID_RECENTS) {
// Needs to save pending updates for recent keys when we get out of the recents
// category because we don't want to move the recent emojis around while the user
// is in the recents category.
getRecentsKeyboard().flushPendingRecentKeys();
getRecyclerView(holder.itemView).getAdapter().notifyDataSetChanged();
}
}
@Override
public long getItemId(int position) {
return mEmojiCategory.getShownCategories().get(position).mCategoryId;
}
private static RecyclerView getRecyclerView(View view) {
return view.findViewById(R.id.emoji_keyboard_list);
}
private void updateState(@NonNull RecyclerView recyclerView, PagerViewHolder viewHolder) {
if (viewHolder.mCategoryId != mEmojiCategory.getCurrentCategoryId()) {
return;
}
final int offset = recyclerView.computeVerticalScrollOffset();
final int extent = recyclerView.computeVerticalScrollExtent();
final int range = recyclerView.computeVerticalScrollRange();
final float percentage = offset / (float) (range - extent);
final int currentCategorySize = mEmojiCategory.getCurrentCategoryPageCount();
final int a = (int) (percentage * currentCategorySize);
final float b = percentage * currentCategorySize - a;
mEmojiCategoryPageIndicatorView.setCategoryPageId(currentCategorySize, a, b);
LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
final int firstCompleteVisibleBoard = layoutManager.findFirstCompletelyVisibleItemPosition();
final int firstVisibleBoard = layoutManager.findFirstVisibleItemPosition();
mEmojiCategory.setCurrentCategoryPageId(
firstCompleteVisibleBoard > 0 ? firstCompleteVisibleBoard : firstVisibleBoard);
}
}
private boolean initialized = false; private boolean initialized = false;
private final Colors mColors; private final Colors mColors;
private EmojiPalettesAdapter mEmojiPalettesAdapter;
private final EmojiLayoutParams mEmojiLayoutParams; private final EmojiLayoutParams mEmojiLayoutParams;
private final LinearLayoutManager mEmojiLayoutManager;
private LinearLayout mTabStrip; private LinearLayout mTabStrip;
private RecyclerView mEmojiRecyclerView;
private EmojiCategoryPageIndicatorView mEmojiCategoryPageIndicatorView; private EmojiCategoryPageIndicatorView mEmojiCategoryPageIndicatorView;
private KeyboardActionListener mKeyboardActionListener = KeyboardActionListener.EMPTY_LISTENER; private KeyboardActionListener mKeyboardActionListener = KeyboardActionListener.EMPTY_LISTENER;
private final EmojiCategory mEmojiCategory; private final EmojiCategory mEmojiCategory;
private ViewPager2 mPager;
public EmojiPalettesView(final Context context, final AttributeSet attrs) { public EmojiPalettesView(final Context context, final AttributeSet attrs) {
this(context, attrs, R.attr.emojiPalettesViewStyle); this(context, attrs, R.attr.emojiPalettesViewStyle);
@ -91,7 +203,6 @@ public final class EmojiPalettesView extends LinearLayout
R.styleable.EmojiPalettesView, defStyle, R.style.EmojiPalettesView); R.styleable.EmojiPalettesView, defStyle, R.style.EmojiPalettesView);
mEmojiCategory = new EmojiCategory(context, layoutSet, emojiPalettesViewAttr); mEmojiCategory = new EmojiCategory(context, layoutSet, emojiPalettesViewAttr);
emojiPalettesViewAttr.recycle(); emojiPalettesViewAttr.recycle();
mEmojiLayoutManager = new LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false);
setFitsSystemWindows(true); setFitsSystemWindows(true);
} }
@ -129,47 +240,14 @@ public final class EmojiPalettesView extends LinearLayout
for (final EmojiCategory.CategoryProperties properties : mEmojiCategory.getShownCategories()) { for (final EmojiCategory.CategoryProperties properties : mEmojiCategory.getShownCategories()) {
addTab(mTabStrip, properties.mCategoryId); addTab(mTabStrip, properties.mCategoryId);
} }
mEmojiPalettesAdapter = new EmojiPalettesAdapter(mEmojiCategory, this);
mEmojiRecyclerView = findViewById(R.id.emoji_keyboard_list);
mEmojiRecyclerView.setLayoutManager(mEmojiLayoutManager);
mEmojiRecyclerView.setAdapter(mEmojiPalettesAdapter);
mEmojiRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(@NonNull @NotNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
// Ignore this message. Only want the actual page selected.
}
@Override
public void onScrolled(@NonNull @NotNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
mEmojiPalettesAdapter.onPageScrolled();
final int offset = recyclerView.computeVerticalScrollOffset();
final int extent = recyclerView.computeVerticalScrollExtent();
final int range = recyclerView.computeVerticalScrollRange();
final float percentage = offset / (float) (range - extent);
final int currentCategorySize = mEmojiCategory.getCurrentCategoryPageCount();
final int a = (int) (percentage * currentCategorySize);
final float b = percentage * currentCategorySize - a;
mEmojiCategoryPageIndicatorView.setCategoryPageId(currentCategorySize, a, b);
final int firstCompleteVisibleBoard = mEmojiLayoutManager.findFirstCompletelyVisibleItemPosition();
final int firstVisibleBoard = mEmojiLayoutManager.findFirstVisibleItemPosition();
mEmojiCategory.setCurrentCategoryPageId(
firstCompleteVisibleBoard > 0 ? firstCompleteVisibleBoard : firstVisibleBoard);
}
});
mEmojiRecyclerView.setPersistentDrawingCache(PERSISTENT_NO_CACHE);
mEmojiLayoutParams.setEmojiListProperties(mEmojiRecyclerView);
mPager = findViewById(R.id.emoji_pager);
mPager.setAdapter(new PagerAdapter());
mEmojiLayoutParams.setEmojiListProperties(mPager);
mEmojiCategoryPageIndicatorView = findViewById(R.id.emoji_category_page_id_view); mEmojiCategoryPageIndicatorView = findViewById(R.id.emoji_category_page_id_view);
mEmojiLayoutParams.setCategoryPageIdViewProperties(mEmojiCategoryPageIndicatorView); mEmojiLayoutParams.setCategoryPageIdViewProperties(mEmojiCategoryPageIndicatorView);
setCurrentCategoryAndPageId(mEmojiCategory.getCurrentCategoryId(), mEmojiCategory.getCurrentCategoryPageId(), true); setCurrentCategoryId(mEmojiCategory.getCurrentCategoryId(), true);
mEmojiCategoryPageIndicatorView.setColors(mColors.get(ColorType.EMOJI_CATEGORY_SELECTED), mColors.get(ColorType.STRIP_BACKGROUND)); mEmojiCategoryPageIndicatorView.setColors(mColors.get(ColorType.EMOJI_CATEGORY_SELECTED), mColors.get(ColorType.STRIP_BACKGROUND));
initialized = true; initialized = true;
@ -187,7 +265,7 @@ public final class EmojiPalettesView extends LinearLayout
AudioAndHapticFeedbackManager.getInstance().performHapticAndAudioFeedback(KeyCode.NOT_SPECIFIED, this); AudioAndHapticFeedbackManager.getInstance().performHapticAndAudioFeedback(KeyCode.NOT_SPECIFIED, this);
final int categoryId = ((Long) tag).intValue(); final int categoryId = ((Long) tag).intValue();
if (categoryId != mEmojiCategory.getCurrentCategoryId()) { if (categoryId != mEmojiCategory.getCurrentCategoryId()) {
setCurrentCategoryAndPageId(categoryId, 0, false); setCurrentCategoryId(categoryId, false);
updateEmojiCategoryPageIdView(); updateEmojiCategoryPageIdView();
} }
} }
@ -212,7 +290,7 @@ public final class EmojiPalettesView extends LinearLayout
*/ */
@Override @Override
public void onReleaseKey(final Key key) { public void onReleaseKey(final Key key) {
mEmojiPalettesAdapter.addRecentKey(key); addRecentKey(key);
final int code = key.getCode(); final int code = key.getCode();
if (code == KeyCode.MULTIPLE_CODE_POINTS) { if (code == KeyCode.MULTIPLE_CODE_POINTS) {
mKeyboardActionListener.onTextInput(key.getOutputText()); mKeyboardActionListener.onTextInput(key.getOutputText());
@ -237,13 +315,22 @@ public final class EmojiPalettesView extends LinearLayout
setupBottomRowKeyboard(editorInfo, keyboardActionListener); setupBottomRowKeyboard(editorInfo, keyboardActionListener);
final KeyDrawParams params = new KeyDrawParams(); final KeyDrawParams params = new KeyDrawParams();
params.updateParams(mEmojiLayoutParams.getBottomRowKeyboardHeight(), keyVisualAttr); params.updateParams(mEmojiLayoutParams.getBottomRowKeyboardHeight(), keyVisualAttr);
if (mEmojiRecyclerView.getAdapter() == null) {
mEmojiRecyclerView.setAdapter(mEmojiPalettesAdapter);
setCurrentCategoryAndPageId(mEmojiCategory.getCurrentCategoryId(), mEmojiCategory.getCurrentCategoryPageId(), true);
}
setupSidePadding(); setupSidePadding();
} }
private void addRecentKey(final Key key) {
if (Settings.getValues().mIncognitoModeEnabled) {
// We do not want to log recent keys while being in incognito
return;
}
if (mEmojiCategory.isInRecentTab()) {
getRecentsKeyboard().addPendingKey(key);
return;
}
getRecentsKeyboard().addKeyFirst(key);
mPager.getAdapter().notifyItemChanged(mEmojiCategory.getRecentTabId());
}
private void setupBottomRowKeyboard(final EditorInfo editorInfo, final KeyboardActionListener keyboardActionListener) { private void setupBottomRowKeyboard(final EditorInfo editorInfo, final KeyboardActionListener keyboardActionListener) {
MainKeyboardView keyboardView = findViewById(R.id.bottom_row_keyboard); MainKeyboardView keyboardView = findViewById(R.id.bottom_row_keyboard);
keyboardView.setKeyboardActionListener(keyboardActionListener); keyboardView.setKeyboardActionListener(keyboardActionListener);
@ -263,11 +350,11 @@ public final class EmojiPalettesView extends LinearLayout
final float rightPadding = keyboardAttr.getFraction(R.styleable.Keyboard_keyboardRightPadding, final float rightPadding = keyboardAttr.getFraction(R.styleable.Keyboard_keyboardRightPadding,
keyboardWidth, keyboardWidth, 0f) * sv.mSidePaddingScale; keyboardWidth, keyboardWidth, 0f) * sv.mSidePaddingScale;
keyboardAttr.recycle(); keyboardAttr.recycle();
mEmojiRecyclerView.setPadding( mPager.setPadding(
(int) leftPadding, (int) leftPadding,
mEmojiRecyclerView.getPaddingTop(), mPager.getPaddingTop(),
(int) rightPadding, (int) rightPadding,
mEmojiRecyclerView.getPaddingBottom() mPager.getPaddingBottom()
); );
mEmojiCategoryPageIndicatorView.setPadding( mEmojiCategoryPageIndicatorView.setPadding(
(int) leftPadding, (int) leftPadding,
@ -280,9 +367,11 @@ public final class EmojiPalettesView extends LinearLayout
public void stopEmojiPalettes() { public void stopEmojiPalettes() {
if (!initialized) return; if (!initialized) return;
mEmojiPalettesAdapter.releaseCurrentKey(true); getRecentsKeyboard().flushPendingRecentKeys();
mEmojiPalettesAdapter.flushPendingRecentKeys(); }
mEmojiRecyclerView.setAdapter(null);
private DynamicGridKeyboard getRecentsKeyboard() {
return mEmojiCategory.getKeyboard(EmojiCategory.ID_RECENTS, 0);
} }
public void setKeyboardActionListener(final KeyboardActionListener listener) { public void setKeyboardActionListener(final KeyboardActionListener listener) {
@ -298,22 +387,15 @@ public final class EmojiPalettesView extends LinearLayout
mEmojiCategory.getCurrentCategoryPageId(), 0.0f); mEmojiCategory.getCurrentCategoryPageId(), 0.0f);
} }
private void setCurrentCategoryAndPageId(final int categoryId, final int categoryPageId, final boolean force) { private void setCurrentCategoryId(final int categoryId, final boolean initial) {
final int oldCategoryId = mEmojiCategory.getCurrentCategoryId(); final int oldCategoryId = mEmojiCategory.getCurrentCategoryId();
final int oldCategoryPageId = mEmojiCategory.getCurrentCategoryPageId(); if (initial || oldCategoryId != categoryId) {
if (oldCategoryId == EmojiCategory.ID_RECENTS && categoryId != EmojiCategory.ID_RECENTS) {
// Needs to save pending updates for recent keys when we get out of the recents
// category because we don't want to move the recent emojis around while the user
// is in the recents category.
mEmojiPalettesAdapter.flushPendingRecentKeys();
}
if (force || oldCategoryId != categoryId || oldCategoryPageId != categoryPageId) {
mEmojiCategory.setCurrentCategoryId(categoryId); mEmojiCategory.setCurrentCategoryId(categoryId);
mEmojiCategory.setCurrentCategoryPageId(categoryPageId);
mEmojiPalettesAdapter.notifyDataSetChanged(); if (mPager.getScrollState() != ViewPager2.SCROLL_STATE_DRAGGING) {
mEmojiRecyclerView.scrollToPosition(categoryPageId); // Not swiping
mPager.setCurrentItem(mEmojiCategory.getTabIdFromCategoryId(
mEmojiCategory.getCurrentCategoryId()), ! initial);
} }
final View old = mTabStrip.findViewWithTag((long) oldCategoryId); final View old = mTabStrip.findViewWithTag((long) oldCategoryId);
@ -324,8 +406,14 @@ public final class EmojiPalettesView extends LinearLayout
if (current instanceof ImageView) if (current instanceof ImageView)
Settings.getValues().mColors.setColor((ImageView) current, ColorType.EMOJI_CATEGORY_SELECTED); Settings.getValues().mColors.setColor((ImageView) current, ColorType.EMOJI_CATEGORY_SELECTED);
} }
}
public void clearKeyboardCache() { public void clearKeyboardCache() {
if (!initialized) {
return;
}
mEmojiCategory.clearKeyboardCache(); mEmojiCategory.clearKeyboardCache();
mPager.getAdapter().notifyDataSetChanged();
} }
} }

View file

@ -2022,8 +2022,10 @@ public class LatinIME extends InputMethodService implements
public void onTrimMemory(int level) { public void onTrimMemory(int level) {
super.onTrimMemory(level); super.onTrimMemory(level);
switch (level) { switch (level) {
case TRIM_MEMORY_RUNNING_LOW, TRIM_MEMORY_RUNNING_CRITICAL, TRIM_MEMORY_COMPLETE -> case TRIM_MEMORY_RUNNING_LOW, TRIM_MEMORY_RUNNING_CRITICAL, TRIM_MEMORY_COMPLETE -> {
KeyboardLayoutSet.onSystemLocaleChanged(); // clears caches, nothing else KeyboardLayoutSet.onSystemLocaleChanged(); // clears caches, nothing else
mKeyboardSwitcher.trimMemory();
}
// deallocateMemory always called on hiding, and should not be called when showing // deallocateMemory always called on hiding, and should not be called when showing
} }
} }

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2013 The Android Open Source Project
modified
SPDX-License-Identifier: Apache-2.0 AND GPL-3.0-only
-->
<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="match_parent"
android:background="@android:color/transparent"
style="?attr/emojiPalettesViewStyle">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/emoji_keyboard_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
android:orientation="vertical" />
</FrameLayout>

View file

@ -15,11 +15,10 @@
android:background="@android:color/transparent" android:background="@android:color/transparent"
style="?attr/emojiPalettesViewStyle" style="?attr/emojiPalettesViewStyle"
> >
<androidx.recyclerview.widget.RecyclerView <androidx.viewpager2.widget.ViewPager2
android:id="@+id/emoji_keyboard_list" android:id="@+id/emoji_pager"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content" />
android:orientation="vertical" />
<helium314.keyboard.keyboard.emoji.EmojiCategoryPageIndicatorView <helium314.keyboard.keyboard.emoji.EmojiCategoryPageIndicatorView
android:id="@+id/emoji_category_page_id_view" android:id="@+id/emoji_category_page_id_view"
android:layout_width="match_parent" android:layout_width="match_parent"