Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Added emoji usage frequency support #618

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
import android.view.LayoutInflater;
import android.view.View;
import android.view.inputmethod.EditorInfo;

import org.dslul.openboard.inputmethod.event.Event;
import org.dslul.openboard.inputmethod.keyboard.KeyboardLayoutSet.KeyboardLayoutSetException;
import org.dslul.openboard.inputmethod.keyboard.clipboard.ClipboardHistoryView;
Expand Down Expand Up @@ -512,6 +511,10 @@ public MainKeyboardView getMainKeyboardView() {
return mKeyboardView;
}

public EmojiPalettesView getEmojiPalettesView() {
return mEmojiPalettesView;
}

public void deallocateMemory() {
if (mKeyboardView != null) {
mKeyboardView.cancelAllOngoingEvents();
Expand Down Expand Up @@ -545,6 +548,7 @@ public View onCreateInputView(final boolean isHardwareAcceleratedDrawingEnabled)
mKeyboardView.setKeyboardActionListener(mLatinIME);
mEmojiPalettesView.setHardwareAcceleratedDrawingEnabled(
isHardwareAcceleratedDrawingEnabled);
mEmojiPalettesView.setUiHandler(mLatinIME.mHandler);
mEmojiPalettesView.setKeyboardActionListener(mLatinIME);
mClipboardHistoryView.setHardwareAcceleratedDrawingEnabled(
isHardwareAcceleratedDrawingEnabled);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,57 +16,46 @@

package org.dslul.openboard.inputmethod.keyboard.emoji;

import android.content.SharedPreferences;
import android.text.TextUtils;
import android.util.Log;

import org.dslul.openboard.inputmethod.keyboard.Key;
import org.dslul.openboard.inputmethod.keyboard.Keyboard;
import org.dslul.openboard.inputmethod.keyboard.internal.MoreKeySpec;
import org.dslul.openboard.inputmethod.latin.settings.Settings;
import org.dslul.openboard.inputmethod.latin.utils.JsonUtils;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;

/**
* This is a Keyboard class where you can add keys dynamically shown in a grid layout
*/
final class DynamicGridKeyboard extends Keyboard {
private static final String TAG = DynamicGridKeyboard.class.getSimpleName();
public class DynamicGridKeyboard extends Keyboard {

private static final String TAG = "DynamicGridKeyboard";
private static final int TEMPLATE_KEY_CODE_0 = 0x30;
private static final int TEMPLATE_KEY_CODE_1 = 0x31;
private final Object mLock = new Object();

private final SharedPreferences mPrefs;
private final int mHorizontalStep;
private final int mHorizontalGap;
private final int mVerticalStep;
private final int mColumnsNum;
private final int mMaxKeyCount;
private final boolean mIsRecents;
private final ArrayDeque<GridKey> mGridKeys = new ArrayDeque<>();
private final ArrayDeque<Key> mPendingKeys = new ArrayDeque<>();
private final LinkedList<GridKey> mGridKeys = new LinkedList<>();

private List<Key> mCachedGridKeys;

public DynamicGridKeyboard(final SharedPreferences prefs, final Keyboard templateKeyboard,
final int maxKeyCount, final int categoryId) {
public DynamicGridKeyboard(final Keyboard templateKeyboard) {
super(templateKeyboard);
final Key key0 = getTemplateKey(TEMPLATE_KEY_CODE_0);
final Key key1 = getTemplateKey(TEMPLATE_KEY_CODE_1);
mHorizontalGap = Math.abs(key1.getX() - key0.getX()) - key0.getWidth();
mHorizontalStep = key0.getWidth() + mHorizontalGap;
mVerticalStep = key0.getHeight() + mVerticalGap;
mColumnsNum = mBaseWidth / mHorizontalStep;
mMaxKeyCount = maxKeyCount;
mIsRecents = categoryId == EmojiCategory.ID_RECENTS;
mPrefs = prefs;
}

private Key getTemplateKey(final int code) {
Expand All @@ -87,124 +76,56 @@ public int getColumnsCount() {
return mColumnsNum;
}

public void addPendingKey(final Key usedKey) {
synchronized (mLock) {
mPendingKeys.addLast(usedKey);
}
public int getKeyCount() {
return mGridKeys.size();
}

public void flushPendingRecentKeys() {
synchronized (mLock) {
while (!mPendingKeys.isEmpty()) {
addKey(mPendingKeys.pollFirst(), true);
}
saveRecentKeys();
}
}

public void addKeyFirst(final Key usedKey) {
addKey(usedKey, true);
if (mIsRecents) {
saveRecentKeys();
public void addKeyFrom(final Key baseKey) {
if (baseKey == null) {
return;
}
addKey(makeGridKey(baseKey));
}

public void addKeyLast(final Key usedKey) {
addKey(usedKey, false);
}

private void addKey(final Key usedKey, final boolean addFirst) {
if (usedKey == null) {
public void addKey(final GridKey key) {
if (key == null) {
return;
}
synchronized (mLock) {
mCachedGridKeys = null;
// When a key is added to recents keyboard, we don't want to keep its more keys
// neither its hint label. Also, we make sure its background type is matching our keyboard
// if key comes from another keyboard (ie. a {@link MoreKeysKeyboard}).
final boolean dropMoreKeys = mIsRecents;
// Check if hint was a more emoji indicator and prevent its copy if more keys aren't copied
final boolean dropHintLabel = dropMoreKeys && "\u25E5".equals(usedKey.getHintLabel());
final GridKey key = new GridKey(usedKey,
dropMoreKeys ? null : usedKey.getMoreKeys(),
dropHintLabel ? null : usedKey.getHintLabel(),
mIsRecents ? Key.BACKGROUND_TYPE_EMPTY : usedKey.getBackgroundType());
while (mGridKeys.remove(key)) {
// Remove duplicate keys.
}
if (addFirst) {
mGridKeys.addFirst(key);
} else {
mGridKeys.addLast(key);
}
while (mGridKeys.size() > mMaxKeyCount) {
mGridKeys.removeLast();
}
int index = 0;
for (final GridKey gridKey : mGridKeys) {
final int keyX0 = getKeyX0(index);
final int keyY0 = getKeyY0(index);
final int keyX1 = getKeyX1(index);
final int keyY1 = getKeyY1(index);
gridKey.updateCoordinates(keyX0, keyY0, keyX1, keyY1);
index++;
}
mGridKeys.add(key);
}
updateKeysCoordinates();
}

private void saveRecentKeys() {
final ArrayList<Object> keys = new ArrayList<>();
for (final Key key : mGridKeys) {
if (key.getOutputText() != null) {
keys.add(key.getOutputText());
} else {
keys.add(key.getCode());
}
}
final String jsonStr = JsonUtils.listToJsonStr(keys);
Settings.writeEmojiRecentKeys(mPrefs, jsonStr);
public void removeLastKey() {
mGridKeys.removeLast();
}

private static Key getKeyByCode(final Collection<DynamicGridKeyboard> keyboards,
final int code) {
for (final DynamicGridKeyboard keyboard : keyboards) {
for (final Key key : keyboard.getSortedKeys()) {
if (key.getCode() == code) {
return key;
}
}
}
return null;
public void sortKeys(Comparator<GridKey> comparator) {
Collections.sort(mGridKeys, comparator);
updateKeysCoordinates();
}

private static Key getKeyByOutputText(final Collection<DynamicGridKeyboard> keyboards,
final String outputText) {
for (final DynamicGridKeyboard keyboard : keyboards) {
for (final Key key : keyboard.getSortedKeys()) {
if (outputText.equals(key.getOutputText())) {
return key;
}
}
}
return null;
protected GridKey makeGridKey(Key baseKey) {
return new GridKey(baseKey,
baseKey.getMoreKeys(),
baseKey.getHintLabel(),
baseKey.getBackgroundType());
}

public void loadRecentKeys(final Collection<DynamicGridKeyboard> keyboards) {
final String str = Settings.readEmojiRecentKeys(mPrefs);
final List<Object> keys = JsonUtils.jsonStrToList(str);
for (final Object o : keys) {
final Key key;
if (o instanceof Integer) {
final int code = (Integer)o;
key = getKeyByCode(keyboards, code);
} else if (o instanceof String) {
final String outputText = (String)o;
key = getKeyByOutputText(keyboards, outputText);
} else {
Log.w(TAG, "Invalid object: " + o);
continue;
}
addKeyLast(key);
private void updateKeysCoordinates() {
int index = 0;
for (final GridKey gridKey : mGridKeys) {
final int keyX0 = getKeyX0(index);
final int keyY0 = getKeyY0(index);
final int keyX1 = getKeyX1(index);
final int keyY1 = getKeyY1(index);
gridKey.updateCoordinates(keyX0, keyY0, keyX1, keyY1);
index++;
}
}

Expand Down Expand Up @@ -234,7 +155,7 @@ public List<Key> getSortedKeys() {
if (mCachedGridKeys != null) {
return mCachedGridKeys;
}
final ArrayList<Key> cachedKeys = new ArrayList<Key>(mGridKeys);
final ArrayList<Key> cachedKeys = new ArrayList<>(mGridKeys);
mCachedGridKeys = Collections.unmodifiableList(cachedKeys);
return mCachedGridKeys;
}
Expand All @@ -246,7 +167,7 @@ public List<Key> getNearestKeys(final int x, final int y) {
return getSortedKeys();
}

static final class GridKey extends Key {
protected static class GridKey extends Key {
private int mCurrentX;
private int mCurrentY;

Expand Down Expand Up @@ -280,6 +201,11 @@ public boolean equals(final Object o) {
return TextUtils.equals(getOutputText(), key.getOutputText());
}

@Override
public int hashCode() {
return Objects.hash(getCode(), getLabel(), getOutputText());
}

@Override
public String toString() {
return "GridKey: " + super.toString();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,21 @@

package org.dslul.openboard.inputmethod.keyboard.emoji;

import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.Log;

import androidx.core.graphics.PaintCompat;
import org.dslul.openboard.inputmethod.keyboard.Key;
import org.dslul.openboard.inputmethod.keyboard.Keyboard;
import org.dslul.openboard.inputmethod.keyboard.KeyboardId;
import org.dslul.openboard.inputmethod.keyboard.KeyboardLayoutSet;
import org.dslul.openboard.inputmethod.latin.R;
import org.dslul.openboard.inputmethod.latin.settings.Settings;
import org.dslul.openboard.inputmethod.latin.utils.DeviceProtectedUtils;

import java.util.ArrayList;
import java.util.Collections;
Expand Down Expand Up @@ -119,6 +120,7 @@ public CategoryProperties(final int categoryId, final int pageCount) {

private final SharedPreferences mPrefs;
private final Resources mRes;
private final RecentEmojiDbHelper mRecentEmojiDbHelper;
private final int mMaxRecentsKeyCount;
private final KeyboardLayoutSet mLayoutSet;
private final HashMap<String, Integer> mCategoryNameToIdMap = new HashMap<>();
Expand All @@ -130,11 +132,12 @@ public CategoryProperties(final int categoryId, final int pageCount) {
private int mCurrentCategoryId = EmojiCategory.ID_UNSPECIFIED;
private int mCurrentCategoryPageId = 0;

public EmojiCategory(final SharedPreferences prefs, final Resources res,
final KeyboardLayoutSet layoutSet, final TypedArray emojiPaletteViewAttr) {
mPrefs = prefs;
mRes = res;
mMaxRecentsKeyCount = res.getInteger(R.integer.config_emoji_keyboard_max_recents_key_count);
public EmojiCategory(final Context context, final KeyboardLayoutSet layoutSet,
final TypedArray emojiPaletteViewAttr) {
mPrefs = DeviceProtectedUtils.getSharedPreferences(context);
mRes = context.getResources();
mRecentEmojiDbHelper = new RecentEmojiDbHelper(context);
mMaxRecentsKeyCount = mRes.getInteger(R.integer.config_emoji_keyboard_max_recents_key_count);
mLayoutSet = layoutSet;
for (int i = 0; i < sCategoryName.length; ++i) {
mCategoryNameToIdMap.put(sCategoryName[i], i);
Expand All @@ -157,16 +160,12 @@ public EmojiCategory(final SharedPreferences prefs, final Resources res,
}
addShownCategoryId(EmojiCategory.ID_EMOTICONS);

DynamicGridKeyboard recentsKbd =
getKeyboard(EmojiCategory.ID_RECENTS, 0 /* categoryPageId */);
recentsKbd.loadRecentKeys(mCategoryKeyboardMap.values());

mCurrentCategoryId = Settings.readLastShownEmojiCategoryId(mPrefs, defaultCategoryId);
mCurrentCategoryPageId = Settings.readLastShownEmojiCategoryPageId(mPrefs, 0);
if (!isShownCategoryId(mCurrentCategoryId)) {
mCurrentCategoryId = defaultCategoryId;
} else if (mCurrentCategoryId == EmojiCategory.ID_RECENTS &&
recentsKbd.getSortedKeys().isEmpty()) {
Settings.readEmojiRecentCount(mPrefs) == 0) {
mCurrentCategoryId = defaultCategoryId;
}

Expand Down Expand Up @@ -309,9 +308,10 @@ public DynamicGridKeyboard getKeyboard(final int categoryId, final int id) {
}

if (categoryId == EmojiCategory.ID_RECENTS) {
final DynamicGridKeyboard kbd = new DynamicGridKeyboard(mPrefs,
final DynamicGridKeyboard kbd = new RecentEmojiKeyboard(mRecentEmojiDbHelper,
mLayoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_RECENTS),
mMaxRecentsKeyCount, categoryId);
mCategoryKeyboardMap.values(),
mMaxRecentsKeyCount);
mCategoryKeyboardMap.put(categoryKeyboardMapKey, kbd);
return kbd;
}
Expand All @@ -321,14 +321,13 @@ public DynamicGridKeyboard getKeyboard(final int categoryId, final int id) {
final Key[][] sortedKeysPages = sortKeysGrouped(
keyboard.getSortedKeys(), keyCountPerPage);
for (int pageId = 0; pageId < sortedKeysPages.length; ++pageId) {
final DynamicGridKeyboard tempKeyboard = new DynamicGridKeyboard(mPrefs,
mLayoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_RECENTS),
keyCountPerPage, categoryId);
final DynamicGridKeyboard tempKeyboard = new DynamicGridKeyboard(
mLayoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_RECENTS));
for (final Key emojiKey : sortedKeysPages[pageId]) {
if (emojiKey == null) {
break;
}
tempKeyboard.addKeyLast(emojiKey);
tempKeyboard.addKeyFrom(emojiKey);
}
mCategoryKeyboardMap.put(
getCategoryKeyboardMapKey(categoryId, pageId), tempKeyboard);
Expand All @@ -337,10 +336,13 @@ public DynamicGridKeyboard getKeyboard(final int categoryId, final int id) {
}
}

public RecentEmojiKeyboard getRecentEmojiKeyboard() {
return (RecentEmojiKeyboard) getKeyboard(EmojiCategory.ID_RECENTS, 0 /* categoryPageId */);
}

private int computeMaxKeyCountPerPage() {
final DynamicGridKeyboard tempKeyboard = new DynamicGridKeyboard(mPrefs,
mLayoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_RECENTS),
0, 0);
final DynamicGridKeyboard tempKeyboard = new DynamicGridKeyboard(
mLayoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_RECENTS));
return MAX_LINE_COUNT_PER_PAGE * tempKeyboard.getColumnsCount();
}

Expand Down
Loading