diff --git a/app/build.gradle b/app/build.gradle index 773ce60..3cd5340 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -12,7 +12,7 @@ android { minSdk = 30 targetSdk = 35 versionCode = 1 - versionName = "1.0" + versionName = "1.0.12" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } diff --git a/app/src/main/kotlin/dev/bsb/moakeyvim/OpenMoaIME.kt b/app/src/main/kotlin/dev/bsb/moakeyvim/OpenMoaIME.kt index e4d8417..71fed28 100644 --- a/app/src/main/kotlin/dev/bsb/moakeyvim/OpenMoaIME.kt +++ b/app/src/main/kotlin/dev/bsb/moakeyvim/OpenMoaIME.kt @@ -1135,8 +1135,9 @@ class OpenMoaIME : InputMethodService(), KoinComponent { return KoLayout(useQwerty = true, simpleQwerty = savedMode.isSimpleQwerty) } val isLandscape = resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE - if (isLandscape && SettingsPreferences.getLandscapeQwerty(this)) { - return KoLayout(useQwerty = true, simpleQwerty = false) + val landscapeLayout = SettingsPreferences.getLandscapeKoLayout(this) + if (isLandscape && landscapeLayout.isQwerty) { + return KoLayout(useQwerty = true, simpleQwerty = landscapeLayout.isSimple) } return KoLayout(useQwerty = false, simpleQwerty = false) } diff --git a/app/src/main/kotlin/dev/bsb/moakeyvim/config/LandscapeKoLayout.kt b/app/src/main/kotlin/dev/bsb/moakeyvim/config/LandscapeKoLayout.kt new file mode 100644 index 0000000..cc04a21 --- /dev/null +++ b/app/src/main/kotlin/dev/bsb/moakeyvim/config/LandscapeKoLayout.kt @@ -0,0 +1,17 @@ +package dev.bsb.moakeyvim.config + +import dev.bsb.moakeyvim.R + +enum class LandscapeKoLayout(val labelResId: Int) { + NONE(R.string.settings_landscape_ko_layout_none), + QWERTY(R.string.settings_input_mode_qwerty), + QWERTY_SIMPLE(R.string.settings_input_mode_qwerty_simple); + + val isQwerty: Boolean get() = this != NONE + val isSimple: Boolean get() = this == QWERTY_SIMPLE + + companion object { + fun fromString(value: String?): LandscapeKoLayout = + values().find { it.name == value } ?: NONE + } +} diff --git a/app/src/main/kotlin/dev/bsb/moakeyvim/settings/SettingsFragment.kt b/app/src/main/kotlin/dev/bsb/moakeyvim/settings/SettingsFragment.kt index 2505ea4..6850d37 100644 --- a/app/src/main/kotlin/dev/bsb/moakeyvim/settings/SettingsFragment.kt +++ b/app/src/main/kotlin/dev/bsb/moakeyvim/settings/SettingsFragment.kt @@ -28,6 +28,7 @@ import dev.bsb.moakeyvim.hotstring.HotstringRule import dev.bsb.moakeyvim.hotstring.HotstringRepository import dev.bsb.moakeyvim.config.EnterLongPressAction import dev.bsb.moakeyvim.config.HangulInputMode +import dev.bsb.moakeyvim.config.LandscapeKoLayout import dev.bsb.moakeyvim.config.HapticStrength import dev.bsb.moakeyvim.config.KeyboardSkin import dev.bsb.moakeyvim.config.KeypadHeight @@ -62,7 +63,6 @@ class SettingsFragment : PreferenceFragmentCompat() { SettingsPreferences.KEY_WORD_SUGGESTION_ENABLED, SettingsPreferences.KEY_KOREAN_WORD_SUGGESTION_ENABLED, SettingsPreferences.KEY_CLIPBOARD_ENABLED, - SettingsPreferences.KEY_LANDSCAPE_QWERTY, SettingsPreferences.KEY_FLOATING_INDICATOR_ENABLED, SettingsPreferences.KEY_OVERLAY_PERMISSION_NOTIFIED, SettingsPreferences.KEY_HW_CAPSLOCK_TO_CTRL, @@ -124,6 +124,8 @@ class SettingsFragment : PreferenceFragmentCompat() { setupClipboardListPreferences() pref(SettingsPreferences.KEY_HANGUL_INPUT_MODE) ?.setupEnum(HangulInputMode.values(), { it.labelResId }, HangulInputMode.TWO_HAND_MOAKEY) + pref(SettingsPreferences.KEY_LANDSCAPE_KO_LAYOUT) + ?.setupEnum(LandscapeKoLayout.values(), { it.labelResId }, LandscapeKoLayout.NONE) pref(SettingsPreferences.KEY_KEYBOARD_SKIN) ?.setupEnum(KeyboardSkin.values(), { it.labelResId }, KeyboardSkin.WHITE) pref(SettingsPreferences.KEY_KEYPAD_HEIGHT) diff --git a/app/src/main/kotlin/dev/bsb/moakeyvim/settings/SettingsPreferences.kt b/app/src/main/kotlin/dev/bsb/moakeyvim/settings/SettingsPreferences.kt index cd21812..9f9b148 100644 --- a/app/src/main/kotlin/dev/bsb/moakeyvim/settings/SettingsPreferences.kt +++ b/app/src/main/kotlin/dev/bsb/moakeyvim/settings/SettingsPreferences.kt @@ -5,6 +5,7 @@ import dev.bsb.moakeyvim.config.EnterLongPressAction import dev.bsb.moakeyvim.config.GestureAnglePreset import dev.bsb.moakeyvim.config.GestureAngles import dev.bsb.moakeyvim.config.HangulInputMode +import dev.bsb.moakeyvim.config.LandscapeKoLayout import dev.bsb.moakeyvim.config.HapticStrength import dev.bsb.moakeyvim.config.KeyboardSkin import dev.bsb.moakeyvim.config.SoundType @@ -45,6 +46,7 @@ object SettingsPreferences { const val KEY_CLIPBOARD_EXPIRY_MINUTES = "clipboard_expiry_minutes" const val KEY_MIN_LEARN_COUNT = "min_learn_count" const val KEY_LANDSCAPE_QWERTY = "landscape_qwerty" + const val KEY_LANDSCAPE_KO_LAYOUT = "landscape_ko_layout" const val KEY_FLOATING_INDICATOR_ENABLED = "floating_indicator_enabled" const val KEY_FLOATING_INDICATOR_X = "floating_indicator_x" const val KEY_FLOATING_INDICATOR_Y = "floating_indicator_y" @@ -79,6 +81,7 @@ object SettingsPreferences { KEY_CLIPBOARD_EXPIRY_MINUTES, KEY_MIN_LEARN_COUNT, KEY_LANDSCAPE_QWERTY, + KEY_LANDSCAPE_KO_LAYOUT, KEY_FLOATING_INDICATOR_ENABLED, KEY_FLOATING_INDICATOR_X, KEY_FLOATING_INDICATOR_Y, @@ -197,9 +200,21 @@ object SettingsPreferences { prefs(context).edit().putBoolean(KEY_CLIPBOARD_ENABLED, enabled).apply() } - fun getLandscapeQwerty(context: Context): Boolean = - prefs(context).getBoolean(KEY_LANDSCAPE_QWERTY, false) + fun getLandscapeKoLayout(context: Context): LandscapeKoLayout { + val p = prefs(context) + val saved = p.getString(KEY_LANDSCAPE_KO_LAYOUT, null) + if (saved != null) return LandscapeKoLayout.fromString(saved) + // 기존 boolean 설정 마이그레이션 + return if (p.getBoolean(KEY_LANDSCAPE_QWERTY, false)) { + LandscapeKoLayout.QWERTY + } else { + LandscapeKoLayout.NONE + } + } + + fun getLandscapeQwerty(context: Context): Boolean = getLandscapeKoLayout(context).isQwerty + @Deprecated("Use getLandscapeKoLayout() instead", ReplaceWith("getLandscapeKoLayout(context)")) fun setLandscapeQwerty(context: Context, enabled: Boolean) { prefs(context).edit().putBoolean(KEY_LANDSCAPE_QWERTY, enabled).apply() } diff --git a/app/src/main/kotlin/dev/bsb/moakeyvim/view/keyboardview/OpenMoaView.kt b/app/src/main/kotlin/dev/bsb/moakeyvim/view/keyboardview/OpenMoaView.kt index 3c5934f..14a1258 100644 --- a/app/src/main/kotlin/dev/bsb/moakeyvim/view/keyboardview/OpenMoaView.kt +++ b/app/src/main/kotlin/dev/bsb/moakeyvim/view/keyboardview/OpenMoaView.kt @@ -330,7 +330,7 @@ class OpenMoaView : ConstraintLayout, KoinComponent { CrossKeyTouchListener( context, listOf( - StringKeyMessage("ᆢ"), + StringKeyMessage("ㆍ"), StringKeyMessage("ㅡ"), StringKeyMessage("ㆍ"), StringKeyMessage("ㅣ"), diff --git a/app/src/main/kotlin/dev/bsb/moakeyvim/view/keytouchlistener/CrossKeyTouchListener.kt b/app/src/main/kotlin/dev/bsb/moakeyvim/view/keytouchlistener/CrossKeyTouchListener.kt index a56052f..c58eaa3 100644 --- a/app/src/main/kotlin/dev/bsb/moakeyvim/view/keytouchlistener/CrossKeyTouchListener.kt +++ b/app/src/main/kotlin/dev/bsb/moakeyvim/view/keytouchlistener/CrossKeyTouchListener.kt @@ -8,7 +8,27 @@ import dev.bsb.moakeyvim.view.message.StringKeyMessage import dev.bsb.moakeyvim.view.preview.KeyPreviewController import kotlin.math.* -// keyList 순서: [0]=위/왼, [1]=오른쪽, [2]=기본(탭), [3]=왼쪽/아래 +// keyList 순서: [0]=위, [1]=오른쪽, [2]=기본(탭/아래), [3]=왼쪽 +internal fun resolveKeyFromGesture( + startX: Float, + startY: Float, + currentX: Float, + currentY: Float, + gestureThreshold: Float, + keyList: List, +): StringKeyMessage { + require(keyList.size == 4) { "keyList must have exactly 4 entries: [up, right, center, left]" } + val distance = sqrt((currentX - startX).pow(2) + (currentY - startY).pow(2)) + if (distance <= gestureThreshold) return keyList[2] + val degree = ((atan2(currentY - startY, currentX - startX) * 180.0) / PI).toFloat() + // atan2 범위: (-180, 180]. abs >= 135도 = 왼쪽(keyList[3]) + return when { + abs(degree) < 45f -> keyList[1] + abs(degree) < 135f -> if (degree > 0) keyList[2] else keyList[0] + else -> keyList[3] + } +} + class CrossKeyTouchListener( context: Context, private val keyList: List, @@ -18,16 +38,8 @@ class CrossKeyTouchListener( private var startX: Float = 0f private var startY: Float = 0f - private fun resolveKey(currentX: Float, currentY: Float): StringKeyMessage { - val distance = sqrt((currentX - startX).pow(2) + (currentY - startY).pow(2)) - if (distance <= config.gestureThreshold) return keyList[2] - val degree = (atan2(currentY - startY, currentX - startX) * 180f) / PI - return when { - abs(degree) < 45f -> keyList[1] - abs(degree) < 135f -> if (degree > 0) keyList[2] else keyList[0] - else -> keyList[3] - } - } + private fun resolveKey(currentX: Float, currentY: Float): StringKeyMessage = + resolveKeyFromGesture(startX, startY, currentX, currentY, config.gestureThreshold, keyList) @SuppressLint("ClickableViewAccessibility") override fun onTouch(view: View, motionEvent: MotionEvent): Boolean { diff --git a/app/src/main/kotlin/dev/bsb/moakeyvim/view/suggestion/SuggestionBarView.kt b/app/src/main/kotlin/dev/bsb/moakeyvim/view/suggestion/SuggestionBarView.kt index bba46dc..d73f2b3 100644 --- a/app/src/main/kotlin/dev/bsb/moakeyvim/view/suggestion/SuggestionBarView.kt +++ b/app/src/main/kotlin/dev/bsb/moakeyvim/view/suggestion/SuggestionBarView.kt @@ -122,7 +122,11 @@ class SuggestionBarView @JvmOverloads constructor( val showFunctionKeys = words.isEmpty() leftActions.visibility = if (showFunctionKeys) VISIBLE else GONE rightActions.visibility = if (showFunctionKeys) VISIBLE else GONE - words.forEach { word -> container.addView(buildWordView(word, word in hotstringExpansions)) } + if (words.isNotEmpty()) { + val skin = SettingsPreferences.getKeyboardSkin(context) + val wordColor = dimTextColor(SkinApplier.fgColor(context, skin)) + words.forEach { word -> container.addView(buildWordView(word, word in hotstringExpansions, wordColor)) } + } } fun showClipboard(text: String, onPaste: (String) -> Unit) { @@ -171,12 +175,14 @@ class SuggestionBarView @JvmOverloads constructor( scrollView.scrollTo(0, 0) leftActions.visibility = VISIBLE rightActions.visibility = VISIBLE - container.addView(buildActionView(context.getString(R.string.key_cut), onCut)) - container.addView(buildActionView(context.getString(R.string.key_copy), onCopy)) + val skin = SettingsPreferences.getKeyboardSkin(context) + val actionColor = dimTextColor(SkinApplier.fgColor(context, skin)) + container.addView(buildActionView(context.getString(R.string.key_cut), onCut, actionColor)) + container.addView(buildActionView(context.getString(R.string.key_copy), onCopy, actionColor)) } fun applyColors(textColor: Int, bgColor: Int, keyBgColor: Int = Color.WHITE) { - currentTextColor = textColor + currentTextColor = dimTextColor(textColor) currentBgColor = bgColor currentKeyBgColor = keyBgColor setBackgroundColor(bgColor) @@ -193,17 +199,17 @@ class SuggestionBarView @JvmOverloads constructor( (child.getChildAt(1) as? TextView)?.setTextColor(textColor) child.background = buildChipBackground() } - child is TextView -> child.setTextColor(textColor) + child is TextView -> child.setTextColor(currentTextColor) child is ImageButton -> child.imageTintList = tintList } } } - private fun buildWordView(word: String, isHotstring: Boolean): TextView { + private fun buildWordView(word: String, isHotstring: Boolean, wordColor: Int): TextView { return TextView(context).apply { - text = word + text = word.trim() textSize = TEXT_SIZE_SP - setTextColor(currentTextColor) + setTextColor(wordColor) setPadding(hPad, vPad, hPad, vPad) gravity = Gravity.CENTER isClickable = true @@ -271,11 +277,11 @@ class SuggestionBarView @JvmOverloads constructor( return buildIconButton(R.drawable.ic_content_paste) { onOpenClipboardPanel?.invoke() } } - private fun buildActionView(label: String, action: () -> Unit): TextView { + private fun buildActionView(label: String, action: () -> Unit, actionColor: Int): TextView { return TextView(context).apply { text = label textSize = TEXT_SIZE_SP - setTextColor(currentTextColor) + setTextColor(actionColor) setPadding(hPad, vPad, hPad, vPad) gravity = Gravity.CENTER isClickable = true @@ -336,6 +342,13 @@ class SuggestionBarView @JvmOverloads constructor( } } + private fun dimTextColor(color: Int): Int { + val hsv = FloatArray(3) + Color.colorToHSV(color, hsv) + hsv[2] *= 0.88f + return Color.HSVToColor(hsv) + } + private fun buildRipple(): RippleDrawable { return RippleDrawable(ColorStateList.valueOf(0x22000000), null, ColorDrawable(Color.WHITE)) } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 16522eb..7aa36ba 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -113,7 +113,8 @@ 두벌식 두벌식 단모음 가로 모드 두벌식 - 가로 모드일 때 한글 자판을 쿼티로 표시합니다 + 가로 모드 입력 방식 + 세로와 동일(기본) 키보드 외형 키패드 높이 조절 diff --git a/app/src/main/res/xml/preferences_main.xml b/app/src/main/res/xml/preferences_main.xml index 96b556f..4a620d0 100644 --- a/app/src/main/res/xml/preferences_main.xml +++ b/app/src/main/res/xml/preferences_main.xml @@ -11,11 +11,11 @@ android:negativeButtonText="@string/dialog_close" app:icon="@drawable/ic_pref_keyboard" /> -