// SPDX-License-Identifier: GPL-3.0-only package helium314.keyboard.latin import android.annotation.SuppressLint import android.content.Context import android.content.res.Configuration import android.util.AttributeSet import android.view.Gravity import android.view.MotionEvent import android.view.View import android.widget.FrameLayout import android.widget.ImageButton import helium314.keyboard.keyboard.KeyboardActionListener import helium314.keyboard.keyboard.KeyboardSwitcher import helium314.keyboard.latin.common.ColorType import helium314.keyboard.latin.common.Constants import helium314.keyboard.latin.settings.Settings import helium314.keyboard.latin.utils.DeviceProtectedUtils import kotlin.math.abs class KeyboardWrapperView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyle: Int = 0 ) : FrameLayout(context, attrs, defStyle), View.OnClickListener { var keyboardActionListener: KeyboardActionListener? = null private lateinit var stopOneHandedModeBtn: ImageButton private lateinit var switchOneHandedModeBtn: ImageButton private lateinit var resizeOneHandedModeBtn: ImageButton private val iconStopOneHandedModeId: Int private val iconSwitchOneHandedModeId: Int private val iconResizeOneHandedModeId: Int var oneHandedModeEnabled = false set(enabled) { field = enabled updateViewsVisibility() requestLayout() } var oneHandedGravity = Gravity.NO_GRAVITY set(value) { field = value updateSwitchButtonSide() requestLayout() } @SuppressLint("ClickableViewAccessibility") override fun onFinishInflate() { super.onFinishInflate() stopOneHandedModeBtn = findViewById(R.id.btn_stop_one_handed_mode) stopOneHandedModeBtn.setImageResource(iconStopOneHandedModeId) stopOneHandedModeBtn.visibility = GONE switchOneHandedModeBtn = findViewById(R.id.btn_switch_one_handed_mode) switchOneHandedModeBtn.setImageResource(iconSwitchOneHandedModeId) switchOneHandedModeBtn.visibility = GONE resizeOneHandedModeBtn = findViewById(R.id.btn_resize_one_handed_mode) resizeOneHandedModeBtn.setImageResource(iconResizeOneHandedModeId) resizeOneHandedModeBtn.visibility = GONE stopOneHandedModeBtn.setOnClickListener(this) switchOneHandedModeBtn.setOnClickListener(this) var x = 0f resizeOneHandedModeBtn.setOnTouchListener { _, motionEvent -> when (motionEvent.action) { MotionEvent.ACTION_DOWN -> x = motionEvent.rawX MotionEvent.ACTION_MOVE -> { // performance is not great because settings are reloaded and keyboard is redrawn // on every move, but it's good enough val sign = -switchOneHandedModeBtn.scaleX // factor 2 to make it more sensitive (maybe could be tuned a little) val changePercent = 2 * sign * (x - motionEvent.rawX) / context.resources.displayMetrics.density if (abs(changePercent) < 1) return@setOnTouchListener true x = motionEvent.rawX val prefs = DeviceProtectedUtils.getSharedPreferences(context) val oldScale = Settings.readOneHandedModeScale(prefs, Settings.getInstance().current.mDisplayOrientation == Configuration.ORIENTATION_PORTRAIT) val newScale = (oldScale + changePercent / 100f).coerceAtMost(2.5f).coerceAtLeast(0.5f) if (newScale == oldScale) return@setOnTouchListener true Settings.getInstance().writeOneHandedModeScale(newScale) oneHandedModeEnabled = false // intentionally putting wrong value, so KeyboardSwitcher.setOneHandedModeEnabled does actually reload keyboardActionListener?.onCodeInput(Constants.CODE_START_ONE_HANDED_MODE, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE, false) } else -> x = 0f } true } val colors = Settings.getInstance().current.mColors colors.setColor(stopOneHandedModeBtn, ColorType.ONE_HANDED_MODE_BUTTON) colors.setColor(switchOneHandedModeBtn, ColorType.ONE_HANDED_MODE_BUTTON) colors.setColor(resizeOneHandedModeBtn, ColorType.ONE_HANDED_MODE_BUTTON) colors.setBackground(stopOneHandedModeBtn, ColorType.ONE_HANDED_MODE_BUTTON) colors.setBackground(switchOneHandedModeBtn, ColorType.ONE_HANDED_MODE_BUTTON) colors.setBackground(resizeOneHandedModeBtn, ColorType.ONE_HANDED_MODE_BUTTON) } @SuppressLint("RtlHardcoded") fun switchOneHandedModeSide() { oneHandedGravity = if (oneHandedGravity == Gravity.LEFT) Gravity.RIGHT else Gravity.LEFT } private fun updateViewsVisibility() { stopOneHandedModeBtn.visibility = if (oneHandedModeEnabled) VISIBLE else GONE switchOneHandedModeBtn.visibility = if (oneHandedModeEnabled) VISIBLE else GONE resizeOneHandedModeBtn.visibility = if (oneHandedModeEnabled) VISIBLE else GONE } @SuppressLint("RtlHardcoded") private fun updateSwitchButtonSide() { switchOneHandedModeBtn.scaleX = if (oneHandedGravity == Gravity.LEFT) -1f else 1f } override fun onClick(view: View) { if (view === stopOneHandedModeBtn) { keyboardActionListener?.onCodeInput(Constants.CODE_STOP_ONE_HANDED_MODE, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE, false /* isKeyRepeat */) } else if (view === switchOneHandedModeBtn) { keyboardActionListener?.onCodeInput(Constants.CODE_SWITCH_ONE_HANDED_MODE, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE, false /* isKeyRepeat */) } } @SuppressLint("RtlHardcoded") override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { if (!oneHandedModeEnabled) { super.onLayout(changed, left, top, right, bottom) return } val isLeftGravity = oneHandedGravity == Gravity.LEFT val width = right - left val keyboardView = KeyboardSwitcher.getInstance().visibleKeyboardView val spareWidth = width - keyboardView.measuredWidth val keyboardLeft = if (isLeftGravity) 0 else spareWidth keyboardView.layout( keyboardLeft, 0, keyboardLeft + keyboardView.measuredWidth, keyboardView.measuredHeight ) val scale = Settings.getInstance().current.mKeyboardHeightScale // scale one-handed mode button height if keyboard height scale is < 80% val heightScale = if (scale < 0.8f) scale + 0.2f else 1f val buttonsLeft = if (isLeftGravity) keyboardView.measuredWidth else 0 val buttonXLeft = buttonsLeft + (spareWidth - stopOneHandedModeBtn.measuredWidth) / 2 val buttonXRight = buttonsLeft + (spareWidth + stopOneHandedModeBtn.measuredWidth) / 2 val buttonHeight = (heightScale * stopOneHandedModeBtn.measuredHeight).toInt() fun View.setLayout(yPosition: Int) { layout(buttonXLeft, yPosition - buttonHeight / 2, buttonXRight, yPosition + buttonHeight / 2) } stopOneHandedModeBtn.setLayout((keyboardView.measuredHeight * 0.2f).toInt()) switchOneHandedModeBtn.setLayout((keyboardView.measuredHeight * 0.5f).toInt()) resizeOneHandedModeBtn.setLayout((keyboardView.measuredHeight * 0.8f).toInt()) } init { @SuppressLint("CustomViewStyleable") val keyboardAttr = context.obtainStyledAttributes(attrs, R.styleable.Keyboard, defStyle, R.style.Keyboard) iconStopOneHandedModeId = keyboardAttr.getResourceId(R.styleable.Keyboard_iconStopOneHandedMode, 0) iconSwitchOneHandedModeId = keyboardAttr.getResourceId(R.styleable.Keyboard_iconSwitchOneHandedMode, 0) iconResizeOneHandedModeId = keyboardAttr.getResourceId(R.styleable.Keyboard_iconResizeOneHandedMode, 0) keyboardAttr.recycle() } }