Clipboard suggestions (#647)

This commit is contained in:
codokie 2024-07-06 00:14:54 +03:00 committed by GitHub
parent 21124a5a45
commit bdab98c2c9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 213 additions and 18 deletions

View file

@ -3,6 +3,7 @@
package helium314.keyboard.compat; package helium314.keyboard.compat;
import android.content.ClipData; import android.content.ClipData;
import android.content.ClipDescription;
import android.content.ClipboardManager; import android.content.ClipboardManager;
import android.os.Build; import android.os.Build;
@ -29,4 +30,11 @@ public class ClipboardManagerCompat {
} }
} }
public static Boolean getClipSensitivity(final ClipDescription cd) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
return cd != null && cd.getExtras() != null && cd.getExtras().getBoolean("android.content.extra.IS_SENSITIVE");
}
return null; // can't determine
}
} }

View file

@ -4,12 +4,24 @@ package helium314.keyboard.latin
import android.content.ClipboardManager import android.content.ClipboardManager
import android.content.Context import android.content.Context
import android.text.InputType
import android.text.TextUtils import android.text.TextUtils
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.EditorInfo
import androidx.core.view.isGone
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import helium314.keyboard.compat.ClipboardManagerCompat import helium314.keyboard.compat.ClipboardManagerCompat
import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode
import helium314.keyboard.latin.common.ColorType
import helium314.keyboard.latin.common.isValidNumber
import helium314.keyboard.latin.databinding.ClipboardSuggestionBinding
import helium314.keyboard.latin.settings.Settings import helium314.keyboard.latin.settings.Settings
import helium314.keyboard.latin.utils.DeviceProtectedUtils import helium314.keyboard.latin.utils.DeviceProtectedUtils
import helium314.keyboard.latin.utils.InputTypeUtils
import helium314.keyboard.latin.utils.ToolbarKey
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
class ClipboardHistoryManager( class ClipboardHistoryManager(
@ -18,6 +30,7 @@ class ClipboardHistoryManager(
private lateinit var clipboardManager: ClipboardManager private lateinit var clipboardManager: ClipboardManager
private var onHistoryChangeListener: OnHistoryChangeListener? = null private var onHistoryChangeListener: OnHistoryChangeListener? = null
private var clipboardSuggestionView: View? = null
fun onCreate() { fun onCreate() {
clipboardManager = latinIME.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager clipboardManager = latinIME.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
@ -36,6 +49,7 @@ class ClipboardHistoryManager(
// Make sure we read clipboard content only if history settings is set // Make sure we read clipboard content only if history settings is set
if (latinIME.mSettings.current?.mClipboardHistoryEnabled == true) { if (latinIME.mSettings.current?.mClipboardHistoryEnabled == true) {
fetchPrimaryClip() fetchPrimaryClip()
dontShowCurrentSuggestion = false
} }
} }
@ -90,6 +104,7 @@ class ClipboardHistoryManager(
if (onHistoryChangeListener != null) { if (onHistoryChangeListener != null) {
onHistoryChangeListener?.onClipboardHistoryEntriesRemoved(pos, count) onHistoryChangeListener?.onClipboardHistoryEntriesRemoved(pos, count)
} }
removeClipboardSuggestion()
} }
fun canRemove(index: Int) = historyEntries.getOrNull(index)?.isPinned != true fun canRemove(index: Int) = historyEntries.getOrNull(index)?.isPinned != true
@ -131,6 +146,11 @@ class ClipboardHistoryManager(
return clipData.getItemAt(0)?.coerceToText(latinIME) ?: "" return clipData.getItemAt(0)?.coerceToText(latinIME) ?: ""
} }
private fun isClipSensitive(inputType: Int): Boolean {
ClipboardManagerCompat.getClipSensitivity(clipboardManager.primaryClip?.description)?.let { return it }
return InputTypeUtils.isPasswordInputType(inputType)
}
// pinned clips are stored in default shared preferences, not in device protected preferences! // pinned clips are stored in default shared preferences, not in device protected preferences!
private fun loadPinnedClips() { private fun loadPinnedClips() {
val pinnedClipString = Settings.readPinnedClipString(latinIME) val pinnedClipString = Settings.readPinnedClipString(latinIME)
@ -156,8 +176,66 @@ class ClipboardHistoryManager(
fun onClipboardHistoryEntryMoved(from: Int, to: Int) fun onClipboardHistoryEntryMoved(from: Int, to: Int)
} }
fun getClipboardSuggestionView(editorInfo: EditorInfo?, parent: ViewGroup?): View? {
// maybe no need to create a new view
// but a cache has to consider a few possible changes, so better don't implement without need
clipboardSuggestionView = null
// get the content, or return null
if (!latinIME.mSettings.current.mSuggestClipboardContent) return null
if (dontShowCurrentSuggestion) return null
if (parent == null) return null
val clipData = clipboardManager.primaryClip ?: return null
if (clipData.itemCount == 0 || clipData.description?.hasMimeType("text/*") == false) return null
val clipItem = clipData.getItemAt(0) ?: return null
val timeStamp = ClipboardManagerCompat.getClipTimestamp(clipData) ?: System.currentTimeMillis()
if (System.currentTimeMillis() - timeStamp > RECENT_TIME_MILLIS) return null
val content = clipItem.coerceToText(latinIME)
if (TextUtils.isEmpty(content)) return null
val inputType = editorInfo?.inputType ?: InputType.TYPE_NULL
if (InputTypeUtils.isNumberInputType(inputType) && !content.isValidNumber()) return null
// create the view
val binding = ClipboardSuggestionBinding.inflate(LayoutInflater.from(latinIME), parent, false)
val textView = binding.clipboardSuggestionText
textView.text = if (isClipSensitive(inputType)) "*".repeat(content.length) else content
val clipIcon = latinIME.mKeyboardSwitcher.keyboard.mIconsSet.getIconDrawable(ToolbarKey.CLIPBOARD.name.lowercase())
textView.setCompoundDrawablesRelativeWithIntrinsicBounds(clipIcon, null, null, null)
textView.setOnClickListener {
dontShowCurrentSuggestion = true
latinIME.onTextInput(content.toString())
AudioAndHapticFeedbackManager.getInstance().performHapticAndAudioFeedback(KeyCode.NOT_SPECIFIED, it);
binding.root.isGone = true
}
val closeButton = binding.clipboardSuggestionClose
closeButton.setImageDrawable(latinIME.mKeyboardSwitcher.keyboard.mIconsSet.getIconDrawable(ToolbarKey.CLOSE_HISTORY.name.lowercase()))
closeButton.setOnClickListener { removeClipboardSuggestion() }
val colors = latinIME.mSettings.current.mColors
textView.setTextColor(colors.get(ColorType.KEY_TEXT))
clipIcon?.let { colors.setColor(it, ColorType.KEY_ICON) }
colors.setColor(closeButton, ColorType.REMOVE_SUGGESTION_ICON)
colors.setBackground(binding.root, ColorType.CLIPBOARD_SUGGESTION_BACKGROUND)
clipboardSuggestionView = binding.root
return clipboardSuggestionView
}
private fun removeClipboardSuggestion() {
dontShowCurrentSuggestion = true
val csv = clipboardSuggestionView ?: return
if (csv.parent != null && !csv.isGone) {
// clipboard view is shown ->
latinIME.setNeutralSuggestionStrip()
latinIME.mHandler.postResumeSuggestions(false)
}
csv.isGone = true
}
companion object { companion object {
// store pinned clips in companion object so they survive a keyboard switch (which destroys the current instance) // store pinned clips in companion object so they survive a keyboard switch (which destroys the current instance)
private val historyEntries: MutableList<ClipboardHistoryEntry> = ArrayList() private val historyEntries: MutableList<ClipboardHistoryEntry> = ArrayList()
private var dontShowCurrentSuggestion: Boolean = false
const val RECENT_TIME_MILLIS = 3 * 60 * 1000L // 3 minutes (for clipboard suggestions)
} }
} }

View file

@ -1029,12 +1029,11 @@ public class LatinIME extends InputMethodService implements
// Space state must be updated before calling updateShiftState // Space state must be updated before calling updateShiftState
switcher.requestUpdatingShiftState(getCurrentAutoCapsState(), getCurrentRecapitalizeState()); switcher.requestUpdatingShiftState(getCurrentAutoCapsState(), getCurrentRecapitalizeState());
} }
// This will set the punctuation suggestions if next word suggestion is off; // Set neutral suggestions and show the toolbar if the "Auto show toolbar" setting is enabled.
// otherwise it will clear the suggestion strip.
if (!mHandler.hasPendingResumeSuggestions()) { if (!mHandler.hasPendingResumeSuggestions()) {
mHandler.cancelUpdateSuggestionStrip(); mHandler.cancelUpdateSuggestionStrip();
setNeutralSuggestionStrip(); setNeutralSuggestionStrip();
if (hasSuggestionStripView() && currentSettingsValues.mAutoShowToolbar) { if (hasSuggestionStripView() && currentSettingsValues.mAutoShowToolbar && !tryShowClipboardSuggestion()) {
mSuggestionStripView.setToolbarVisibility(true); mSuggestionStripView.setToolbarVisibility(true);
} }
} }
@ -1330,7 +1329,7 @@ public class LatinIME extends InputMethodService implements
// Without this function the inline autofill suggestions will not be visible // Without this function the inline autofill suggestions will not be visible
mHandler.cancelResumeSuggestions(); mHandler.cancelResumeSuggestions();
mSuggestionStripView.setInlineSuggestionsView(inlineSuggestionView); mSuggestionStripView.setExternalSuggestionView(inlineSuggestionView);
return true; return true;
} }
@ -1652,13 +1651,33 @@ public class LatinIME extends InputMethodService implements
updateStateAfterInputTransaction(completeInputTransaction); updateStateAfterInputTransaction(completeInputTransaction);
} }
// This will show either an empty suggestion strip (if prediction is enabled) or /**
// punctuation suggestions (if it's disabled). * Checks if a recent clipboard suggestion is available. If available, it is set in suggestion strip.
// The toolbar will be shown automatically if the relevant setting is enabled * returns whether a clipboard suggestion has been set.
*/
public boolean tryShowClipboardSuggestion() {
final View clipboardView = mClipboardHistoryManager.getClipboardSuggestionView(getCurrentInputEditorInfo(), mSuggestionStripView);
if (clipboardView != null && hasSuggestionStripView()) {
mSuggestionStripView.setExternalSuggestionView(clipboardView);
return true;
}
return false;
}
// This will first try showing a clipboard suggestion. On success, the toolbar will be hidden
// if the "Auto hide toolbar" is enabled. Otherwise, an empty suggestion strip (if prediction
// is enabled) or punctuation suggestions (if it's disabled) will be set.
// Then, the toolbar will be shown automatically if the relevant setting is enabled
// and there is a selection of text or it's the start of a line. // and there is a selection of text or it's the start of a line.
@Override @Override
public void setNeutralSuggestionStrip() { public void setNeutralSuggestionStrip() {
final SettingsValues currentSettings = mSettings.getCurrent(); final SettingsValues currentSettings = mSettings.getCurrent();
if (tryShowClipboardSuggestion()) {
// clipboard suggestion has been set
if (hasSuggestionStripView() && currentSettings.mAutoHideToolbar)
mSuggestionStripView.setToolbarVisibility(false);
return;
}
final SuggestedWords neutralSuggestions = currentSettings.mBigramPredictionEnabled final SuggestedWords neutralSuggestions = currentSettings.mBigramPredictionEnabled
? SuggestedWords.getEmptyInstance() ? SuggestedWords.getEmptyInstance()
: currentSettings.mSpacingAndPunctuations.mSuggestPuncList; : currentSettings.mSpacingAndPunctuations.mSuggestPuncList;

View file

@ -286,6 +286,7 @@ class DynamicColors(context: Context, override val themeStyle: String, override
KEY_BACKGROUND -> keyBackground KEY_BACKGROUND -> keyBackground
ACTION_KEY_POPUP_KEYS_BACKGROUND -> if (themeStyle == STYLE_HOLO) adjustedBackground else accent ACTION_KEY_POPUP_KEYS_BACKGROUND -> if (themeStyle == STYLE_HOLO) adjustedBackground else accent
STRIP_BACKGROUND -> if (!hasKeyBorders && themeStyle == STYLE_MATERIAL) adjustedBackground else background STRIP_BACKGROUND -> if (!hasKeyBorders && themeStyle == STYLE_MATERIAL) adjustedBackground else background
CLIPBOARD_SUGGESTION_BACKGROUND -> doubleAdjustedBackground
NAVIGATION_BAR -> navBar NAVIGATION_BAR -> navBar
MORE_SUGGESTIONS_HINT, SUGGESTED_WORD, SUGGESTION_TYPED_WORD, SUGGESTION_VALID_WORD -> adjustedKeyText MORE_SUGGESTIONS_HINT, SUGGESTED_WORD, SUGGESTION_TYPED_WORD, SUGGESTION_VALID_WORD -> adjustedKeyText
ACTION_KEY_ICON, TOOL_BAR_EXPAND_KEY -> Color.WHITE ACTION_KEY_ICON, TOOL_BAR_EXPAND_KEY -> Color.WHITE
@ -467,7 +468,7 @@ class DefaultColors (
CLIPBOARD_PIN, SHIFT_KEY_ICON -> accent CLIPBOARD_PIN, SHIFT_KEY_ICON -> accent
AUTOFILL_BACKGROUND_CHIP -> if (themeStyle == STYLE_MATERIAL && !hasKeyBorders) background else adjustedBackground AUTOFILL_BACKGROUND_CHIP -> if (themeStyle == STYLE_MATERIAL && !hasKeyBorders) background else adjustedBackground
GESTURE_PREVIEW, POPUP_KEYS_BACKGROUND, MORE_SUGGESTIONS_BACKGROUND, KEY_PREVIEW -> adjustedBackground GESTURE_PREVIEW, POPUP_KEYS_BACKGROUND, MORE_SUGGESTIONS_BACKGROUND, KEY_PREVIEW -> adjustedBackground
TOOL_BAR_EXPAND_KEY_BACKGROUND -> doubleAdjustedBackground TOOL_BAR_EXPAND_KEY_BACKGROUND, CLIPBOARD_SUGGESTION_BACKGROUND -> doubleAdjustedBackground
GESTURE_TRAIL -> gesture GESTURE_TRAIL -> gesture
KEY_TEXT, REMOVE_SUGGESTION_ICON, FUNCTIONAL_KEY_TEXT, KEY_ICON -> keyText KEY_TEXT, REMOVE_SUGGESTION_ICON, FUNCTIONAL_KEY_TEXT, KEY_ICON -> keyText
KEY_HINT_TEXT -> keyHintText KEY_HINT_TEXT -> keyHintText
@ -519,7 +520,7 @@ class DefaultColors (
view.setBackgroundColor(Color.WHITE) // set white to make the color filters work view.setBackgroundColor(Color.WHITE) // set white to make the color filters work
when (color) { when (color) {
KEY_PREVIEW, POPUP_KEYS_BACKGROUND -> view.background.colorFilter = adjustedBackgroundFilter KEY_PREVIEW, POPUP_KEYS_BACKGROUND -> view.background.colorFilter = adjustedBackgroundFilter
FUNCTIONAL_KEY_BACKGROUND, KEY_BACKGROUND, MORE_SUGGESTIONS_WORD_BACKGROUND, SPACE_BAR_BACKGROUND, STRIP_BACKGROUND -> setColor(view.background, color) FUNCTIONAL_KEY_BACKGROUND, KEY_BACKGROUND, MORE_SUGGESTIONS_WORD_BACKGROUND, SPACE_BAR_BACKGROUND, STRIP_BACKGROUND, CLIPBOARD_SUGGESTION_BACKGROUND -> setColor(view.background, color)
ONE_HANDED_MODE_BUTTON -> setColor(view.background, if (keyboardBackground == null) MAIN_BACKGROUND else STRIP_BACKGROUND) ONE_HANDED_MODE_BUTTON -> setColor(view.background, if (keyboardBackground == null) MAIN_BACKGROUND else STRIP_BACKGROUND)
MORE_SUGGESTIONS_BACKGROUND -> view.background.colorFilter = backgroundFilter MORE_SUGGESTIONS_BACKGROUND -> view.background.colorFilter = backgroundFilter
MAIN_BACKGROUND -> { MAIN_BACKGROUND -> {
@ -658,6 +659,7 @@ enum class ColorType {
ONE_HANDED_MODE_BUTTON, ONE_HANDED_MODE_BUTTON,
REMOVE_SUGGESTION_ICON, REMOVE_SUGGESTION_ICON,
STRIP_BACKGROUND, STRIP_BACKGROUND,
CLIPBOARD_SUGGESTION_BACKGROUND,
SUGGESTED_WORD, SUGGESTED_WORD,
SUGGESTION_AUTO_CORRECT, SUGGESTION_AUTO_CORRECT,
SUGGESTION_TYPED_WORD, SUGGESTION_TYPED_WORD,

View file

@ -103,6 +103,10 @@ fun String.splitOnFirstSpacesOnly(): List<String> {
return out return out
} }
fun CharSequence.isValidNumber(): Boolean {
return this.toString().toDoubleOrNull() != null
}
fun String.decapitalize(locale: Locale): String { fun String.decapitalize(locale: Locale): String {
if (isEmpty() || !this[0].isUpperCase()) if (isEmpty() || !this[0].isUpperCase())
return this return this

View file

@ -1648,8 +1648,12 @@ public final class InputLogic {
final SuggestedWords suggestedWords = holder.get(null, final SuggestedWords suggestedWords = holder.get(null,
Constants.GET_SUGGESTED_WORDS_TIMEOUT); Constants.GET_SUGGESTED_WORDS_TIMEOUT);
if (suggestedWords != null) { if (suggestedWords != null) {
// Prefer clipboard suggestions (if available and setting is enabled) over beginning of sentence predictions.
if (!(suggestedWords.mInputStyle == SuggestedWords.INPUT_STYLE_BEGINNING_OF_SENTENCE_PREDICTION
&& mLatinIME.tryShowClipboardSuggestion())) {
mSuggestionStripViewAccessor.showSuggestionStrip(suggestedWords); mSuggestionStripViewAccessor.showSuggestionStrip(suggestedWords);
} }
}
if (DebugFlags.DEBUG_ENABLED) { if (DebugFlags.DEBUG_ENABLED) {
long runTimeMillis = System.currentTimeMillis() - startTimeMillis; long runTimeMillis = System.currentTimeMillis() - startTimeMillis;
Log.d(TAG, "performUpdateSuggestionStripSync() : " + runTimeMillis + " ms to finish"); Log.d(TAG, "performUpdateSuggestionStripSync() : " + runTimeMillis + " ms to finish");

View file

@ -107,6 +107,7 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
public static final String PREF_AUTOSPACE_AFTER_PUNCTUATION = "autospace_after_punctuation"; public static final String PREF_AUTOSPACE_AFTER_PUNCTUATION = "autospace_after_punctuation";
public static final String PREF_ALWAYS_INCOGNITO_MODE = "always_incognito_mode"; public static final String PREF_ALWAYS_INCOGNITO_MODE = "always_incognito_mode";
public static final String PREF_BIGRAM_PREDICTIONS = "next_word_prediction"; public static final String PREF_BIGRAM_PREDICTIONS = "next_word_prediction";
public static final String PREF_SUGGEST_CLIPBOARD_CONTENT = "suggest_clipboard_content";
public static final String PREF_GESTURE_INPUT = "gesture_input"; public static final String PREF_GESTURE_INPUT = "gesture_input";
public static final String PREF_VIBRATION_DURATION_SETTINGS = "vibration_duration_settings"; public static final String PREF_VIBRATION_DURATION_SETTINGS = "vibration_duration_settings";
public static final String PREF_KEYPRESS_SOUND_VOLUME = "keypress_sound_volume"; public static final String PREF_KEYPRESS_SOUND_VOLUME = "keypress_sound_volume";

View file

@ -125,6 +125,7 @@ public class SettingsValues {
public final int mScoreLimitForAutocorrect; public final int mScoreLimitForAutocorrect;
private final boolean mSuggestionsEnabledPerUserSettings; private final boolean mSuggestionsEnabledPerUserSettings;
private final boolean mOverrideShowingSuggestions; private final boolean mOverrideShowingSuggestions;
public final boolean mSuggestClipboardContent;
public final SettingsValuesForSuggestion mSettingsValuesForSuggestion; public final SettingsValuesForSuggestion mSettingsValuesForSuggestion;
public final boolean mIncognitoModeEnabled; public final boolean mIncognitoModeEnabled;
public final boolean mLongPressSymbolsForNumpad; public final boolean mLongPressSymbolsForNumpad;
@ -179,6 +180,7 @@ public class SettingsValues {
mScoreLimitForAutocorrect = (mAutoCorrectionThreshold < 0) ? 600000 // very aggressive mScoreLimitForAutocorrect = (mAutoCorrectionThreshold < 0) ? 600000 // very aggressive
: (mAutoCorrectionThreshold < 0.07 ? 800000 : 950000); // aggressive or modest : (mAutoCorrectionThreshold < 0.07 ? 800000 : 950000); // aggressive or modest
mBigramPredictionEnabled = readBigramPredictionEnabled(prefs, res); mBigramPredictionEnabled = readBigramPredictionEnabled(prefs, res);
mSuggestClipboardContent = readSuggestClipboardContent(prefs, res);
mDoubleSpacePeriodTimeout = res.getInteger(R.integer.config_double_space_period_timeout); mDoubleSpacePeriodTimeout = res.getInteger(R.integer.config_double_space_period_timeout);
mHasHardwareKeyboard = Settings.readHasHardwareKeyboard(res.getConfiguration()); mHasHardwareKeyboard = Settings.readHasHardwareKeyboard(res.getConfiguration());
final float displayWidthDp = TypedValueCompat.pxToDp(res.getDisplayMetrics().widthPixels, res.getDisplayMetrics()); final float displayWidthDp = TypedValueCompat.pxToDp(res.getDisplayMetrics().widthPixels, res.getDisplayMetrics());
@ -327,6 +329,12 @@ public class SettingsValues {
R.bool.config_default_next_word_prediction)); R.bool.config_default_next_word_prediction));
} }
private static boolean readSuggestClipboardContent (SharedPreferences prefs,
final Resources res) {
return prefs.getBoolean(Settings.PREF_SUGGEST_CLIPBOARD_CONTENT, res.getBoolean(
R.bool.config_default_suggest_clipboard_content));
}
private static float readAutoCorrectionThreshold(final Resources res, private static float readAutoCorrectionThreshold(final Resources res,
final SharedPreferences prefs) { final SharedPreferences prefs) {
final String currentAutoCorrectionSetting = Settings.readAutoCorrectConfidence(prefs, res); final String currentAutoCorrectionSetting = Settings.readAutoCorrectConfidence(prefs, res);

View file

@ -26,7 +26,6 @@ import android.util.AttributeSet;
import android.util.TypedValue; import android.util.TypedValue;
import android.view.GestureDetector; import android.view.GestureDetector;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener; import android.view.View.OnClickListener;
@ -59,7 +58,6 @@ import helium314.keyboard.latin.settings.Settings;
import helium314.keyboard.latin.settings.SettingsValues; import helium314.keyboard.latin.settings.SettingsValues;
import helium314.keyboard.latin.suggestions.PopupSuggestionsView.MoreSuggestionsListener; import helium314.keyboard.latin.suggestions.PopupSuggestionsView.MoreSuggestionsListener;
import helium314.keyboard.latin.utils.DeviceProtectedUtils; import helium314.keyboard.latin.utils.DeviceProtectedUtils;
import helium314.keyboard.latin.utils.DialogUtilsKt;
import helium314.keyboard.latin.utils.Log; import helium314.keyboard.latin.utils.Log;
import helium314.keyboard.latin.utils.ToolbarKey; import helium314.keyboard.latin.utils.ToolbarKey;
import helium314.keyboard.latin.utils.ToolbarUtilsKt; import helium314.keyboard.latin.utils.ToolbarUtilsKt;
@ -69,7 +67,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.widget.PopupMenu;
public final class SuggestionStripView extends RelativeLayout implements OnClickListener, public final class SuggestionStripView extends RelativeLayout implements OnClickListener,
OnLongClickListener { OnLongClickListener {
@ -110,7 +107,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
private final SuggestionStripLayoutHelper mLayoutHelper; private final SuggestionStripLayoutHelper mLayoutHelper;
private final StripVisibilityGroup mStripVisibilityGroup; private final StripVisibilityGroup mStripVisibilityGroup;
private boolean isInlineAutofillSuggestionsVisible = false; // Required to disable the more suggestions if inline autofill suggestions are visible private boolean isExternalSuggestionVisible = false; // Required to disable the more suggestions if other suggestions are visible
private static class StripVisibilityGroup { private static class StripVisibilityGroup {
private final View mSuggestionStripView; private final View mSuggestionStripView;
@ -258,7 +255,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
: km.isKeyguardLocked(); : km.isKeyguardLocked();
mToolbarExpandKey.setOnClickListener(hideToolbarKeys ? null : this); mToolbarExpandKey.setOnClickListener(hideToolbarKeys ? null : this);
mPinnedKeys.setVisibility(hideToolbarKeys ? GONE : mSuggestionsStrip.getVisibility()); mPinnedKeys.setVisibility(hideToolbarKeys ? GONE : mSuggestionsStrip.getVisibility());
isInlineAutofillSuggestionsVisible = false; isExternalSuggestionVisible = false;
} }
public void setRtl(final boolean isRtlLanguage) { public void setRtl(final boolean isRtlLanguage) {
@ -281,9 +278,9 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
getContext(), mSuggestedWords, mSuggestionsStrip, this); getContext(), mSuggestedWords, mSuggestionsStrip, this);
} }
public void setInlineSuggestionsView(final View view) { public void setExternalSuggestionView(final View view) {
clear(); clear();
isInlineAutofillSuggestionsVisible = true; isExternalSuggestionVisible = true;
mSuggestionsStrip.addView(view); mSuggestionsStrip.addView(view);
if (Settings.getInstance().getCurrent().mAutoHideToolbar) if (Settings.getInstance().getCurrent().mAutoHideToolbar)
setToolbarVisibility(false); setToolbarVisibility(false);
@ -548,7 +545,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
public boolean onInterceptTouchEvent(final MotionEvent me) { public boolean onInterceptTouchEvent(final MotionEvent me) {
// Disable More Suggestions if inline autofill suggestions is visible // Disable More Suggestions if inline autofill suggestions is visible
if(isInlineAutofillSuggestionsVisible) { if(isExternalSuggestionVisible) {
return false; return false;
} }

View file

@ -18,6 +18,8 @@ public final class InputTypeUtils implements InputType {
TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_PASSWORD; TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_PASSWORD;
private static final int TEXT_VISIBLE_PASSWORD_INPUT_TYPE = private static final int TEXT_VISIBLE_PASSWORD_INPUT_TYPE =
TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_VISIBLE_PASSWORD; TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_VISIBLE_PASSWORD;
private static final int TEXT_NUMBER_INPUT_TYPE =
TYPE_CLASS_NUMBER | TYPE_NUMBER_FLAG_DECIMAL;
private static final int[] SUPPRESSING_AUTO_SPACES_FIELD_VARIATION = { private static final int[] SUPPRESSING_AUTO_SPACES_FIELD_VARIATION = {
InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS, InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS,
InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS, InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS,
@ -46,6 +48,10 @@ public final class InputTypeUtils implements InputType {
return variation == TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS; return variation == TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS;
} }
public static boolean isNumberInputType(final int inputType) {
return (inputType & TEXT_NUMBER_INPUT_TYPE) != 0;
}
public static boolean isEmailVariation(final int variation) { public static boolean isEmailVariation(final int variation) {
return variation == TYPE_TEXT_VARIATION_EMAIL_ADDRESS return variation == TYPE_TEXT_VARIATION_EMAIL_ADDRESS
|| isWebEmailAddressVariation(variation); || isWebEmailAddressVariation(variation);

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2020 The Android Open Source Project
SPDX-License-Identifier: Apache-2.0
-->
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="#14000000">
<item
android:bottom="5dp"
android:left="5dp"
android:right="5dp"
android:shape="rectangle"
android:top="5dp">
<shape>
<corners android:radius="32dp"/>
<stroke android:color="#1F000000" android:width="1dp"/>
<solid android:color="#FFFFFFFF"/>
</shape>
</item>
</ripple>

View file

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:background="@drawable/clipboard_suggestion_background"
android:gravity="center"
android:paddingRight="12dp"
android:layout_width="wrap_content"
android:layout_height="match_parent">
<TextView
android:id="@+id/clipboard_suggestion_text"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
android:contentDescription="@string/spoken_clipboard_suggestion"
android:drawablePadding="3dp"
android:paddingHorizontal="12dp"
android:hapticFeedbackEnabled="false"
android:soundEffectsEnabled="false"
android:singleLine="true"
android:gravity="center"
android:ellipsize="end"
android:textStyle="bold"
style="?android:attr/textAppearanceSmall" />
<ImageView
android:id="@+id/clipboard_suggestion_close"
android:src="@drawable/ic_close"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center" />
</LinearLayout>

View file

@ -51,6 +51,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_weight="1" android:layout_weight="1"
android:gravity="center_horizontal"
android:hapticFeedbackEnabled="false" android:hapticFeedbackEnabled="false"
android:soundEffectsEnabled="false" /> android:soundEffectsEnabled="false" />
<LinearLayout <LinearLayout

View file

@ -96,6 +96,9 @@
<dimen name="config_more_suggestions_modal_tolerance">32.0dp</dimen> <dimen name="config_more_suggestions_modal_tolerance">32.0dp</dimen>
<fraction name="config_more_suggestions_info_ratio">18%</fraction> <fraction name="config_more_suggestions_info_ratio">18%</fraction>
<!-- Common clipboard suggestion configuration. -->
<bool name="config_default_suggest_clipboard_content">true</bool>
<!-- Common gesture trail parameters --> <!-- Common gesture trail parameters -->
<!-- Minimum distance between gesture trail sampling points. --> <!-- Minimum distance between gesture trail sampling points. -->
<dimen name="config_gesture_trail_min_sampling_distance">9.6dp</dimen> <dimen name="config_gesture_trail_min_sampling_distance">9.6dp</dimen>

View file

@ -20,6 +20,8 @@
<!-- Spoken description of a suggestion when nothing is specified and the field is blank. --> <!-- Spoken description of a suggestion when nothing is specified and the field is blank. -->
<string name="spoken_empty_suggestion">No suggestion</string> <string name="spoken_empty_suggestion">No suggestion</string>
<!-- Spoken description of a suggestion when clipboard content appears as a suggestion. -->
<string name="spoken_clipboard_suggestion">Clipboard suggestion</string>
<!-- Spoken description for unknown keyboard keys. --> <!-- Spoken description for unknown keyboard keys. -->
<string name="spoken_description_unknown">Unknown character</string> <string name="spoken_description_unknown">Unknown character</string>

View file

@ -134,6 +134,10 @@
<string name="bigram_prediction">Next-word suggestions</string> <string name="bigram_prediction">Next-word suggestions</string>
<!-- Description for "next word suggestion" option. This displays suggestions even when there is no input, based on the previous word. --> <!-- Description for "next word suggestion" option. This displays suggestions even when there is no input, based on the previous word. -->
<string name="bigram_prediction_summary">Use the previous word in making suggestions</string> <string name="bigram_prediction_summary">Use the previous word in making suggestions</string>
<!-- Option to enable the suggestion of clipboard content. -->
<string name="suggest_clipboard_content">Suggest clipboard content</string>
<!-- Description for the "suggest clipboard content" option. This makes the primary clipboard content visible in the suggestion strip view. -->
<string name="suggest_clipboard_content_summary">Show recently copied clipboard content as a suggestion</string>
<!-- Option to enable gesture input. The user can input a word by tracing the letters of a word without releasing the finger from the screen. [CHAR LIMIT=30]--> <!-- Option to enable gesture input. The user can input a word by tracing the letters of a word without releasing the finger from the screen. [CHAR LIMIT=30]-->
<string name="gesture_input">Enable gesture typing</string> <string name="gesture_input">Enable gesture typing</string>
<!-- Description for "gesture_input" option. The user can input a word by tracing the letters of a word without releasing the finger from the screen. [CHAR LIMIT=65]--> <!-- Description for "gesture_input" option. The user can input a word by tracing the letters of a word without releasing the finger from the screen. [CHAR LIMIT=65]-->

View file

@ -108,6 +108,13 @@
android:defaultValue="@bool/config_center_suggestion_text_to_enter" android:defaultValue="@bool/config_center_suggestion_text_to_enter"
android:persistent="true" /> android:persistent="true" />
<SwitchPreference
android:key="suggest_clipboard_content"
android:title="@string/suggest_clipboard_content"
android:defaultValue="@bool/config_default_suggest_clipboard_content"
android:summary="@string/suggest_clipboard_content_summary"
android:persistent="true" />
<SwitchPreference <SwitchPreference
android:key="use_contacts" android:key="use_contacts"
android:title="@string/use_contacts_dict" android:title="@string/use_contacts_dict"