diff --git a/CHANGELOG.md b/CHANGELOG.md index c98f44ed..8fd8308f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added +- Option to play sound on keypress ([#79]) - Optional key to quickly switch keyboard language ([#62]) - Added apostrophe as a pop-up character on the dot key ([#356]) @@ -123,6 +124,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [#47]: https://github.com/FossifyOrg/Keyboard/issues/47 [#62]: https://github.com/FossifyOrg/Keyboard/issues/62 [#78]: https://github.com/FossifyOrg/Keyboard/issues/78 +[#79]: https://github.com/FossifyOrg/Keyboard/issues/79 [#100]: https://github.com/FossifyOrg/Keyboard/issues/100 [#117]: https://github.com/FossifyOrg/Keyboard/issues/117 [#129]: https://github.com/FossifyOrg/Keyboard/issues/129 diff --git a/app/src/main/kotlin/org/fossify/keyboard/activities/SettingsActivity.kt b/app/src/main/kotlin/org/fossify/keyboard/activities/SettingsActivity.kt index 5133024c..ead937ca 100644 --- a/app/src/main/kotlin/org/fossify/keyboard/activities/SettingsActivity.kt +++ b/app/src/main/kotlin/org/fossify/keyboard/activities/SettingsActivity.kt @@ -3,10 +3,8 @@ package org.fossify.keyboard.activities import android.content.Intent import android.os.Bundle import org.fossify.commons.dialogs.RadioGroupDialog -import org.fossify.commons.extensions.beGoneIf import org.fossify.commons.extensions.beVisibleIf import org.fossify.commons.extensions.getProperPrimaryColor -import org.fossify.commons.extensions.isOrWasThankYouInstalled import org.fossify.commons.extensions.toast import org.fossify.commons.extensions.updateTextColors import org.fossify.commons.extensions.viewBinding @@ -29,6 +27,9 @@ import org.fossify.keyboard.helpers.KEYBOARD_HEIGHT_160_PERCENT import org.fossify.keyboard.helpers.KEYBOARD_HEIGHT_70_PERCENT import org.fossify.keyboard.helpers.KEYBOARD_HEIGHT_80_PERCENT import org.fossify.keyboard.helpers.KEYBOARD_HEIGHT_90_PERCENT +import org.fossify.keyboard.helpers.SOUND_ALWAYS +import org.fossify.keyboard.helpers.SOUND_NONE +import org.fossify.keyboard.helpers.SOUND_SYSTEM import java.util.Locale import kotlin.system.exitProcess @@ -54,6 +55,7 @@ class SettingsActivity : SimpleActivity() { setupLanguage() setupManageClipboardItems() setupVibrateOnKeypress() + setupSoundOnKeypress() setupShowPopupOnKeypress() setupShowKeyBorders() setupManageKeyboardLanguages() @@ -128,6 +130,35 @@ class SettingsActivity : SimpleActivity() { } } + private fun setupSoundOnKeypress() { + binding.apply { + settingsSoundOnKeypress.text = getSoundOnKeypressText(config.soundOnKeypress) + settingsSoundOnKeypressHolder.setOnClickListener { + val items = arrayListOf( + RadioItem(SOUND_NONE, getString(R.string.sound_none)), + RadioItem(SOUND_SYSTEM, getString(R.string.sound_system)), + RadioItem(SOUND_ALWAYS, getString(R.string.sound_always)) + ) + RadioGroupDialog( + activity = this@SettingsActivity, + items = items, + checkedItemId = config.soundOnKeypress + ) { + config.soundOnKeypress = it as Int + settingsSoundOnKeypress.text = getSoundOnKeypressText(config.soundOnKeypress) + } + } + } + } + + private fun getSoundOnKeypressText(mode: Int): String = getString( + when (mode) { + SOUND_SYSTEM -> R.string.sound_system + SOUND_ALWAYS -> R.string.sound_always + else -> R.string.sound_none + } + ) + private fun setupShowPopupOnKeypress() { binding.apply { settingsShowPopupOnKeypress.isChecked = config.showPopupOnKeypress diff --git a/app/src/main/kotlin/org/fossify/keyboard/helpers/Config.kt b/app/src/main/kotlin/org/fossify/keyboard/helpers/Config.kt index 58b8f510..46003b5c 100644 --- a/app/src/main/kotlin/org/fossify/keyboard/helpers/Config.kt +++ b/app/src/main/kotlin/org/fossify/keyboard/helpers/Config.kt @@ -15,6 +15,10 @@ class Config(context: Context) : BaseConfig(context) { get() = prefs.getBoolean(VIBRATE_ON_KEYPRESS, true) set(vibrateOnKeypress) = prefs.edit().putBoolean(VIBRATE_ON_KEYPRESS, vibrateOnKeypress).apply() + var soundOnKeypress: Int + get() = prefs.getInt(SOUND_ON_KEYPRESS, SOUND_SYSTEM) + set(soundOnKeypress) = prefs.edit().putInt(SOUND_ON_KEYPRESS, soundOnKeypress).apply() + var showPopupOnKeypress: Boolean get() = prefs.getBoolean(SHOW_POPUP_ON_KEYPRESS, true) set(showPopupOnKeypress) = prefs.edit().putBoolean(SHOW_POPUP_ON_KEYPRESS, showPopupOnKeypress).apply() diff --git a/app/src/main/kotlin/org/fossify/keyboard/helpers/Constants.kt b/app/src/main/kotlin/org/fossify/keyboard/helpers/Constants.kt index b60629ec..c31276db 100644 --- a/app/src/main/kotlin/org/fossify/keyboard/helpers/Constants.kt +++ b/app/src/main/kotlin/org/fossify/keyboard/helpers/Constants.kt @@ -12,6 +12,12 @@ const val MAX_KEYS_PER_MINI_ROW = 9 // shared prefs const val VIBRATE_ON_KEYPRESS = "vibrate_on_keypress" + +const val SOUND_ON_KEYPRESS = "sound_on_keypress" +const val SOUND_NONE = 0 +const val SOUND_SYSTEM = 1 +const val SOUND_ALWAYS = 2 + const val SHOW_POPUP_ON_KEYPRESS = "show_popup_on_keypress" const val SHOW_KEY_BORDERS = "show_key_borders" const val SENTENCES_CAPITALIZATION = "sentences_capitalization" diff --git a/app/src/main/kotlin/org/fossify/keyboard/helpers/KeyboardFeedbackManager.kt b/app/src/main/kotlin/org/fossify/keyboard/helpers/KeyboardFeedbackManager.kt new file mode 100644 index 00000000..5655a1ff --- /dev/null +++ b/app/src/main/kotlin/org/fossify/keyboard/helpers/KeyboardFeedbackManager.kt @@ -0,0 +1,70 @@ +package org.fossify.keyboard.helpers + +import android.content.Context +import android.media.AudioManager +import android.view.HapticFeedbackConstants +import android.view.View +import org.fossify.commons.extensions.performHapticFeedback +import org.fossify.commons.helpers.isOreoMr1Plus +import org.fossify.keyboard.extensions.config +import org.fossify.keyboard.extensions.safeStorageContext + +/** + * Helper for keypress haptics and audio. + */ +class KeyboardFeedbackManager(private val context: Context) { + + private val config = context.safeStorageContext.config + private val audioManager by lazy { + context.getSystemService(Context.AUDIO_SERVICE) as AudioManager + } + + /** + * Perform haptic feedback for standard keypress. + */ + fun vibrateIfNeeded(view: View) { + if (config.vibrateOnKeypress) view.performHapticFeedback() + } + + /** + * Perform haptic feedback for cursor handle movement. + */ + fun performHapticHandleMove(view: View) { + if (!config.vibrateOnKeypress) return + if (isOreoMr1Plus()) { + @Suppress("DEPRECATION") + view.performHapticFeedback( + HapticFeedbackConstants.TEXT_HANDLE_MOVE, + HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING + ) + } + } + + /** + * Play keypress sound if enabled. + */ + fun playKeypressSoundIfNeeded(code: Int) { + val soundMode = config.soundOnKeypress + if (soundMode == SOUND_NONE) return + + val effect = when (code) { + MyKeyboard.KEYCODE_DELETE -> AudioManager.FX_KEYPRESS_DELETE + MyKeyboard.KEYCODE_ENTER -> AudioManager.FX_KEYPRESS_RETURN + MyKeyboard.KEYCODE_SPACE -> AudioManager.FX_KEYPRESS_SPACEBAR + else -> AudioManager.FX_KEYPRESS_STANDARD + } + + when (soundMode) { + SOUND_SYSTEM -> audioManager.playSoundEffect(effect) + SOUND_ALWAYS -> audioManager.playSoundEffect(effect, 1.0f) + } + } + + /** + * Perform both haptic and audio feedback for a keypress. + */ + fun performKeypressFeedback(view: View, keyCode: Int) { + vibrateIfNeeded(view) + playKeypressSoundIfNeeded(keyCode) + } +} diff --git a/app/src/main/kotlin/org/fossify/keyboard/services/SimpleKeyboardIME.kt b/app/src/main/kotlin/org/fossify/keyboard/services/SimpleKeyboardIME.kt index 02784ac6..45b1cbdb 100644 --- a/app/src/main/kotlin/org/fossify/keyboard/services/SimpleKeyboardIME.kt +++ b/app/src/main/kotlin/org/fossify/keyboard/services/SimpleKeyboardIME.kt @@ -12,16 +12,28 @@ import android.icu.util.ULocale import android.inputmethodservice.InputMethodService import android.os.Build import android.os.Bundle -import android.text.InputType.* +import android.text.InputType.TYPE_CLASS_DATETIME +import android.text.InputType.TYPE_CLASS_NUMBER +import android.text.InputType.TYPE_CLASS_PHONE +import android.text.InputType.TYPE_CLASS_TEXT +import android.text.InputType.TYPE_MASK_CLASS +import android.text.InputType.TYPE_MASK_VARIATION +import android.text.InputType.TYPE_NULL import android.text.TextUtils import android.util.Size import android.view.KeyEvent import android.view.View import android.view.ViewGroup -import android.view.inputmethod.* +import android.view.inputmethod.CursorAnchorInfo +import android.view.inputmethod.EditorInfo import android.view.inputmethod.EditorInfo.IME_ACTION_NONE import android.view.inputmethod.EditorInfo.IME_FLAG_NO_ENTER_ACTION import android.view.inputmethod.EditorInfo.IME_MASK_ACTION +import android.view.inputmethod.ExtractedTextRequest +import android.view.inputmethod.InlineSuggestionsRequest +import android.view.inputmethod.InlineSuggestionsResponse +import android.view.inputmethod.InputConnection +import android.view.inputmethod.InputMethodSubtype import android.widget.inline.InlinePresentationSpec import androidx.annotation.RequiresApi import androidx.autofill.inline.UiVersions @@ -35,8 +47,23 @@ import androidx.core.view.ViewCompat import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsCompat.Type import androidx.core.view.updatePadding -import org.fossify.commons.extensions.* -import org.fossify.commons.helpers.* +import org.fossify.commons.extensions.applyColorFilter +import org.fossify.commons.extensions.getProperBackgroundColor +import org.fossify.commons.extensions.getProperTextColor +import org.fossify.commons.extensions.getSharedPrefs +import org.fossify.commons.extensions.setSystemBarsAppearance +import org.fossify.commons.helpers.ACCENT_COLOR +import org.fossify.commons.helpers.BACKGROUND_COLOR +import org.fossify.commons.helpers.CUSTOM_ACCENT_COLOR +import org.fossify.commons.helpers.CUSTOM_BACKGROUND_COLOR +import org.fossify.commons.helpers.CUSTOM_PRIMARY_COLOR +import org.fossify.commons.helpers.CUSTOM_TEXT_COLOR +import org.fossify.commons.helpers.IS_GLOBAL_THEME_ENABLED +import org.fossify.commons.helpers.IS_SYSTEM_THEME_ENABLED +import org.fossify.commons.helpers.PRIMARY_COLOR +import org.fossify.commons.helpers.TEXT_COLOR +import org.fossify.commons.helpers.isNougatPlus +import org.fossify.commons.helpers.isPiePlus import org.fossify.keyboard.R import org.fossify.keyboard.activities.SettingsActivity import org.fossify.keyboard.databinding.KeyboardViewKeyboardBinding @@ -46,7 +73,56 @@ import org.fossify.keyboard.extensions.getKeyboardLanguageText import org.fossify.keyboard.extensions.getSelectedLanguagesSorted import org.fossify.keyboard.extensions.getStrokeColor import org.fossify.keyboard.extensions.safeStorageContext -import org.fossify.keyboard.helpers.* +import org.fossify.keyboard.helpers.HEIGHT_PERCENTAGE +import org.fossify.keyboard.helpers.KEYBOARD_LANGUAGE +import org.fossify.keyboard.helpers.LANGUAGE_ARABIC +import org.fossify.keyboard.helpers.LANGUAGE_BELARUSIAN_CYRL +import org.fossify.keyboard.helpers.LANGUAGE_BELARUSIAN_LATN +import org.fossify.keyboard.helpers.LANGUAGE_BENGALI +import org.fossify.keyboard.helpers.LANGUAGE_BULGARIAN +import org.fossify.keyboard.helpers.LANGUAGE_CENTRAL_KURDISH +import org.fossify.keyboard.helpers.LANGUAGE_CHUVASH +import org.fossify.keyboard.helpers.LANGUAGE_CZECH_QWERTY +import org.fossify.keyboard.helpers.LANGUAGE_CZECH_QWERTZ +import org.fossify.keyboard.helpers.LANGUAGE_DANISH +import org.fossify.keyboard.helpers.LANGUAGE_DUTCH +import org.fossify.keyboard.helpers.LANGUAGE_ENGLISH_ASSET +import org.fossify.keyboard.helpers.LANGUAGE_ENGLISH_COLEMAK +import org.fossify.keyboard.helpers.LANGUAGE_ENGLISH_COLEMAKDH +import org.fossify.keyboard.helpers.LANGUAGE_ENGLISH_DVORAK +import org.fossify.keyboard.helpers.LANGUAGE_ENGLISH_NIRO +import org.fossify.keyboard.helpers.LANGUAGE_ENGLISH_QWERTZ +import org.fossify.keyboard.helpers.LANGUAGE_ENGLISH_SOUL +import org.fossify.keyboard.helpers.LANGUAGE_ENGLISH_WORKMAN +import org.fossify.keyboard.helpers.LANGUAGE_ESPERANTO +import org.fossify.keyboard.helpers.LANGUAGE_FRENCH_AZERTY +import org.fossify.keyboard.helpers.LANGUAGE_FRENCH_BEPO +import org.fossify.keyboard.helpers.LANGUAGE_GERMAN +import org.fossify.keyboard.helpers.LANGUAGE_GERMAN_QWERTZ +import org.fossify.keyboard.helpers.LANGUAGE_GREEK +import org.fossify.keyboard.helpers.LANGUAGE_HEBREW +import org.fossify.keyboard.helpers.LANGUAGE_ITALIAN +import org.fossify.keyboard.helpers.LANGUAGE_KABYLE_AZERTY +import org.fossify.keyboard.helpers.LANGUAGE_LATVIAN +import org.fossify.keyboard.helpers.LANGUAGE_LITHUANIAN +import org.fossify.keyboard.helpers.LANGUAGE_NORWEGIAN +import org.fossify.keyboard.helpers.LANGUAGE_POLISH +import org.fossify.keyboard.helpers.LANGUAGE_PORTUGUESE +import org.fossify.keyboard.helpers.LANGUAGE_PORTUGUESE_HCESAR +import org.fossify.keyboard.helpers.LANGUAGE_ROMANIAN +import org.fossify.keyboard.helpers.LANGUAGE_RUSSIAN +import org.fossify.keyboard.helpers.LANGUAGE_SLOVENIAN +import org.fossify.keyboard.helpers.LANGUAGE_SPANISH +import org.fossify.keyboard.helpers.LANGUAGE_SWEDISH +import org.fossify.keyboard.helpers.LANGUAGE_TURKISH +import org.fossify.keyboard.helpers.LANGUAGE_TURKISH_Q +import org.fossify.keyboard.helpers.LANGUAGE_UKRAINIAN +import org.fossify.keyboard.helpers.MyKeyboard +import org.fossify.keyboard.helpers.SHOW_KEY_BORDERS +import org.fossify.keyboard.helpers.SHOW_NUMBERS_ROW +import org.fossify.keyboard.helpers.ShiftState +import org.fossify.keyboard.helpers.VOICE_INPUT_METHOD +import org.fossify.keyboard.helpers.cachedVNTelexData import org.fossify.keyboard.interfaces.OnKeyboardActionListener import org.fossify.keyboard.views.MyKeyboardView import java.io.ByteArrayOutputStream @@ -108,7 +184,7 @@ class SimpleKeyboardIME : InputMethodService(), OnKeyboardActionListener, Shared override fun onPress(primaryCode: Int) { if (primaryCode != 0) { - keyboardView?.vibrateIfNeeded() + keyboardView?.performKeypressFeedback(primaryCode) } } diff --git a/app/src/main/kotlin/org/fossify/keyboard/views/MyKeyboardView.kt b/app/src/main/kotlin/org/fossify/keyboard/views/MyKeyboardView.kt index 01ac4a48..cf79d6f6 100644 --- a/app/src/main/kotlin/org/fossify/keyboard/views/MyKeyboardView.kt +++ b/app/src/main/kotlin/org/fossify/keyboard/views/MyKeyboardView.kt @@ -26,7 +26,6 @@ import android.os.Message import android.util.AttributeSet import android.util.TypedValue import android.view.Gravity -import android.view.HapticFeedbackConstants import android.view.LayoutInflater import android.view.MotionEvent import android.view.View @@ -61,12 +60,10 @@ import org.fossify.commons.extensions.getProperPrimaryColor import org.fossify.commons.extensions.getProperTextColor import org.fossify.commons.extensions.isDynamicTheme import org.fossify.commons.extensions.lightenColor -import org.fossify.commons.extensions.performHapticFeedback import org.fossify.commons.extensions.removeUnderlines import org.fossify.commons.extensions.toast import org.fossify.commons.helpers.HIGHER_ALPHA import org.fossify.commons.helpers.ensureBackgroundThread -import org.fossify.commons.helpers.isOreoMr1Plus import org.fossify.commons.helpers.isPiePlus import org.fossify.keyboard.R import org.fossify.keyboard.activities.ManageClipboardItemsActivity @@ -90,6 +87,7 @@ import org.fossify.keyboard.extensions.safeStorageContext import org.fossify.keyboard.helpers.AccessHelper import org.fossify.keyboard.helpers.EMOJI_SPEC_FILE_PATH import org.fossify.keyboard.helpers.EmojiData +import org.fossify.keyboard.helpers.KeyboardFeedbackManager import org.fossify.keyboard.helpers.LANGUAGE_TURKISH_Q import org.fossify.keyboard.helpers.LANGUAGE_VIETNAMESE_TELEX import org.fossify.keyboard.helpers.LANGUAGE_VN_TELEX @@ -137,6 +135,7 @@ class MyKeyboardView @JvmOverloads constructor( private var keyboardViewBinding: KeyboardViewKeyboardBinding? = null private var accessHelper: AccessHelper? = null + private val feedbackManager by lazy { KeyboardFeedbackManager(context) } private var mKeyboard: MyKeyboard? = null private var mCurrentKeyIndex: Int = NOT_A_KEY @@ -546,20 +545,15 @@ class MyKeyboardView @JvmOverloads constructor( } fun vibrateIfNeeded() { - if (context.config.vibrateOnKeypress) { - performHapticFeedback() - } + feedbackManager.vibrateIfNeeded(this) + } + + fun performKeypressFeedback(keyCode: Int) { + feedbackManager.performKeypressFeedback(this, keyCode) } fun performHapticHandleMove() { - if (!context.config.vibrateOnKeypress) return - if (isOreoMr1Plus()) { - @Suppress("DEPRECATION") - performHapticFeedback( - HapticFeedbackConstants.TEXT_HANDLE_MOVE, - HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING - ) - } + feedbackManager.performHapticHandleMove(this) } /** diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml index 80e300b1..490c7c92 100644 --- a/app/src/main/res/layout/activity_settings.xml +++ b/app/src/main/res/layout/activity_settings.xml @@ -177,6 +177,28 @@ + + + + + + + + Show clipboard content if available Show a popup on keypress Vibrate on keypress + Sound on keypress + Off + Follow system + Always on Keyboard language Keyboard height Show key borders