diff --git a/app/src/main/java/helium314/keyboard/keyboard/Key.java b/app/src/main/java/helium314/keyboard/keyboard/Key.java
index d19907cc49..7a1e4733a3 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/Key.java
+++ b/app/src/main/java/helium314/keyboard/keyboard/Key.java
@@ -234,6 +234,24 @@ public Key(@Nullable final String label, @Nullable final String iconName, final
*/
protected Key(@NonNull final Key key, @Nullable final PopupKeySpec[] popupKeys,
@Nullable final String labelHint, final int backgroundType) {
+ this(key, popupKeys, labelHint, backgroundType, key.getPopupKeysColumnNumber());
+ }
+
+ /**
+ * Copy constructor that overrides the number of popup key columns.
+ *
+ *
The source key may report zero columns (e.g. emoji recent keys restored from settings),
+ * which cannot lay out popup keys. Callers that assign popup keys can pass an explicit column
+ * count here instead of relying on the source key.
+ *
+ * @param key the original key.
+ * @param popupKeys the popup keys that should be assigned to this key.
+ * @param labelHint the label hint that should be assigned to this key.
+ * @param backgroundType the background type that should be assigned to this key.
+ * @param popupKeysColumnNumber the number of columns to lay the popup keys out in.
+ */
+ protected Key(@NonNull final Key key, @Nullable final PopupKeySpec[] popupKeys,
+ @Nullable final String labelHint, final int backgroundType, final int popupKeysColumnNumber) {
// Final attributes.
mCode = key.mCode;
mLabel = key.mLabel;
@@ -249,7 +267,8 @@ protected Key(@NonNull final Key key, @Nullable final PopupKeySpec[] popupKeys,
mY = key.mY;
mHitBox.set(key.mHitBox);
mPopupKeys = popupKeys;
- mPopupKeysColumnAndFlags = key.mPopupKeysColumnAndFlags;
+ mPopupKeysColumnAndFlags = (key.mPopupKeysColumnAndFlags & ~POPUP_KEYS_COLUMN_NUMBER_MASK)
+ | (popupKeysColumnNumber & POPUP_KEYS_COLUMN_NUMBER_MASK);
mBackgroundType = backgroundType;
mActionFlags = key.mActionFlags;
mKeyVisualAttributes = key.mKeyVisualAttributes;
diff --git a/app/src/main/java/helium314/keyboard/keyboard/emoji/DynamicGridKeyboard.java b/app/src/main/java/helium314/keyboard/keyboard/emoji/DynamicGridKeyboard.java
index b87c648c25..6e22f6c940 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/emoji/DynamicGridKeyboard.java
+++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/DynamicGridKeyboard.java
@@ -164,6 +164,35 @@ public void removeAllKeys() {
}
}
+ public boolean isRecents() {
+ return mIsRecents;
+ }
+
+ public void removeKey(final Key usedKey) {
+ synchronized (mLock) {
+ boolean removed = false;
+ while (mGridKeys.remove(usedKey)) {
+ // Remove all keys matching the removed one.
+ removed = true;
+ }
+ if (!removed) {
+ return;
+ }
+ mCachedGridKeys = null;
+ updateKeyCoordinates();
+ }
+ if (mIsRecents) {
+ removeRecentKey(usedKey);
+ }
+ }
+
+ public void clearRecentKeys() {
+ removeAllKeys();
+ if (mIsRecents) {
+ RecentEmojis.clear();
+ }
+ }
+
private void addKey(final Key usedKey, final boolean addFirst) {
if (usedKey == null) {
return;
@@ -191,18 +220,22 @@ private void addKey(final Key usedKey, final boolean addFirst) {
while (mGridKeys.size() > mMaxKeyCount) {
mGridKeys.removeLast();
}
- int index = 0;
- for (final GridKey gridKey : mGridKeys) {
- while (mEmptyColumnIndices.contains(index % mColumnsNum)) {
- index++;
- }
- 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);
+ updateKeyCoordinates();
+ }
+ }
+
+ private void updateKeyCoordinates() {
+ int index = 0;
+ for (final GridKey gridKey : mGridKeys) {
+ while (mEmptyColumnIndices.contains(index % mColumnsNum)) {
index++;
}
+ 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++;
}
}
@@ -213,6 +246,13 @@ private void saveRecentKey(@Nullable Key key) {
else RecentEmojis.addCodepoint(key.getCode());
}
+ private void removeRecentKey(@Nullable Key key) {
+ if (key == null) return;
+ String outputText = key.getOutputText();
+ if (outputText != null) RecentEmojis.remove(outputText);
+ else RecentEmojis.removeCodepoint(key.getCode());
+ }
+
private Key getKeyByCode(final Collection keyboards,
final int code) {
for (final DynamicGridKeyboard keyboard : keyboards) {
@@ -304,6 +344,11 @@ public GridKey(@NonNull final Key originalKey, @Nullable final PopupKeySpec[] po
super(originalKey, popupKeys, labelHint, backgroundType);
}
+ public GridKey(@NonNull final Key originalKey, @Nullable final PopupKeySpec[] popupKeys,
+ @Nullable final String labelHint, final int backgroundType, final int popupKeysColumnNumber) {
+ super(originalKey, popupKeys, labelHint, backgroundType, popupKeysColumnNumber);
+ }
+
public void updateCoordinates(final int x0, final int y0, final int x1, final int y1) {
mCurrentX = x0;
mCurrentY = y0;
diff --git a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiPageKeyboardView.java b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiPageKeyboardView.java
index 6142d41cb2..2f79b67c84 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiPageKeyboardView.java
+++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiPageKeyboardView.java
@@ -38,11 +38,14 @@
import helium314.keyboard.keyboard.PopupKeysKeyboard;
import helium314.keyboard.keyboard.PopupKeysKeyboardView;
import helium314.keyboard.keyboard.PopupKeysPanel;
+import helium314.keyboard.keyboard.internal.KeyboardIconsSet;
import helium314.keyboard.keyboard.internal.PopupKeySpec;
+import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode;
import helium314.keyboard.latin.R;
import helium314.keyboard.latin.common.CoordinateUtils;
import helium314.keyboard.latin.settings.Settings;
+import java.util.Locale;
import java.util.WeakHashMap;
/**
@@ -56,12 +59,20 @@ public final class EmojiPageKeyboardView extends KeyboardView implements
private static final long KEY_PRESS_DELAY_TIME = 250; // msec
private static final long KEY_RELEASE_DELAY_TIME = 30; // msec
+ // Single delete button shown when long-pressing a key in the recents category.
+ private static final PopupKeySpec[] REMOVE_RECENT_POPUP_KEYS = {
+ new PopupKeySpec(KeyboardIconsSet.PREFIX_ICON + KeyboardIconsSet.NAME_BIN
+ + "|!code/" + KeyCode.EMOJI_RECENT_REMOVE, false, Locale.ROOT)
+ };
+
private static final EmojiViewCallback EMPTY_EMOJI_VIEW_CALLBACK = new EmojiViewCallback() {
@Override
public void onPressKey(final Key key) {}
@Override
public void onReleaseKey(final Key key) {}
@Override
+ public void onRemoveRecentKey(final Key key) {}
+ @Override
public String getDescription(String emoji) {
return null;
}
@@ -200,6 +211,51 @@ private PopupKeysPanel showPopupKeysKeyboard(@NonNull final Key key) {
return mPopupKeysKeyboardView;
}
+ // Builds a single-key popup holding a delete button, used to remove a key from the recents category.
+ @NonNull
+ private PopupKeysPanel showRemoveKeyboard(@NonNull final Key key) {
+ Keyboard popupKeysKeyboard = mPopupKeysKeyboardCache.get(key);
+ if (popupKeysKeyboard == null) {
+ // A recent key restored from settings can report zero popup-key columns, which cannot
+ // lay out popup keys. Give the remove popup one column per delete button it contains.
+ final Key removeKey = new DynamicGridKeyboard.GridKey(key, REMOVE_RECENT_POPUP_KEYS, null,
+ key.getBackgroundType(), REMOVE_RECENT_POPUP_KEYS.length);
+ // The delete button is icon-only, so size the single-key popup explicitly instead of
+ // measuring a label (which would divide by a zero base width). Fall back to the most
+ // common key height so the width is always non-zero.
+ final Keyboard keyboard = getKeyboard();
+ final int keyWidth = Math.max(key.getHitBox().width(), keyboard.mMostCommonKeyHeight);
+ final int keyHeight = Math.max(key.getHitBox().height(), keyboard.mMostCommonKeyHeight);
+ final PopupKeysKeyboard.Builder builder = new PopupKeysKeyboard.Builder(
+ getContext(), removeKey, keyboard, true, keyWidth, keyHeight, newLabelPaint(removeKey));
+ popupKeysKeyboard = builder.build();
+ mPopupKeysKeyboardCache.put(key, popupKeysKeyboard);
+ }
+
+ mPopupKeysKeyboardView.setKeyboard(popupKeysKeyboard);
+ mPopupKeysKeyboardView.setVisibility(VISIBLE);
+ return mPopupKeysKeyboardView;
+ }
+
+ // Wraps the regular callback so that selecting the delete button removes the given recent key,
+ // while emoji descriptions keep being resolved through the original callback.
+ private EmojiViewCallback createRemoveRecentCallback(@NonNull final Key key) {
+ return new EmojiViewCallback() {
+ @Override
+ public void onPressKey(final Key pressedKey) {}
+ @Override
+ public void onReleaseKey(final Key releasedKey) {
+ mEmojiViewCallback.onRemoveRecentKey(key);
+ }
+ @Override
+ public void onRemoveRecentKey(final Key removedKey) {}
+ @Override
+ public String getDescription(final String emoji) {
+ return mEmojiViewCallback.getDescription(emoji);
+ }
+ };
+ }
+
private void dismissPopupKeysPanel() {
if (isShowingPopupKeysPanel()) {
mPopupKeysPanel.dismissPopupKeysPanel();
@@ -298,7 +354,13 @@ private void onLongPressed(final Key key) {
}
var descriptionPanel = showDescription(key);
- final PopupKeysPanel popupKeysPanel = showPopupKeysKeyboard(key);
+ // In the recents category, long-pressing a key offers a delete button to remove it from
+ // recents (similar to deleting a suggestion), instead of the skin tone variants panel.
+ final Keyboard keyboard = getKeyboard();
+ final boolean isRecents = keyboard instanceof DynamicGridKeyboard && ((DynamicGridKeyboard) keyboard).isRecents();
+ final PopupKeysPanel popupKeysPanel = isRecents ? showRemoveKeyboard(key) : showPopupKeysKeyboard(key);
+ // Route the delete button to recents removal, everything else to the regular callback.
+ final EmojiViewCallback callback = isRecents ? createRemoveRecentCallback(key) : mEmojiViewCallback;
final int x = mLastX;
final int y = mLastY;
@@ -314,7 +376,7 @@ private void onLongPressed(final Key key) {
: key.getX() + key.getWidth() / 2;
final int pointY = key.getY() - getKeyboard().mVerticalGap;
(popupKeysPanel != null? popupKeysPanel : descriptionPanel)
- .showPopupKeysPanel(this, this, pointX, pointY, mEmojiViewCallback);
+ .showPopupKeysPanel(this, this, pointX, pointY, callback);
}
if (popupKeysPanel != null) {
diff --git a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiPalettesView.java b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiPalettesView.java
index a292d3a2a9..cf7d3764dd 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiPalettesView.java
+++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiPalettesView.java
@@ -246,6 +246,12 @@ private void addTab(LinearLayout host, EmojiCategory.Category category) {
host.addView(iconView);
iconView.setLayoutParams(new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, 1f));
iconView.setOnClickListener(this);
+ if (category == EmojiCategory.Category.RECENTS) {
+ iconView.setOnLongClickListener(v -> {
+ clearRecentKeys();
+ return true;
+ });
+ }
}
@SuppressLint("ClickableViewAccessibility")
@@ -317,6 +323,25 @@ public void onReleaseKey(final Key key) {
mKeyboardActionListener.onCodeInput(KeyCode.ALPHA, NOT_A_COORDINATE, NOT_A_COORDINATE, false);
}
+ /**
+ * Called from {@link EmojiPageKeyboardView} through {@link EmojiViewCallback}
+ * interface to remove a single emoji from the recents category.
+ */
+ @Override
+ public void onRemoveRecentKey(final Key key) {
+ AudioAndHapticFeedbackManager.getInstance().performHapticAndAudioFeedback(KeyCode.NOT_SPECIFIED, this, HapticEvent.KEY_LONG_PRESS);
+ getRecentsKeyboard().removeKey(key);
+ if (initialized)
+ mPager.getAdapter().notifyItemChanged(mEmojiCategory.getRecentTabId());
+ }
+
+ private void clearRecentKeys() {
+ AudioAndHapticFeedbackManager.getInstance().performHapticAndAudioFeedback(KeyCode.NOT_SPECIFIED, this, HapticEvent.KEY_LONG_PRESS);
+ getRecentsKeyboard().clearRecentKeys();
+ if (initialized)
+ mPager.getAdapter().notifyItemChanged(mEmojiCategory.getRecentTabId());
+ }
+
@Override
public String getDescription(String emoji) {
if (sDictionaryFacilitator == null) {
diff --git a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
index 00289c9301..ce963200cd 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
+++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
@@ -319,6 +319,9 @@ class EmojiSearchActivity : ComponentActivity() {
finish()
}
+ override fun onRemoveRecentKey(key: Key) {
+ }
+
override fun getDescription(emoji: String): String? = if (Settings.getValues().mShowEmojiDescriptions)
dictionaryFacilitator?.getWordProperty(getEmojiNeutralVersion(emoji))?.let {
if (it.mHasShortcuts) it.mShortcutTargets[0]?.mWord else null
diff --git a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiViewCallback.java b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiViewCallback.java
index 192d92fee4..8f8faded7c 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiViewCallback.java
+++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiViewCallback.java
@@ -22,6 +22,11 @@ public interface EmojiViewCallback {
*/
void onReleaseKey(Key key);
+ /**
+ * Called when a key in the recents category is long-pressed, to remove it from recents.
+ */
+ void onRemoveRecentKey(Key key);
+
/**
* Called from keyboard view to get an emoji description
*/
diff --git a/app/src/main/java/helium314/keyboard/keyboard/emoji/RecentEmojis.kt b/app/src/main/java/helium314/keyboard/keyboard/emoji/RecentEmojis.kt
index 7e1af2488d..37b67c56e4 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/emoji/RecentEmojis.kt
+++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/RecentEmojis.kt
@@ -33,6 +33,23 @@ object RecentEmojis {
if (emoji > 0) add(StringUtils.newSingleCodePointString(emoji))
}
+ @JvmStatic
+ fun remove(emoji: String) {
+ if (emoji.isEmpty()) return
+ val recents = get()
+ if (recents.removeAll { it == emoji }) set(recents)
+ }
+
+ @JvmStatic
+ fun removeCodepoint(emoji: Int) {
+ if (emoji > 0) remove(StringUtils.newSingleCodePointString(emoji))
+ }
+
+ @JvmStatic
+ fun clear() {
+ set(emptyList())
+ }
+
fun get(): MutableList {
val pref = prefs.getString(Settings.PREF_RECENT_EMOJIS, Defaults.PREF_RECENT_EMOJIS)
if (pref.isNullOrEmpty()) return mutableListOf()
diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyCode.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyCode.kt
index 4a8bc2659e..b9bb03819d 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyCode.kt
+++ b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyCode.kt
@@ -127,6 +127,7 @@ object KeyCode {
const val CJK_SPACE = 12288
// heliboard only codes
+ const val EMOJI_RECENT_REMOVE = -10000 // remove a single emoji from the recents category
const val SYMBOL_ALPHA = -10001
const val TOGGLE_ONE_HANDED_MODE = -10002
const val TOGGLE_ONE_HANDED_MODE_2 = -10003 // does the same as TOGGLE_ONE_HANDED_MODE (used to be start & stop)
@@ -211,7 +212,7 @@ object KeyCode {
MEDIA_PREVIOUS, VOL_UP, VOL_DOWN, MUTE, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, BACK,
TIMESTAMP, CTRL_LEFT, CTRL_RIGHT, ALT_LEFT, ALT_RIGHT, META_LEFT, META_RIGHT, SEND_INTENT_ONE, SEND_INTENT_TWO,
SEND_INTENT_THREE, EMOJI_SEARCH, INLINE_EMOJI_SEARCH_DONE, META_LOCK,
- BACKGROUND_GATHERING, BACKGROUND_GATHERING_TEMP_OFF, DPAD,
+ BACKGROUND_GATHERING, BACKGROUND_GATHERING_TEMP_OFF, DPAD, EMOJI_RECENT_REMOVE
-> this
KEY_REPEAT if (longPress) -> this
diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml
index f4adeff79c..fd44489ac6 100644
--- a/app/src/main/res/values-bn/strings.xml
+++ b/app/src/main/res/values-bn/strings.xml
@@ -96,7 +96,7 @@
প্রচ্ছন্ন বৈশিষ্ট্য
অপরিদৃষ্ট হতে পারে এমন বৈশিষ্ট্যের বর্ণনা
ডিভাইস সুরক্ষিত স্টোরেজ
- ► বাংলা চন্দ্রবিন্দু (ঁ) বোতামে দীর্ঘ চাপে সচরাচর ব্যবহৃত হয় না এমন বর্ণ যেমন: ৠ, ৡ, ৺, ৰ, ৱ ইত্যাদি টাইপ করা যাবে। \'র\' এবং \'ত\' বোতামে দীর্ঘ চাপে যথাক্রমে \'র্য\' এবং \'ৎ\' টাইপ করা যাবে। এছাড়াও দাঁড়ি (।) বোতামে দীর্ঘ চাপে <i>ZWNJ</i> এবং <i>ZWJ</i> টাইপ করা যাবে। <br> <br>► পিন করে রাখা টুলবার বোতামে দীর্ঘ চাপ দিলে তা অতিরিক্ত কাজ করবে: <br> \n\t• ক্লিপবোর্ড → পেস্ট <br> \n\t• বামে/ডানে স্থানান্তর → সম্পূর্ণ বামে/ডানে স্থানান্তর <br> \n\t• উপরে/নিচে স্থানান্তর → পৃষ্ঠার উপরে/নিচে <br> \n\t• শব্দের ডানে/বামে → লাইনের শুরু/শেষ <br> \n\t• পাতার উপরে/নিচে → পাতার শুরু/শেষ <br> \n\t• অনুলিপি → কর্তন <br> \n\t• শব্দ নির্বাচন ↔ সমস্ত নির্বাচন <br> \n\t• পূর্বাবস্থা ↔ পুনরাবৃত্তি <br> <br> \n► পরামর্শ ফলকের টুলবারের কোনো বোতামে দীর্ঘ চাপ দিলে তা পরামর্শ ফলকে আবদ্ধ হবে। <br> <br> \n► কমা বোতামে দীর্ঘ চাপ দিলে ক্লিপবোর্ড দর্শন, ইমোজি দর্শন, একক হাত মোড, সেটিংস অথবা ভাষা পরিবর্তন বোতাম উপলব্ধ হবে:<br> \n\t• সংশ্লিষ্ট বোতাম সক্ষম থাকলে ইমোজি দর্শন বা ভাষা পরিবর্তন বোতাম দৃশ্যমান থাকবে না; <br> \n\t• কিছু লেআউটের জন্য কমা বোতাম নয়, কিন্তু একই জায়গার বোতামটি, যেমন: Dvorak লেআউটে \'q\' বোতাম। <br> <br> \n► ছদ্মবেশী মোড সক্রিয় থাকলে শব্দ শিখন বন্ধ থাকবে, এবং সাম্প্রতিকে কোনো ইমোজি যুক্ত হবে না। <br> <br> \n► ছদ্মবেশী বোতামে চাপ দিলে টুলবার উপলব্ধ হবে। <br> <br> \n► স্লাইডিং কি ইনপুট: বড়ো হাতের অক্ষর লেখার জন্য শিফট বোতাম থেকে অন্য বোতামে অভিস্পর্শ করুন: <br> \n\t• সিম্বল কিবোর্ড থেকে সিম্বল এবং সম্পর্কিত অক্ষর লেখার জন্য \'?123\' বোতামেও এটি কাজ করে।<br> <br> \n► শিফট অথবা প্রতীক বোতাম চেপে ধরে এক বা একাধিক বোতাম চাপার পর শিফট অথবা প্রতীক বোতাম ছেড়ে দিলে তা পূর্ববর্তী কিবোর্ডে নিয়ে যাবে। <br> <br> \n► পরামর্শ ফলকে কোনো পরামর্শে দীর্ঘ চাপ দিলে আরও পরামর্শ, বিলোপ করার জন্য অপসারণ বোতাম প্রদর্শিত হবে। <br> <br> \n► আরও পরামর্শ খুলতে কোনো পরামর্শ থেকে উপরে অভিস্পর্শ করুন এবং নির্দিষ্ট পরামর্শ নির্বাচনের জন্য ছেড়ে দিন। <br> <br> \n► ক্লিপবোর্ড ইতিহাসে কোনো অন্তর্ভুক্তিতে দীর্ঘ চাপ দিয়ে তা পিন করা যাবে (আনপিন না করা পর্যন্ত তা ক্লিপবোর্ডে থাকবে)<br> <br> \n► ক্লিপবোর্ড দর্শনে বাম পাশে অভিস্পর্শ করলে অন্তর্ভুক্তি অপসারণ হবে (যদি সেটি পিন করা না থাকে)। <br> <br> \n► লেখা সিলেক্ট করে শিফট চাপলে তা বড়ো হাতের অক্ষর, ছোটো হাতের অক্ষর এবং সব অক্ষর বড়ো হাতের হবে। <br> <br> \n► আপনি ফাইল এক্সপ্লোরারে খোলার মাধ্যমে অভিধান সংযুক্ত করতে পারেন: <br> \n\t• এটি কেবল <i>content-uris</i> এর সাথে কাজ করে, <i>file-uris</i> এর সাথে নয়। অর্থাৎ কিছু ফাইল এক্সপ্লোরারের সাথে নাও কাজ করতে পারে। <br> <br> \n► রুট উপলব্ধতাসহ ম্যানুয়াল ব্যাকআপ করা ব্যবহারকারীদের জন্য: <br> \nঅ্যান্ড্রয়েড ৭ থেকে শেয়ারড্ প্রেফারেন্স ফাইল ডিফল্ট জায়গা নয়। কারণ অ্যাপ %s ব্যবহার করছে। ডিভাইস আনলকের আগে সেটিংস খোলার জন্য এটি প্রয়োজনীয়, যেমন: বুট করার সময়। <br> \nফাইলটি /data/user_de/0/package_id/shared_prefs/ থাকে। যদিও এটা ডিভাইস এবং অ্যান্ড্রয়েড সংস্করণের উপরে নির্ভর করে। <br> <br> \n<i><b>কেবল ডিবাগ এপিকে</b></i> <br> <br> \n► পরামর্শে দীর্ঘ চাপ দিলে তার উৎস-অভিধান দেখাবে। <br> <br> \n► ডিবাগ সেটিংস উচ্চতর সেটিংসে উপলব্ধ। যদিও লগ ফাইলে অভিধান অবমুক্ত করা ছাড়া এর ব্যবহারযোগ্যতা সীমিত। <br> \n\t• রিলিজ এপিকে এর জন্য <i>সম্পর্কে</i> এর সংস্করণে কয়েকবার চাপ দিতে হবে। তাহলে <i>উচ্চতর</i>-এ ডিবাগ সেটিংস উপলব্ধ হবে। <br> \n\t• <i>পরামর্শ তথ্য প্রদর্শন</i> চালু করলে পরামর্শের উপরে কিছু ছোটো নম্বর থাকবে যা অভ্যন্তরীণ হিসাব ও উৎস অভিধান নির্দেশ করে। <br> <br> \n► অ্যাপ্লিকেশন ক্র্যাশের পরে সেটিংস খুললে আপনি ক্র্যাশ লগ চান কি না তা জিজ্ঞেস করা হবে৷ <br> <br> \n► বহুভাষী টাইপিং ব্যবহারের সময় স্পেসবার বর্তমান ভাষা নির্ধারণের জন্য দৃঢ়তা মান দেখাবে। <br> <br> \n► পরামর্শের উপরে কিছু ছোটো নম্বর থাকবে যা অভ্যন্তরীণ হিসাব ও উৎস অভিধান নির্দেশ করে (নিষ্ক্রিয় করা যেতে পারে)।
+ ► বাংলা চন্দ্রবিন্দু (ঁ) বোতামে দীর্ঘ চাপে সচরাচর ব্যবহৃত হয় না এমন বর্ণ যেমন: ৠ, ৡ, ৺, ৰ, ৱ ইত্যাদি টাইপ করা যাবে। \'র\' এবং \'ত\' বোতামে দীর্ঘ চাপে যথাক্রমে \'র্য\' এবং \'ৎ\' টাইপ করা যাবে। এছাড়াও দাঁড়ি (।) বোতামে দীর্ঘ চাপে <i>ZWNJ</i> এবং <i>ZWJ</i> টাইপ করা যাবে। <br> <br>► পিন করে রাখা টুলবার বোতামে দীর্ঘ চাপ দিলে তা অতিরিক্ত কাজ করবে: <br> \n\t• ক্লিপবোর্ড → পেস্ট <br> \n\t• বামে/ডানে স্থানান্তর → সম্পূর্ণ বামে/ডানে স্থানান্তর <br> \n\t• উপরে/নিচে স্থানান্তর → পৃষ্ঠার উপরে/নিচে <br> \n\t• শব্দের ডানে/বামে → লাইনের শুরু/শেষ <br> \n\t• পাতার উপরে/নিচে → পাতার শুরু/শেষ <br> \n\t• অনুলিপি → কর্তন <br> \n\t• শব্দ নির্বাচন ↔ সমস্ত নির্বাচন <br> \n\t• পূর্বাবস্থা ↔ পুনরাবৃত্তি <br> <br> \n► পরামর্শ ফলকের টুলবারের কোনো বোতামে দীর্ঘ চাপ দিলে তা পরামর্শ ফলকে আবদ্ধ হবে। <br> <br> \n► কমা বোতামে দীর্ঘ চাপ দিলে ক্লিপবোর্ড দর্শন, ইমোজি দর্শন, একক হাত মোড, সেটিংস অথবা ভাষা পরিবর্তন বোতাম উপলব্ধ হবে:<br> \n\t• সংশ্লিষ্ট বোতাম সক্ষম থাকলে ইমোজি দর্শন বা ভাষা পরিবর্তন বোতাম দৃশ্যমান থাকবে না; <br> \n\t• কিছু লেআউটের জন্য কমা বোতাম নয়, কিন্তু একই জায়গার বোতামটি, যেমন: Dvorak লেআউটে \'q\' বোতাম। <br> <br> \n► ছদ্মবেশী মোড সক্রিয় থাকলে শব্দ শিখন বন্ধ থাকবে, এবং সাম্প্রতিকে কোনো ইমোজি যুক্ত হবে না। <br> <br> \n► ছদ্মবেশী বোতামে চাপ দিলে টুলবার উপলব্ধ হবে। <br> <br> \n► স্লাইডিং কি ইনপুট: বড়ো হাতের অক্ষর লেখার জন্য শিফট বোতাম থেকে অন্য বোতামে অভিস্পর্শ করুন: <br> \n\t• সিম্বল কিবোর্ড থেকে সিম্বল এবং সম্পর্কিত অক্ষর লেখার জন্য \'?123\' বোতামেও এটি কাজ করে।<br> <br> \n► শিফট অথবা প্রতীক বোতাম চেপে ধরে এক বা একাধিক বোতাম চাপার পর শিফট অথবা প্রতীক বোতাম ছেড়ে দিলে তা পূর্ববর্তী কিবোর্ডে নিয়ে যাবে। <br> <br> \n► পরামর্শ ফলকে কোনো পরামর্শে দীর্ঘ চাপ দিলে আরও পরামর্শ, বিলোপ করার জন্য অপসারণ বোতাম প্রদর্শিত হবে। <br> <br> \n► আরও পরামর্শ খুলতে কোনো পরামর্শ থেকে উপরে অভিস্পর্শ করুন এবং নির্দিষ্ট পরামর্শ নির্বাচনের জন্য ছেড়ে দিন। <br> <br> \n► ক্লিপবোর্ড ইতিহাসে কোনো অন্তর্ভুক্তিতে দীর্ঘ চাপ দিয়ে তা পিন করা যাবে (আনপিন না করা পর্যন্ত তা ক্লিপবোর্ডে থাকবে)<br> <br> \n► ক্লিপবোর্ড দর্শনে বাম পাশে অভিস্পর্শ করলে অন্তর্ভুক্তি অপসারণ হবে (যদি সেটি পিন করা না থাকে)। <br> <br> \n► সাম্প্রতিক ইমোজি বিভাগে কোনো ইমোজির ওপর দীর্ঘ চাপ দিলে সেটি অপসারণের জন্য একটি বিলোপ বোতাম দেখাবে। আবার সাম্প্রতিক ইমোজি বিভাগের ট্যাবে দীর্ঘ চাপ দিলে সব সাম্প্রতিক ইমোজি মুছে যাবে। <br> <br> \n► লেখা সিলেক্ট করে শিফট চাপলে তা বড়ো হাতের অক্ষর, ছোটো হাতের অক্ষর এবং সব অক্ষর বড়ো হাতের হবে। <br> <br> \n► আপনি ফাইল এক্সপ্লোরারে খোলার মাধ্যমে অভিধান সংযুক্ত করতে পারেন: <br> \n\t• এটি কেবল <i>content-uris</i> এর সাথে কাজ করে, <i>file-uris</i> এর সাথে নয়। অর্থাৎ কিছু ফাইল এক্সপ্লোরারের সাথে নাও কাজ করতে পারে। <br> <br> \n► রুট উপলব্ধতাসহ ম্যানুয়াল ব্যাকআপ করা ব্যবহারকারীদের জন্য: <br> \nঅ্যান্ড্রয়েড ৭ থেকে শেয়ারড্ প্রেফারেন্স ফাইল ডিফল্ট জায়গা নয়। কারণ অ্যাপ %s ব্যবহার করছে। ডিভাইস আনলকের আগে সেটিংস খোলার জন্য এটি প্রয়োজনীয়, যেমন: বুট করার সময়। <br> \nফাইলটি /data/user_de/0/package_id/shared_prefs/ থাকে। যদিও এটা ডিভাইস এবং অ্যান্ড্রয়েড সংস্করণের উপরে নির্ভর করে। <br> <br> \n<i><b>কেবল ডিবাগ এপিকে</b></i> <br> <br> \n► পরামর্শে দীর্ঘ চাপ দিলে তার উৎস-অভিধান দেখাবে। <br> <br> \n► ডিবাগ সেটিংস উচ্চতর সেটিংসে উপলব্ধ। যদিও লগ ফাইলে অভিধান অবমুক্ত করা ছাড়া এর ব্যবহারযোগ্যতা সীমিত। <br> \n\t• রিলিজ এপিকে এর জন্য <i>সম্পর্কে</i> এর সংস্করণে কয়েকবার চাপ দিতে হবে। তাহলে <i>উচ্চতর</i>-এ ডিবাগ সেটিংস উপলব্ধ হবে। <br> \n\t• <i>পরামর্শ তথ্য প্রদর্শন</i> চালু করলে পরামর্শের উপরে কিছু ছোটো নম্বর থাকবে যা অভ্যন্তরীণ হিসাব ও উৎস অভিধান নির্দেশ করে। <br> <br> \n► অ্যাপ্লিকেশন ক্র্যাশের পরে সেটিংস খুললে আপনি ক্র্যাশ লগ চান কি না তা জিজ্ঞেস করা হবে৷ <br> <br> \n► বহুভাষী টাইপিং ব্যবহারের সময় স্পেসবার বর্তমান ভাষা নির্ধারণের জন্য দৃঢ়তা মান দেখাবে। <br> <br> \n► পরামর্শের উপরে কিছু ছোটো নম্বর থাকবে যা অভ্যন্তরীণ হিসাব ও উৎস অভিধান নির্দেশ করে (নিষ্ক্রিয় করা যেতে পারে)।
সার্বিয়ান (ল্যাটিন)
%s (প্রথাগত)
%s (অক্ষর)
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 06f57fad04..1bb4df0308 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -845,6 +845,7 @@ New dictionary:
\n► Swipe up from a suggestion to open more suggestions, and release on the suggestion to select it. <br> <br>
\n► Long-press an entry in the clipboard history to pin it (keep it in clipboard until you unpin). <br> <br>
\n► Swipe left in clipboard view to remove an entry (except when it\'s pinned) <br> <br>
+\n► Long-press an emoji in the recent category to show a delete button that removes it, or long-press the recent category tab to clear all recent emojis. <br> <br>
\n► Select text and press shift to switch between uppercase, lowercase and capitalize words. <br> <br>
\n► You can add dictionaries by opening them in a file explorer: <br>
\n\t• This only works with <i>content-uris</i> and not with <i>file-uris</i>, meaning that it may not work with some file explorers. <br> <br>